Skip to content

Commit

Permalink
Add Fyta integration (home-assistant#110816)
Browse files Browse the repository at this point in the history
* Initial commit for fyta integration

* Update __init__.py

Delete BinarySensor for first PR

* Update __init__.py

Rewind wrongful deletion of comma

* Delete homeassistant/components/fyta/binary_sensor.py

Delete binary_sensor for first pr of integration

* Update manifest.json

Updated requirement to new version of fyta_cli 0.2.1, where bug in import of modules has been resolved.

* Update requirements_test_all.txt

adjust to updated manifest

* Update requirements_all.txt

adjust to updated manifest

* Update test_config_flow.py

* Update config_flow.py

update file to correct error with _entry attribute

* Fyta integration - update initial PR based on review in initial PR home-assistant#110816 (#2)

* adjustments to pass test for config_flow

* backport of changes in intitial PR to dev

* update text_config_flow

* changes based on review in initial PR home-assistant#110816

* Update homeassistant/components/fyta/sensor.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/config_flow.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/config_flow.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/sensor.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/sensor.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/coordinator.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/config_flow.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/strings.json

Co-authored-by: Sid <[email protected]>

* Update homeassistant/components/fyta/strings.json

Co-authored-by: Sid <[email protected]>

* Update homeassistant/components/fyta/manifest.json

Co-authored-by: Sid <[email protected]>

* Adjustments based on PR-commet of Feb 19 (#3)

* add test for config_flow.validate_input

* update based on pr review

* update based on pr review

* further refinings based on PR review

* Update tests/components/fyta/test_config_flow.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update test_config_flow.py

Update tests based on PR comment

* Update homeassistant/components/fyta/sensor.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/sensor.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* add handling and test for duplicate entry

* Update homeassistant/components/fyta/coordinator.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/sensor.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/entity.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update test_config_flow.py

parametrize test for exceptions

* Update config_flow.py

Move _async_abort_entries_match, add arguments

* Update coordinator.py

* Update typing in coordinator.py

* Update coordinator.py

update typing

* Update coordinator.py

corrected typo

* Update coordinator.py

* Update entity.py

* Update sensor.py

* Update icons.json

* Update homeassistant/components/fyta/entity.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/entity.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update entity.py

* Update test_config_flow.py

* Update config_flow.py (change FlowResult to ConfigFlowResult)

* Update config_flow.py

* Update homeassistant/components/fyta/config_flow.py

Co-authored-by: Robert Resch <[email protected]>

* Update homeassistant/components/fyta/config_flow.py

Co-authored-by: Robert Resch <[email protected]>

* Update homeassistant/components/fyta/coordinator.py

Co-authored-by: Robert Resch <[email protected]>

* Update coordinator.py

* Update config_flow.py (typing FlowResult -> ConfigFlowResult)

* Update config_flow.py

* Aktualisieren von config_flow.py

* remove coordinator entities

* Update strings.json

remove plant_number

* Update icons.json

remove plant_number

* Update manifest.json

Update requirement to latest fyta_cli version

* Update requirements_all.txt

* Update requirements_test_all.txt

* Update homeassistant/components/fyta/sensor.py

* Update homeassistant/components/fyta/sensor.py

* Update homeassistant/components/fyta/coordinator.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/coordinator.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/coordinator.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/entity.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/strings.json

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/fyta/strings.json

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update tests/components/fyta/test_config_flow.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update tests/components/fyta/test_config_flow.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* move test-helpers into conftest.py, adjust import of coordinator.py

---------

Co-authored-by: Joost Lekkerkerker <[email protected]>
Co-authored-by: Sid <[email protected]>
Co-authored-by: Robert Resch <[email protected]>
  • Loading branch information
4 people authored Mar 15, 2024
1 parent cfc2f17 commit 98132d1
Show file tree
Hide file tree
Showing 18 changed files with 652 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ omit =
homeassistant/components/frontier_silicon/browse_media.py
homeassistant/components/frontier_silicon/media_player.py
homeassistant/components/futurenow/light.py
homeassistant/components/fyta/__init__.py
homeassistant/components/fyta/coordinator.py
homeassistant/components/fyta/entity.py
homeassistant/components/fyta/sensor.py
homeassistant/components/garadget/cover.py
homeassistant/components/garages_amsterdam/__init__.py
homeassistant/components/garages_amsterdam/binary_sensor.py
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,8 @@ build.json @home-assistant/supervisor
/tests/components/frontier_silicon/ @wlcrs
/homeassistant/components/fully_kiosk/ @cgarwood
/tests/components/fully_kiosk/ @cgarwood
/homeassistant/components/fyta/ @dontinelli
/tests/components/fyta/ @dontinelli
/homeassistant/components/garages_amsterdam/ @klaasnicolaas
/tests/components/garages_amsterdam/ @klaasnicolaas
/homeassistant/components/gardena_bluetooth/ @elupus
Expand Down
48 changes: 48 additions & 0 deletions homeassistant/components/fyta/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Initialization of FYTA integration."""
from __future__ import annotations

import logging

from fyta_cli.fyta_connector import FytaConnector

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant

from .const import DOMAIN
from .coordinator import FytaCoordinator

_LOGGER = logging.getLogger(__name__)

PLATFORMS = [
Platform.SENSOR,
]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the Fyta integration."""

username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]

fyta = FytaConnector(username, password)

coordinator = FytaCoordinator(hass, fyta)

await coordinator.async_config_entry_first_refresh()

hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload Fyta entity."""

unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
64 changes: 64 additions & 0 deletions homeassistant/components/fyta/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Config flow for FYTA integration."""
from __future__ import annotations

import logging
from typing import Any

from fyta_cli.fyta_connector import FytaConnector
from fyta_cli.fyta_exceptions import (
FytaAuthentificationError,
FytaConnectionError,
FytaPasswordError,
)
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)


DATA_SCHEMA = vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
)


class FytaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Fyta."""

VERSION = 1

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> config_entries.ConfigFlowResult:
"""Handle the initial step."""

errors = {}
if user_input:
self._async_abort_entries_match({CONF_USERNAME: user_input[CONF_USERNAME]})

fyta = FytaConnector(user_input[CONF_USERNAME], user_input[CONF_PASSWORD])

try:
await fyta.login()
except FytaConnectionError:
errors["base"] = "cannot_connect"
except FytaAuthentificationError:
errors["base"] = "invalid_auth"
except FytaPasswordError:
errors["base"] = "invalid_auth"
errors[CONF_PASSWORD] = "password_error"
except Exception: # pylint: disable=broad-except
errors["base"] = "unknown"
else:
return self.async_create_entry(
title=user_input[CONF_USERNAME], data=user_input
)
finally:
await fyta.client.close()

return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
)
2 changes: 2 additions & 0 deletions homeassistant/components/fyta/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"""Const for fyta integration."""
DOMAIN = "fyta"
55 changes: 55 additions & 0 deletions homeassistant/components/fyta/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Coordinator for FYTA integration."""

from datetime import datetime, timedelta
import logging
from typing import Any

from fyta_cli.fyta_connector import FytaConnector
from fyta_cli.fyta_exceptions import (
FytaAuthentificationError,
FytaConnectionError,
FytaPasswordError,
)

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

_LOGGER = logging.getLogger(__name__)


class FytaCoordinator(DataUpdateCoordinator[dict[int, dict[str, Any]]]):
"""Fyta custom coordinator."""

config_entry: ConfigEntry

def __init__(self, hass: HomeAssistant, fyta: FytaConnector) -> None:
"""Initialize my coordinator."""
super().__init__(
hass,
_LOGGER,
name="FYTA Coordinator",
update_interval=timedelta(seconds=60),
)
self.fyta = fyta

async def _async_update_data(
self,
) -> dict[int, dict[str, Any]]:
"""Fetch data from API endpoint."""

if self.fyta.expiration is None or self.fyta.expiration < datetime.now():
await self.renew_authentication()

return await self.fyta.update_all_plants()

async def renew_authentication(self) -> None:
"""Renew access token for FYTA API."""

try:
await self.fyta.login()
except FytaConnectionError as ex:
raise ConfigEntryNotReady from ex
except (FytaAuthentificationError, FytaPasswordError) as ex:
raise ConfigEntryError from ex
47 changes: 47 additions & 0 deletions homeassistant/components/fyta/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Entities for FYTA integration."""
from typing import Any

from homeassistant.components.sensor import SensorEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import FytaCoordinator


class FytaPlantEntity(CoordinatorEntity[FytaCoordinator]):
"""Base Fyta Plant entity."""

_attr_has_entity_name = True

def __init__(
self,
coordinator: FytaCoordinator,
entry: ConfigEntry,
description: SensorEntityDescription,
plant_id: int,
) -> None:
"""Initialize the Fyta sensor."""
super().__init__(coordinator)

self.plant_id = plant_id
self._attr_unique_id = f"{entry.entry_id}-{plant_id}-{description.key}"
self._attr_device_info = DeviceInfo(
manufacturer="Fyta",
model="Plant",
identifiers={(DOMAIN, f"{entry.entry_id}-{plant_id}")},
name=self.plant.get("name"),
sw_version=self.plant.get("sw_version"),
)
self.entity_description = description

@property
def plant(self) -> dict[str, Any]:
"""Get plant data."""
return self.coordinator.data[self.plant_id]

@property
def available(self) -> bool:
"""Test if entity is available."""
return super().available and self.plant_id in self.coordinator.data
27 changes: 27 additions & 0 deletions homeassistant/components/fyta/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"entity": {
"sensor": {
"status": {
"default": "mdi:flower"
},
"temperature_status": {
"default": "mdi:thermometer-lines"
},
"light_status": {
"default": "mdi:sun-clock-outline"
},
"moisture_status": {
"default": "mdi:water-percent-alert"
},
"salinity_status": {
"default": "mdi:sprout-outline"
},
"light": {
"default": "mdi:weather-sunny"
},
"salinity": {
"default": "mdi:sprout-outline"
}
}
}
}
10 changes: 10 additions & 0 deletions homeassistant/components/fyta/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"domain": "fyta",
"name": "FYTA",
"codeowners": ["@dontinelli"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/fyta",
"integration_type": "hub",
"iot_class": "cloud_polling",
"requirements": ["fyta_cli==0.3.3"]
}
Loading

0 comments on commit 98132d1

Please sign in to comment.