Update Somfy to reduce calls to /site entrypoint (#51572)
Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
parent
f5c3444072
commit
5c07fb51a2
12 changed files with 180 additions and 129 deletions
|
@ -1,5 +1,4 @@
|
||||||
"""Support for Somfy hubs."""
|
"""Support for Somfy hubs."""
|
||||||
from abc import abstractmethod
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -8,20 +7,16 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_OPTIMISTIC
|
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_OPTIMISTIC
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
config_entry_oauth2_flow,
|
config_entry_oauth2_flow,
|
||||||
config_validation as cv,
|
config_validation as cv,
|
||||||
device_registry as dr,
|
device_registry as dr,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
from homeassistant.helpers.update_coordinator import (
|
|
||||||
CoordinatorEntity,
|
|
||||||
DataUpdateCoordinator,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import api, config_flow
|
from . import api, config_flow
|
||||||
from .const import API, COORDINATOR, DOMAIN
|
from .const import COORDINATOR, DOMAIN
|
||||||
|
from .coordinator import SomfyDataUpdateCoordinator
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -84,25 +79,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
data = hass.data[DOMAIN]
|
data = hass.data[DOMAIN]
|
||||||
data[API] = api.ConfigEntrySomfyApi(hass, entry, implementation)
|
coordinator = SomfyDataUpdateCoordinator(
|
||||||
|
|
||||||
async def _update_all_devices():
|
|
||||||
"""Update all the devices."""
|
|
||||||
devices = await hass.async_add_executor_job(data[API].get_devices)
|
|
||||||
previous_devices = data[COORDINATOR].data
|
|
||||||
# Sometimes Somfy returns an empty list.
|
|
||||||
if not devices and previous_devices:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"No devices returned. Assuming the previous ones are still valid"
|
|
||||||
)
|
|
||||||
return previous_devices
|
|
||||||
return {dev.id: dev for dev in devices}
|
|
||||||
|
|
||||||
coordinator = DataUpdateCoordinator(
|
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
name="somfy device update",
|
name="somfy device update",
|
||||||
update_method=_update_all_devices,
|
client=api.ConfigEntrySomfyApi(hass, entry, implementation),
|
||||||
update_interval=SCAN_INTERVAL,
|
update_interval=SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
data[COORDINATOR] = coordinator
|
data[COORDINATOR] = coordinator
|
||||||
|
@ -140,70 +121,4 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
hass.data[DOMAIN].pop(API, None)
|
|
||||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
|
||||||
class SomfyEntity(CoordinatorEntity, Entity):
|
|
||||||
"""Representation of a generic Somfy device."""
|
|
||||||
|
|
||||||
def __init__(self, coordinator, device_id, somfy_api):
|
|
||||||
"""Initialize the Somfy device."""
|
|
||||||
super().__init__(coordinator)
|
|
||||||
self._id = device_id
|
|
||||||
self.api = somfy_api
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device(self):
|
|
||||||
"""Return data for the device id."""
|
|
||||||
return self.coordinator.data[self._id]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str:
|
|
||||||
"""Return the unique id base on the id returned by Somfy."""
|
|
||||||
return self._id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
"""Return the name of the device."""
|
|
||||||
return self.device.name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self):
|
|
||||||
"""Return device specific attributes.
|
|
||||||
|
|
||||||
Implemented by platform classes.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"identifiers": {(DOMAIN, self.unique_id)},
|
|
||||||
"name": self.name,
|
|
||||||
"model": self.device.type,
|
|
||||||
"via_device": (DOMAIN, self.device.parent_id),
|
|
||||||
# For the moment, Somfy only returns their own device.
|
|
||||||
"manufacturer": "Somfy",
|
|
||||||
}
|
|
||||||
|
|
||||||
def has_capability(self, capability: str) -> bool:
|
|
||||||
"""Test if device has a capability."""
|
|
||||||
capabilities = self.device.capabilities
|
|
||||||
return bool([c for c in capabilities if c.name == capability])
|
|
||||||
|
|
||||||
def has_state(self, state: str) -> bool:
|
|
||||||
"""Test if device has a state."""
|
|
||||||
states = self.device.states
|
|
||||||
return bool([c for c in states if c.name == state])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def assumed_state(self) -> bool:
|
|
||||||
"""Return if the device has an assumed state."""
|
|
||||||
return not bool(self.device.states)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _handle_coordinator_update(self):
|
|
||||||
"""Process an update from the coordinator."""
|
|
||||||
self._create_device()
|
|
||||||
super()._handle_coordinator_update()
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _create_device(self):
|
|
||||||
"""Update the device with the latest data."""
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ from homeassistant.components.climate.const import (
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||||
|
|
||||||
from . import SomfyEntity
|
from .const import COORDINATOR, DOMAIN
|
||||||
from .const import API, COORDINATOR, DOMAIN
|
from .entity import SomfyEntity
|
||||||
|
|
||||||
SUPPORTED_CATEGORIES = {Category.HVAC.value}
|
SUPPORTED_CATEGORIES = {Category.HVAC.value}
|
||||||
|
|
||||||
|
@ -49,10 +49,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the Somfy climate platform."""
|
"""Set up the Somfy climate platform."""
|
||||||
domain_data = hass.data[DOMAIN]
|
domain_data = hass.data[DOMAIN]
|
||||||
coordinator = domain_data[COORDINATOR]
|
coordinator = domain_data[COORDINATOR]
|
||||||
api = domain_data[API]
|
|
||||||
|
|
||||||
climates = [
|
climates = [
|
||||||
SomfyClimate(coordinator, device_id, api)
|
SomfyClimate(coordinator, device_id)
|
||||||
for device_id, device in coordinator.data.items()
|
for device_id, device in coordinator.data.items()
|
||||||
if SUPPORTED_CATEGORIES & set(device.categories)
|
if SUPPORTED_CATEGORIES & set(device.categories)
|
||||||
]
|
]
|
||||||
|
@ -63,15 +62,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
class SomfyClimate(SomfyEntity, ClimateEntity):
|
class SomfyClimate(SomfyEntity, ClimateEntity):
|
||||||
"""Representation of a Somfy thermostat device."""
|
"""Representation of a Somfy thermostat device."""
|
||||||
|
|
||||||
def __init__(self, coordinator, device_id, api):
|
def __init__(self, coordinator, device_id):
|
||||||
"""Initialize the Somfy device."""
|
"""Initialize the Somfy device."""
|
||||||
super().__init__(coordinator, device_id, api)
|
super().__init__(coordinator, device_id)
|
||||||
self._climate = None
|
self._climate = None
|
||||||
self._create_device()
|
self._create_device()
|
||||||
|
|
||||||
def _create_device(self):
|
def _create_device(self):
|
||||||
"""Update the device with the latest data."""
|
"""Update the device with the latest data."""
|
||||||
self._climate = Thermostat(self.device, self.api)
|
self._climate = Thermostat(self.device, self.coordinator.client)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
|
|
||||||
DOMAIN = "somfy"
|
DOMAIN = "somfy"
|
||||||
COORDINATOR = "coordinator"
|
COORDINATOR = "coordinator"
|
||||||
API = "api"
|
|
||||||
|
|
71
homeassistant/components/somfy/coordinator.py
Normal file
71
homeassistant/components/somfy/coordinator.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
"""Helpers to help coordinate updated."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from pymfy.api.error import QuotaViolationException, SetupNotFoundException
|
||||||
|
from pymfy.api.model import Device
|
||||||
|
from pymfy.api.somfy_api import SomfyApi
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
class SomfyDataUpdateCoordinator(DataUpdateCoordinator):
|
||||||
|
"""Class to manage fetching Somfy data."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
logger: logging.Logger,
|
||||||
|
*,
|
||||||
|
name: str,
|
||||||
|
client: SomfyApi,
|
||||||
|
update_interval: timedelta | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize global data updater."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
logger,
|
||||||
|
name=name,
|
||||||
|
update_interval=update_interval,
|
||||||
|
)
|
||||||
|
self.data = {}
|
||||||
|
self.client = client
|
||||||
|
self.site_device = {}
|
||||||
|
self.last_site_index = -1
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> dict[str, Device]:
|
||||||
|
"""Fetch Somfy data.
|
||||||
|
|
||||||
|
Somfy only allow one call per minute to /site. There is one exception: 2 calls are allowed after site retrieval.
|
||||||
|
"""
|
||||||
|
if not self.site_device:
|
||||||
|
sites = await self.hass.async_add_executor_job(self.client.get_sites)
|
||||||
|
if not sites:
|
||||||
|
return {}
|
||||||
|
self.site_device = {site.id: [] for site in sites}
|
||||||
|
|
||||||
|
site_id = self._site_id
|
||||||
|
try:
|
||||||
|
devices = await self.hass.async_add_executor_job(
|
||||||
|
self.client.get_devices, site_id
|
||||||
|
)
|
||||||
|
self.site_device[site_id] = devices
|
||||||
|
except SetupNotFoundException:
|
||||||
|
del self.site_device[site_id]
|
||||||
|
return await self._async_update_data()
|
||||||
|
except QuotaViolationException:
|
||||||
|
self.logger.warning("Quota violation")
|
||||||
|
|
||||||
|
return {dev.id: dev for devices in self.site_device.values() for dev in devices}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _site_id(self):
|
||||||
|
"""Return the next site id to retrieve.
|
||||||
|
|
||||||
|
This tweak is required as Somfy does not allow to call the /site entrypoint more than once per minute.
|
||||||
|
"""
|
||||||
|
self.last_site_index = (self.last_site_index + 1) % len(self.site_device)
|
||||||
|
return list(self.site_device.keys())[self.last_site_index]
|
|
@ -21,8 +21,8 @@ from homeassistant.components.cover import (
|
||||||
from homeassistant.const import CONF_OPTIMISTIC, STATE_CLOSED, STATE_OPEN
|
from homeassistant.const import CONF_OPTIMISTIC, STATE_CLOSED, STATE_OPEN
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
|
||||||
from . import SomfyEntity
|
from .const import COORDINATOR, DOMAIN
|
||||||
from .const import API, COORDINATOR, DOMAIN
|
from .entity import SomfyEntity
|
||||||
|
|
||||||
BLIND_DEVICE_CATEGORIES = {Category.INTERIOR_BLIND.value, Category.EXTERIOR_BLIND.value}
|
BLIND_DEVICE_CATEGORIES = {Category.INTERIOR_BLIND.value, Category.EXTERIOR_BLIND.value}
|
||||||
SHUTTER_DEVICE_CATEGORIES = {Category.EXTERIOR_BLIND.value}
|
SHUTTER_DEVICE_CATEGORIES = {Category.EXTERIOR_BLIND.value}
|
||||||
|
@ -37,10 +37,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the Somfy cover platform."""
|
"""Set up the Somfy cover platform."""
|
||||||
domain_data = hass.data[DOMAIN]
|
domain_data = hass.data[DOMAIN]
|
||||||
coordinator = domain_data[COORDINATOR]
|
coordinator = domain_data[COORDINATOR]
|
||||||
api = domain_data[API]
|
|
||||||
|
|
||||||
covers = [
|
covers = [
|
||||||
SomfyCover(coordinator, device_id, api, domain_data[CONF_OPTIMISTIC])
|
SomfyCover(coordinator, device_id, domain_data[CONF_OPTIMISTIC])
|
||||||
for device_id, device in coordinator.data.items()
|
for device_id, device in coordinator.data.items()
|
||||||
if SUPPORTED_CATEGORIES & set(device.categories)
|
if SUPPORTED_CATEGORIES & set(device.categories)
|
||||||
]
|
]
|
||||||
|
@ -51,9 +50,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
class SomfyCover(SomfyEntity, RestoreEntity, CoverEntity):
|
class SomfyCover(SomfyEntity, RestoreEntity, CoverEntity):
|
||||||
"""Representation of a Somfy cover device."""
|
"""Representation of a Somfy cover device."""
|
||||||
|
|
||||||
def __init__(self, coordinator, device_id, api, optimistic):
|
def __init__(self, coordinator, device_id, optimistic):
|
||||||
"""Initialize the Somfy device."""
|
"""Initialize the Somfy device."""
|
||||||
super().__init__(coordinator, device_id, api)
|
super().__init__(coordinator, device_id)
|
||||||
self.categories = set(self.device.categories)
|
self.categories = set(self.device.categories)
|
||||||
self.optimistic = optimistic
|
self.optimistic = optimistic
|
||||||
self._closed = None
|
self._closed = None
|
||||||
|
@ -64,7 +63,7 @@ class SomfyCover(SomfyEntity, RestoreEntity, CoverEntity):
|
||||||
|
|
||||||
def _create_device(self) -> Blind:
|
def _create_device(self) -> Blind:
|
||||||
"""Update the device with the latest data."""
|
"""Update the device with the latest data."""
|
||||||
self._cover = Blind(self.device, self.api)
|
self._cover = Blind(self.device, self.coordinator.client)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
|
|
73
homeassistant/components/somfy/entity.py
Normal file
73
homeassistant/components/somfy/entity.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
"""Entity representing a Somfy device."""
|
||||||
|
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
class SomfyEntity(CoordinatorEntity, Entity):
|
||||||
|
"""Representation of a generic Somfy device."""
|
||||||
|
|
||||||
|
def __init__(self, coordinator, device_id):
|
||||||
|
"""Initialize the Somfy device."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self._id = device_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device(self):
|
||||||
|
"""Return data for the device id."""
|
||||||
|
return self.coordinator.data[self._id]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self) -> str:
|
||||||
|
"""Return the unique id base on the id returned by Somfy."""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
"""Return the name of the device."""
|
||||||
|
return self.device.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
"""Return device specific attributes.
|
||||||
|
|
||||||
|
Implemented by platform classes.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"identifiers": {(DOMAIN, self.unique_id)},
|
||||||
|
"name": self.name,
|
||||||
|
"model": self.device.type,
|
||||||
|
"via_device": (DOMAIN, self.device.parent_id),
|
||||||
|
# For the moment, Somfy only returns their own device.
|
||||||
|
"manufacturer": "Somfy",
|
||||||
|
}
|
||||||
|
|
||||||
|
def has_capability(self, capability: str) -> bool:
|
||||||
|
"""Test if device has a capability."""
|
||||||
|
capabilities = self.device.capabilities
|
||||||
|
return bool([c for c in capabilities if c.name == capability])
|
||||||
|
|
||||||
|
def has_state(self, state: str) -> bool:
|
||||||
|
"""Test if device has a state."""
|
||||||
|
states = self.device.states
|
||||||
|
return bool([c for c in states if c.name == state])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def assumed_state(self) -> bool:
|
||||||
|
"""Return if the device has an assumed state."""
|
||||||
|
return not bool(self.device.states)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_coordinator_update(self):
|
||||||
|
"""Process an update from the coordinator."""
|
||||||
|
self._create_device()
|
||||||
|
super()._handle_coordinator_update()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _create_device(self):
|
||||||
|
"""Update the device with the latest data."""
|
|
@ -5,7 +5,7 @@
|
||||||
"documentation": "https://www.home-assistant.io/integrations/somfy",
|
"documentation": "https://www.home-assistant.io/integrations/somfy",
|
||||||
"dependencies": ["http"],
|
"dependencies": ["http"],
|
||||||
"codeowners": ["@tetienne"],
|
"codeowners": ["@tetienne"],
|
||||||
"requirements": ["pymfy==0.9.3"],
|
"requirements": ["pymfy==0.11.0"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_kizbox._tcp.local.",
|
"type": "_kizbox._tcp.local.",
|
||||||
|
|
|
@ -6,8 +6,8 @@ from pymfy.api.devices.thermostat import Thermostat
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
from homeassistant.const import DEVICE_CLASS_BATTERY, PERCENTAGE
|
from homeassistant.const import DEVICE_CLASS_BATTERY, PERCENTAGE
|
||||||
|
|
||||||
from . import SomfyEntity
|
from .const import COORDINATOR, DOMAIN
|
||||||
from .const import API, COORDINATOR, DOMAIN
|
from .entity import SomfyEntity
|
||||||
|
|
||||||
SUPPORTED_CATEGORIES = {Category.HVAC.value}
|
SUPPORTED_CATEGORIES = {Category.HVAC.value}
|
||||||
|
|
||||||
|
@ -16,10 +16,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the Somfy sensor platform."""
|
"""Set up the Somfy sensor platform."""
|
||||||
domain_data = hass.data[DOMAIN]
|
domain_data = hass.data[DOMAIN]
|
||||||
coordinator = domain_data[COORDINATOR]
|
coordinator = domain_data[COORDINATOR]
|
||||||
api = domain_data[API]
|
|
||||||
|
|
||||||
sensors = [
|
sensors = [
|
||||||
SomfyThermostatBatterySensor(coordinator, device_id, api)
|
SomfyThermostatBatterySensor(coordinator, device_id)
|
||||||
for device_id, device in coordinator.data.items()
|
for device_id, device in coordinator.data.items()
|
||||||
if SUPPORTED_CATEGORIES & set(device.categories)
|
if SUPPORTED_CATEGORIES & set(device.categories)
|
||||||
]
|
]
|
||||||
|
@ -33,15 +32,15 @@ class SomfyThermostatBatterySensor(SomfyEntity, SensorEntity):
|
||||||
_attr_device_class = DEVICE_CLASS_BATTERY
|
_attr_device_class = DEVICE_CLASS_BATTERY
|
||||||
_attr_unit_of_measurement = PERCENTAGE
|
_attr_unit_of_measurement = PERCENTAGE
|
||||||
|
|
||||||
def __init__(self, coordinator, device_id, api):
|
def __init__(self, coordinator, device_id):
|
||||||
"""Initialize the Somfy device."""
|
"""Initialize the Somfy device."""
|
||||||
super().__init__(coordinator, device_id, api)
|
super().__init__(coordinator, device_id)
|
||||||
self._climate = None
|
self._climate = None
|
||||||
self._create_device()
|
self._create_device()
|
||||||
|
|
||||||
def _create_device(self):
|
def _create_device(self):
|
||||||
"""Update the device with the latest data."""
|
"""Update the device with the latest data."""
|
||||||
self._climate = Thermostat(self.device, self.api)
|
self._climate = Thermostat(self.device, self.coordinator.client)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self) -> int:
|
def state(self) -> int:
|
||||||
|
|
|
@ -4,18 +4,17 @@ from pymfy.api.devices.category import Category
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity
|
from homeassistant.components.switch import SwitchEntity
|
||||||
|
|
||||||
from . import SomfyEntity
|
from .const import COORDINATOR, DOMAIN
|
||||||
from .const import API, COORDINATOR, DOMAIN
|
from .entity import SomfyEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the Somfy switch platform."""
|
"""Set up the Somfy switch platform."""
|
||||||
domain_data = hass.data[DOMAIN]
|
domain_data = hass.data[DOMAIN]
|
||||||
coordinator = domain_data[COORDINATOR]
|
coordinator = domain_data[COORDINATOR]
|
||||||
api = domain_data[API]
|
|
||||||
|
|
||||||
switches = [
|
switches = [
|
||||||
SomfyCameraShutter(coordinator, device_id, api)
|
SomfyCameraShutter(coordinator, device_id)
|
||||||
for device_id, device in coordinator.data.items()
|
for device_id, device in coordinator.data.items()
|
||||||
if Category.CAMERA.value in device.categories
|
if Category.CAMERA.value in device.categories
|
||||||
]
|
]
|
||||||
|
@ -26,14 +25,14 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
class SomfyCameraShutter(SomfyEntity, SwitchEntity):
|
class SomfyCameraShutter(SomfyEntity, SwitchEntity):
|
||||||
"""Representation of a Somfy Camera Shutter device."""
|
"""Representation of a Somfy Camera Shutter device."""
|
||||||
|
|
||||||
def __init__(self, coordinator, device_id, api):
|
def __init__(self, coordinator, device_id):
|
||||||
"""Initialize the Somfy device."""
|
"""Initialize the Somfy device."""
|
||||||
super().__init__(coordinator, device_id, api)
|
super().__init__(coordinator, device_id)
|
||||||
self._create_device()
|
self._create_device()
|
||||||
|
|
||||||
def _create_device(self):
|
def _create_device(self):
|
||||||
"""Update the device with the latest data."""
|
"""Update the device with the latest data."""
|
||||||
self.shutter = CameraProtect(self.device, self.api)
|
self.shutter = CameraProtect(self.device, self.coordinator.client)
|
||||||
|
|
||||||
def turn_on(self, **kwargs) -> None:
|
def turn_on(self, **kwargs) -> None:
|
||||||
"""Turn the entity on."""
|
"""Turn the entity on."""
|
||||||
|
|
|
@ -1582,7 +1582,7 @@ pymelcloud==2.5.3
|
||||||
pymeteoclimatic==0.0.6
|
pymeteoclimatic==0.0.6
|
||||||
|
|
||||||
# homeassistant.components.somfy
|
# homeassistant.components.somfy
|
||||||
pymfy==0.9.3
|
pymfy==0.11.0
|
||||||
|
|
||||||
# homeassistant.components.xiaomi_tv
|
# homeassistant.components.xiaomi_tv
|
||||||
pymitv==1.4.3
|
pymitv==1.4.3
|
||||||
|
|
|
@ -896,7 +896,7 @@ pymelcloud==2.5.3
|
||||||
pymeteoclimatic==0.0.6
|
pymeteoclimatic==0.0.6
|
||||||
|
|
||||||
# homeassistant.components.somfy
|
# homeassistant.components.somfy
|
||||||
pymfy==0.9.3
|
pymfy==0.11.0
|
||||||
|
|
||||||
# homeassistant.components.mochad
|
# homeassistant.components.mochad
|
||||||
pymochad==0.2.0
|
pymochad==0.2.0
|
||||||
|
|
|
@ -82,7 +82,9 @@ async def test_full_flow(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("homeassistant.components.somfy.api.ConfigEntrySomfyApi"):
|
with patch(
|
||||||
|
"homeassistant.components.somfy.async_setup_entry", return_value=True
|
||||||
|
) as mock_setup_entry:
|
||||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
|
||||||
assert result["data"]["auth_implementation"] == DOMAIN
|
assert result["data"]["auth_implementation"] == DOMAIN
|
||||||
|
@ -95,12 +97,7 @@ async def test_full_flow(
|
||||||
"expires_in": 60,
|
"expires_in": 60,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert DOMAIN in hass.config.components
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.LOADED
|
|
||||||
|
|
||||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
|
||||||
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
|
||||||
|
|
||||||
|
|
||||||
async def test_abort_if_authorization_timeout(hass, current_request_with_host):
|
async def test_abort_if_authorization_timeout(hass, current_request_with_host):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue