* Initial import 0.0.2 * Fixes to URL, and removed commits * Initial import 0.0.2 * Fixes to URL, and removed commits * Added first test for iotty * First release * Reviewers request #1 - Removed clutter - Added support for new naming convention for IottySmartSwitch entity * Removed commmented code * Some modifications * Modified REST EP for iotty CloudApi * Initial import 0.0.2 * Fixes to URL, and removed commits * Added first test for iotty * First release * Rebased and resolved conflicts * Reviewers request #1 - Removed clutter - Added support for new naming convention for IottySmartSwitch entity * Removed commmented code * Some modifications * Modified REST EP for iotty CloudApi * Removed empty entries in manifest.json * Added test_config_flow * Fix as requested by @edenhaus * Added test_init * Removed comments, added one assert * Added TEST_CONFIG_FLOW * Added test for STORE_ENTITY * Increased code coverage * Full coverage for api.py * Added tests for switch component * Converted INFO logs onto DEBUG logs * Removed .gitignore from commits * Modifications to SWITCH.PY * Initial import 0.0.2 * Fixes to URL, and removed commits * Added first test for iotty * First release * Rebased and resolved conflicts * Fixed conflicts * Reviewers request #1 - Removed clutter - Added support for new naming convention for IottySmartSwitch entity * Removed commmented code * Some modifications * Modified REST EP for iotty CloudApi * Removed empty entries in manifest.json * Added test_config_flow * Some modifications * Fix as requested by @edenhaus * Added test_init * Removed comments, added one assert * Added TEST_CONFIG_FLOW * Added test for STORE_ENTITY * Increased code coverage * Full coverage for api.py * Added tests for switch component * Converted INFO logs onto DEBUG logs * Removed .gitignore from commits * Modifications to SWITCH.PY * Fixed tests for SWITCH * First working implementation of Coordinator * Increased code coverage * Full code coverage * Missing a line in testing * Update homeassistant/components/iotty/__init__.py Co-authored-by: Robert Resch <robert@resch.dev> * Update homeassistant/components/iotty/__init__.py Co-authored-by: Robert Resch <robert@resch.dev> * Modified coordinator as per request by edenhaus * use coordinator entities for switches * move platforms to constants * fix whitespace with ruff-format * correct iotty entry in application_credentials list * minor style improvements * refactor function name * handle new and deleted devices * improve code for adding devices after first initialization * use typed config entry instead of adding known devices to hass.data * improve iotty entity removal * test listeners update cycle * handle iotty as devices and not only as entities * fix test typing for mock config entry * test with fewer mocks for an integration test style opposed to the previous unit test style * remove useless tests and add more integration style tests * check if device_to_remove is None * integration style tests for turning switches on and off * remove redundant coordinator tests * check device status after issuing command in tests * remove unused fixtures * add strict typing for iotty * additional asserts and named snapshots in tests * fix mypy issues after enabling strict typing * upgrade iottycloud version to 0.1.3 * move coordinator to runtime_data * remove entity name * fix typing issues * coding style fixes * improve tests coding style and assertion targets * test edge cases when apis are not working * improve tests comments and assertions --------- Co-authored-by: Robert Resch <robert@resch.dev> Co-authored-by: Shapour Nemati <shapour.nemati@iotty.com> Co-authored-by: Erik Montnemery <erik@montnemery.com> Co-authored-by: shapournemati-iotty <130070037+shapournemati-iotty@users.noreply.github.com>
108 lines
3.5 KiB
Python
108 lines
3.5 KiB
Python
"""DataUpdateCoordinator for iotty."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import timedelta
|
|
import logging
|
|
|
|
from iottycloud.device import Device
|
|
from iottycloud.verbs import RESULT, STATUS
|
|
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import aiohttp_client, device_registry as dr
|
|
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
|
|
from homeassistant.helpers.entity import Entity
|
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
|
|
|
from . import api
|
|
from .const import DOMAIN
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
UPDATE_INTERVAL = timedelta(seconds=30)
|
|
|
|
|
|
@dataclass
|
|
class IottyData:
|
|
"""iotty data stored in the DataUpdateCoordinator."""
|
|
|
|
devices: list[Device]
|
|
|
|
|
|
class IottyDataUpdateCoordinator(DataUpdateCoordinator[IottyData]):
|
|
"""Class to manage fetching Iotty data."""
|
|
|
|
config_entry: ConfigEntry
|
|
_entities: dict[str, Entity]
|
|
_devices: list[Device]
|
|
_device_registry: dr.DeviceRegistry
|
|
|
|
def __init__(
|
|
self, hass: HomeAssistant, entry: ConfigEntry, session: OAuth2Session
|
|
) -> None:
|
|
"""Initialize the coordinator."""
|
|
_LOGGER.debug("Initializing iotty data update coordinator")
|
|
|
|
super().__init__(
|
|
hass,
|
|
_LOGGER,
|
|
name=f"{DOMAIN}_coordinator",
|
|
update_interval=UPDATE_INTERVAL,
|
|
)
|
|
|
|
self.config_entry = entry
|
|
self._entities = {}
|
|
self._devices = []
|
|
self.iotty = api.IottyProxy(
|
|
hass, aiohttp_client.async_get_clientsession(hass), session
|
|
)
|
|
self._device_registry = dr.async_get(hass)
|
|
|
|
async def async_config_entry_first_refresh(self) -> None:
|
|
"""Override the first refresh to also fetch iotty devices list."""
|
|
_LOGGER.debug("Fetching devices list from iottyCloud")
|
|
self._devices = await self.iotty.get_devices()
|
|
_LOGGER.debug("There are %d devices", len(self._devices))
|
|
|
|
await super().async_config_entry_first_refresh()
|
|
|
|
async def _async_update_data(self) -> IottyData:
|
|
"""Fetch data from iottyCloud device."""
|
|
_LOGGER.debug("Fetching devices status from iottyCloud")
|
|
|
|
current_devices = await self.iotty.get_devices()
|
|
|
|
removed_devices = [
|
|
d
|
|
for d in self._devices
|
|
if not any(x.device_id == d.device_id for x in current_devices)
|
|
]
|
|
|
|
for removed_device in removed_devices:
|
|
device_to_remove = self._device_registry.async_get_device(
|
|
{(DOMAIN, removed_device.device_id)}
|
|
)
|
|
if device_to_remove is not None:
|
|
self._device_registry.async_remove_device(device_to_remove.id)
|
|
|
|
self._devices = current_devices
|
|
|
|
for device in self._devices:
|
|
res = await self.iotty.get_status(device.device_id)
|
|
json = res.get(RESULT, {})
|
|
if (
|
|
not isinstance(res, dict)
|
|
or RESULT not in res
|
|
or not isinstance(json := res[RESULT], dict)
|
|
or not (status := json.get(STATUS))
|
|
):
|
|
_LOGGER.warning("Unable to read status for device %s", device.device_id)
|
|
else:
|
|
_LOGGER.debug(
|
|
"Retrieved status: '%s' for device %s", status, device.device_id
|
|
)
|
|
device.update_status(status)
|
|
|
|
return IottyData(self._devices)
|