147 lines
4.8 KiB
Python
147 lines
4.8 KiB
Python
"""The Overkiz (by Somfy) integration."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from collections import defaultdict
|
|
from dataclasses import dataclass
|
|
|
|
from aiohttp import ClientError, ServerDisconnectedError
|
|
from pyoverkiz.client import OverkizClient
|
|
from pyoverkiz.const import SUPPORTED_SERVERS
|
|
from pyoverkiz.exceptions import (
|
|
BadCredentialsException,
|
|
MaintenanceException,
|
|
TooManyRequestsException,
|
|
)
|
|
from pyoverkiz.models import Device, Scenario
|
|
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
|
from homeassistant.helpers import device_registry as dr
|
|
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
|
|
|
from .const import (
|
|
CONF_HUB,
|
|
DOMAIN,
|
|
LOGGER,
|
|
OVERKIZ_DEVICE_TO_PLATFORM,
|
|
PLATFORMS,
|
|
UPDATE_INTERVAL,
|
|
UPDATE_INTERVAL_ALL_ASSUMED_STATE,
|
|
)
|
|
from .coordinator import OverkizDataUpdateCoordinator
|
|
|
|
|
|
@dataclass
|
|
class HomeAssistantOverkizData:
|
|
"""Overkiz data stored in the Home Assistant data object."""
|
|
|
|
coordinator: OverkizDataUpdateCoordinator
|
|
platforms: defaultdict[Platform, list[Device]]
|
|
scenarios: list[Scenario]
|
|
|
|
|
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
"""Set up Overkiz from a config entry."""
|
|
username = entry.data[CONF_USERNAME]
|
|
password = entry.data[CONF_PASSWORD]
|
|
server = SUPPORTED_SERVERS[entry.data[CONF_HUB]]
|
|
|
|
# To allow users with multiple accounts/hubs, we create a new session so they have separate cookies
|
|
session = async_create_clientsession(hass)
|
|
client = OverkizClient(
|
|
username=username, password=password, session=session, server=server
|
|
)
|
|
|
|
try:
|
|
await client.login()
|
|
|
|
setup, scenarios = await asyncio.gather(
|
|
*[
|
|
client.get_setup(),
|
|
client.get_scenarios(),
|
|
]
|
|
)
|
|
except BadCredentialsException as exception:
|
|
raise ConfigEntryAuthFailed("Invalid authentication") from exception
|
|
except TooManyRequestsException as exception:
|
|
raise ConfigEntryNotReady("Too many requests, try again later") from exception
|
|
except (TimeoutError, ClientError, ServerDisconnectedError) as exception:
|
|
raise ConfigEntryNotReady("Failed to connect") from exception
|
|
except MaintenanceException as exception:
|
|
raise ConfigEntryNotReady("Server is down for maintenance") from exception
|
|
|
|
coordinator = OverkizDataUpdateCoordinator(
|
|
hass,
|
|
LOGGER,
|
|
name="device events",
|
|
client=client,
|
|
devices=setup.devices,
|
|
places=setup.root_place,
|
|
update_interval=UPDATE_INTERVAL,
|
|
config_entry_id=entry.entry_id,
|
|
)
|
|
|
|
await coordinator.async_config_entry_first_refresh()
|
|
|
|
if coordinator.is_stateless:
|
|
LOGGER.debug(
|
|
(
|
|
"All devices have an assumed state. Update interval has been reduced"
|
|
" to: %s"
|
|
),
|
|
UPDATE_INTERVAL_ALL_ASSUMED_STATE,
|
|
)
|
|
coordinator.update_interval = UPDATE_INTERVAL_ALL_ASSUMED_STATE
|
|
|
|
platforms: defaultdict[Platform, list[Device]] = defaultdict(list)
|
|
|
|
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = HomeAssistantOverkizData(
|
|
coordinator=coordinator, platforms=platforms, scenarios=scenarios
|
|
)
|
|
|
|
# Map Overkiz entities to Home Assistant platform
|
|
for device in coordinator.data.values():
|
|
LOGGER.debug(
|
|
(
|
|
"The following device has been retrieved. Report an issue if not"
|
|
" supported correctly (%s)"
|
|
),
|
|
device,
|
|
)
|
|
|
|
if platform := OVERKIZ_DEVICE_TO_PLATFORM.get(
|
|
device.widget
|
|
) or OVERKIZ_DEVICE_TO_PLATFORM.get(device.ui_class):
|
|
platforms[platform].append(device)
|
|
|
|
device_registry = dr.async_get(hass)
|
|
|
|
for gateway in setup.gateways:
|
|
LOGGER.debug("Added gateway (%s)", gateway)
|
|
|
|
device_registry.async_get_or_create(
|
|
config_entry_id=entry.entry_id,
|
|
identifiers={(DOMAIN, gateway.id)},
|
|
model=gateway.sub_type.beautify_name if gateway.sub_type else None,
|
|
manufacturer=server.manufacturer,
|
|
name=gateway.type.beautify_name if gateway.type else gateway.id,
|
|
sw_version=gateway.connectivity.protocol_version,
|
|
configuration_url=server.configuration_url,
|
|
)
|
|
|
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
|
|
|
return True
|
|
|
|
|
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|
"""Unload a config entry."""
|
|
|
|
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
|
hass.data[DOMAIN].pop(entry.entry_id)
|
|
|
|
return unload_ok
|