hass-core/homeassistant/components/iotty/coordinator.py
Paolo Burgio c1c5cff993
Add integration for iotty Smart Home (#103073)
* 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>
2024-07-19 12:10:39 +02:00

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)