1
mirror of https://github.com/home-assistant/core synced 2024-08-31 05:57:13 +02:00
ha-core/homeassistant/components/gree/bridge.py
Clifford Roche 4ce6d00a22
Improve the discovery process for Gree (#45449)
* Add support for async device discovery

* FIx missing dispatcher cleanup breaking integration reload

* Update homeassistant/components/gree/climate.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/gree/switch.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Update homeassistant/components/gree/bridge.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Working on feedback

* Improving load/unload tests

* Update homeassistant/components/gree/__init__.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Working on more feedback

* Add tests covering async discovery scenarios

* Remove unnecessary shutdown

* Update homeassistant/components/gree/__init__.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Code refactor from reviews

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2021-04-13 11:54:03 +02:00

106 lines
3.5 KiB
Python

"""Helper and wrapper classes for Gree module."""
from __future__ import annotations
from datetime import timedelta
import logging
from greeclimate.device import Device, DeviceInfo
from greeclimate.discovery import Discovery, Listener
from greeclimate.exceptions import DeviceNotBoundError, DeviceTimeoutError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
COORDINATORS,
DISCOVERY_TIMEOUT,
DISPATCH_DEVICE_DISCOVERED,
DOMAIN,
MAX_ERRORS,
)
_LOGGER = logging.getLogger(__name__)
class DeviceDataUpdateCoordinator(DataUpdateCoordinator):
"""Manages polling for state changes from the device."""
def __init__(self, hass: HomeAssistant, device: Device):
"""Initialize the data update coordinator."""
DataUpdateCoordinator.__init__(
self,
hass,
_LOGGER,
name=f"{DOMAIN}-{device.device_info.name}",
update_interval=timedelta(seconds=60),
)
self.device = device
self._error_count = 0
async def _async_update_data(self):
"""Update the state of the device."""
try:
await self.device.update_state()
except DeviceNotBoundError as error:
raise UpdateFailed(f"Device {self.name} is unavailable") from error
except DeviceTimeoutError as error:
self._error_count += 1
# Under normal conditions GREE units timeout every once in a while
if self.last_update_success and self._error_count >= MAX_ERRORS:
_LOGGER.warning(
"Device is unavailable: %s (%s)",
self.name,
self.device.device_info,
)
raise UpdateFailed(f"Device {self.name} is unavailable") from error
async def push_state_update(self):
"""Send state updates to the physical device."""
try:
return await self.device.push_state_update()
except DeviceTimeoutError:
_LOGGER.warning(
"Timeout send state update to: %s (%s)",
self.name,
self.device.device_info,
)
class DiscoveryService(Listener):
"""Discovery event handler for gree devices."""
def __init__(self, hass) -> None:
"""Initialize discovery service."""
super().__init__()
self.hass = hass
self.discovery = Discovery(DISCOVERY_TIMEOUT)
self.discovery.add_listener(self)
hass.data[DOMAIN].setdefault(COORDINATORS, [])
async def device_found(self, device_info: DeviceInfo) -> None:
"""Handle new device found on the network."""
device = Device(device_info)
try:
await device.bind()
except DeviceNotBoundError:
_LOGGER.error("Unable to bind to gree device: %s", device_info)
except DeviceTimeoutError:
_LOGGER.error("Timeout trying to bind to gree device: %s", device_info)
_LOGGER.info(
"Adding Gree device %s at %s:%i",
device.device_info.name,
device.device_info.ip,
device.device_info.port,
)
coordo = DeviceDataUpdateCoordinator(self.hass, device)
self.hass.data[DOMAIN][COORDINATORS].append(coordo)
await coordo.async_refresh()
async_dispatcher_send(self.hass, DISPATCH_DEVICE_DISCOVERED, coordo)