1 """The lookin integration."""
3 from __future__
import annotations
6 from collections.abc
import Callable, Coroutine
11 from aiolookin
import (
14 LookinUDPSubscriptions,
20 from aiolookin.models
import UDPCommandType, UDPEvent
31 METEO_UPDATE_INTERVAL,
33 REMOTE_UPDATE_INTERVAL,
36 from .coordinator
import LookinDataUpdateCoordinator, LookinPushCoordinator
37 from .models
import LookinData
39 LOGGER = logging.getLogger(__name__)
41 UDP_MANAGER =
"udp_manager"
45 lookin_protocol: LookInHttpProtocol,
47 ) -> Callable[[], Coroutine[
None, Any, Remote]]:
48 """Create a function to capture the cell variable."""
50 async
def _async_update() -> Climate:
51 return await lookin_protocol.get_conditioner(uuid)
57 lookin_protocol: LookInHttpProtocol,
59 ) -> Callable[[], Coroutine[
None, Any, Remote]]:
60 """Create a function to capture the cell variable."""
62 async
def _async_update() -> Remote:
63 return await lookin_protocol.get_remote(uuid)
69 """Manage the lookin UDP subscriptions."""
72 """Init the manager."""
73 self.
_lock_lock = asyncio.Lock()
74 self.
_listener_listener: Callable |
None =
None
75 self.
_subscriptions_subscriptions: LookinUDPSubscriptions |
None =
None
78 """Get the shared LookinUDPSubscriptions."""
79 async
with self.
_lock_lock:
86 """Stop the listener."""
87 async
with self.
_lock_lock:
88 assert self.
_listener_listener
is not None
95 """Set up lookin from a config entry."""
96 domain_data = hass.data.setdefault(DOMAIN, {})
97 host = entry.data[CONF_HOST]
98 lookin_protocol = LookInHttpProtocol(
103 lookin_device = await lookin_protocol.get_info()
104 devices = await lookin_protocol.get_devices()
105 except (TimeoutError, aiohttp.ClientError, NoUsableService)
as ex:
106 raise ConfigEntryNotReady
from ex
108 if entry.unique_id != (found_uuid := lookin_device.id.upper()):
115 f
"Unexpected device found at {host}; expected {entry.unique_id}, "
116 f
"found {found_uuid}"
121 if lookin_device.model >= 2:
122 coordinator_class = LookinDataUpdateCoordinator[MeteoSensor]
123 meteo_coordinator = coordinator_class(
127 update_method=lookin_protocol.get_meteo_sensor,
128 update_interval=METEO_UPDATE_INTERVAL,
130 await meteo_coordinator.async_config_entry_first_refresh()
132 device_coordinators: dict[str, LookinDataUpdateCoordinator[Remote]] = {}
133 for remote
in devices:
134 if (platform := TYPE_TO_PLATFORM.get(remote[
"Type"]))
is None:
136 uuid = remote[
"UUID"]
137 if platform == Platform.CLIMATE:
144 name=f
"{entry.title} {uuid}",
145 update_method=updater,
146 update_interval=REMOTE_UPDATE_INTERVAL,
148 await coordinator.async_config_entry_first_refresh()
149 device_coordinators[uuid] = coordinator
152 def _async_meteo_push_update(event: UDPEvent) ->
None:
153 """Process an update pushed via UDP."""
154 LOGGER.debug(
"Processing push message for meteo sensor: %s", event)
155 meteo: MeteoSensor = meteo_coordinator.data
156 meteo.update_from_value(event.value)
157 meteo_coordinator.async_set_updated_data(meteo)
159 if UDP_MANAGER
not in domain_data:
162 manager = domain_data[UDP_MANAGER]
164 lookin_udp_subs = await manager.async_get_subscriptions()
166 if lookin_device.model >= 2:
167 entry.async_on_unload(
168 lookin_udp_subs.subscribe_event(
169 lookin_device.id, UDPCommandType.meteo,
None, _async_meteo_push_update
173 hass.data[DOMAIN][entry.entry_id] =
LookinData(
175 lookin_udp_subs=lookin_udp_subs,
176 lookin_device=lookin_device,
177 meteo_coordinator=meteo_coordinator
if lookin_device.model >= 2
else None,
179 lookin_protocol=lookin_protocol,
180 device_coordinators=device_coordinators,
183 await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
189 """Unload a config entry."""
190 if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
191 hass.data[DOMAIN].pop(entry.entry_id)
195 for entry
in hass.config_entries.async_entries(DOMAIN)
196 if entry.state == ConfigEntryState.LOADED
198 if len(loaded_entries) == 1:
199 manager: LookinUDPManager = hass.data[DOMAIN][UDP_MANAGER]
200 await manager.async_stop()
205 hass: HomeAssistant, entry: ConfigEntry, device_entry: dr.DeviceEntry
207 """Remove lookin config entry from a device."""
208 data: LookinData = hass.data[DOMAIN][entry.entry_id]
209 all_identifiers: set[tuple[str, str]] = {
210 (DOMAIN, data.lookin_device.id),
211 *((DOMAIN, remote[
"UUID"])
for remote
in data.devices),
215 for identifier
in device_entry.identifiers
216 if identifier
in all_identifiers
LookinUDPSubscriptions async_get_subscriptions(self)
Callable[[], Coroutine[None, Any, Remote]] _async_climate_updater(LookInHttpProtocol lookin_protocol, str uuid)
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Callable[[], Coroutine[None, Any, Remote]] _async_remote_updater(LookInHttpProtocol lookin_protocol, str uuid)
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
bool async_remove_config_entry_device(HomeAssistant hass, ConfigEntry entry, dr.DeviceEntry device_entry)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)