diff --git a/.coveragerc b/.coveragerc index 0e5eefeb9a4..6690ed5ee20 100644 --- a/.coveragerc +++ b/.coveragerc @@ -900,6 +900,7 @@ omit = homeassistant/components/screenlogic/__init__.py homeassistant/components/screenlogic/binary_sensor.py homeassistant/components/screenlogic/climate.py + homeassistant/components/screenlogic/light.py homeassistant/components/screenlogic/sensor.py homeassistant/components/screenlogic/services.py homeassistant/components/screenlogic/switch.py diff --git a/homeassistant/components/screenlogic/__init__.py b/homeassistant/components/screenlogic/__init__.py index a9ba02de18d..2178cf7ec2c 100644 --- a/homeassistant/components/screenlogic/__init__.py +++ b/homeassistant/components/screenlogic/__init__.py @@ -5,7 +5,9 @@ import logging from screenlogicpy import ScreenLogicError, ScreenLogicGateway from screenlogicpy.const import ( + DATA as SL_DATA, EQUIPMENT, + ON_OFF, SL_GATEWAY_IP, SL_GATEWAY_NAME, SL_GATEWAY_PORT, @@ -16,6 +18,7 @@ from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, CONF_SCAN_INTERVAL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, @@ -29,7 +32,10 @@ from .services import async_load_screenlogic_services, async_unload_screenlogic_ _LOGGER = logging.getLogger(__name__) -PLATFORMS = ["switch", "sensor", "binary_sensor", "climate"] + +REQUEST_REFRESH_DELAY = 1 + +PLATFORMS = ["switch", "sensor", "binary_sensor", "climate", "light"] async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: @@ -56,10 +62,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await coordinator.async_config_entry_first_refresh() - hass.data[DOMAIN][entry.entry_id] = { - "coordinator": coordinator, - "listener": entry.add_update_listener(async_update_listener), - } + entry.async_on_unload(entry.add_update_listener(async_update_listener)) + + hass.data[DOMAIN][entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) @@ -134,6 +139,11 @@ class ScreenlogicDataUpdateCoordinator(DataUpdateCoordinator): _LOGGER, name=DOMAIN, update_interval=interval, + # We don't want an immediate refresh since the device + # takes a moment to reflect the state change + request_refresh_debouncer=Debouncer( + hass, _LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False + ), ) def reconnect_gateway(self): @@ -221,3 +231,44 @@ class ScreenlogicEntity(CoordinatorEntity): "manufacturer": "Pentair", "model": equipment_model, } + + +class ScreenLogicCircuitEntity(ScreenlogicEntity): + """ScreenLogic circuit entity.""" + + @property + def name(self): + """Get the name of the switch.""" + return f"{self.gateway_name} {self.circuit['name']}" + + @property + def is_on(self) -> bool: + """Get whether the switch is in on state.""" + return self.circuit["value"] == ON_OFF.ON + + async def async_turn_on(self, **kwargs) -> None: + """Send the ON command.""" + await self._async_set_circuit(ON_OFF.ON) + + async def async_turn_off(self, **kwargs) -> None: + """Send the OFF command.""" + await self._async_set_circuit(ON_OFF.OFF) + + async def _async_set_circuit(self, circuit_value) -> None: + async with self.coordinator.api_lock: + success = await self.hass.async_add_executor_job( + self.gateway.set_circuit, self._data_key, circuit_value + ) + + if success: + _LOGGER.debug("Turn %s %s", self._data_key, circuit_value) + await self.coordinator.async_request_refresh() + else: + _LOGGER.warning( + "Failed to set_circuit %s %s", self._data_key, circuit_value + ) + + @property + def circuit(self): + """Shortcut to access the circuit.""" + return self.coordinator.data[SL_DATA.KEY_CIRCUITS][self._data_key] diff --git a/homeassistant/components/screenlogic/binary_sensor.py b/homeassistant/components/screenlogic/binary_sensor.py index f5d95d03be2..136f74d0d6c 100644 --- a/homeassistant/components/screenlogic/binary_sensor.py +++ b/homeassistant/components/screenlogic/binary_sensor.py @@ -15,7 +15,7 @@ SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS = {DEVICE_TYPE.ALARM: DEVICE_CLASS_PROBLEM} async def async_setup_entry(hass, config_entry, async_add_entities): """Set up entry.""" entities = [] - coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] + coordinator = hass.data[DOMAIN][config_entry.entry_id] # Generic binary sensor entities.append(ScreenLogicBinarySensor(coordinator, "chem_alarm")) diff --git a/homeassistant/components/screenlogic/climate.py b/homeassistant/components/screenlogic/climate.py index b83d2fe03ca..dc9e185ca9f 100644 --- a/homeassistant/components/screenlogic/climate.py +++ b/homeassistant/components/screenlogic/climate.py @@ -37,7 +37,7 @@ SUPPORTED_PRESETS = [ async def async_setup_entry(hass, config_entry, async_add_entities): """Set up entry.""" entities = [] - coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] + coordinator = hass.data[DOMAIN][config_entry.entry_id] for body in coordinator.data[SL_DATA.KEY_BODIES]: entities.append(ScreenLogicClimate(coordinator, body)) diff --git a/homeassistant/components/screenlogic/const.py b/homeassistant/components/screenlogic/const.py index 49a57b8d46e..2a1a3c23d2e 100644 --- a/homeassistant/components/screenlogic/const.py +++ b/homeassistant/components/screenlogic/const.py @@ -1,5 +1,5 @@ """Constants for the ScreenLogic integration.""" -from screenlogicpy.const import COLOR_MODE +from screenlogicpy.const import CIRCUIT_FUNCTION, COLOR_MODE from homeassistant.util import slugify @@ -13,4 +13,6 @@ SUPPORTED_COLOR_MODES = { slugify(name): num for num, name in COLOR_MODE.NAME_FOR_NUM.items() } +LIGHT_CIRCUIT_FUNCTIONS = {CIRCUIT_FUNCTION.INTELLIBRITE, CIRCUIT_FUNCTION.LIGHT} + DISCOVERED_GATEWAYS = "_discovered_gateways" diff --git a/homeassistant/components/screenlogic/light.py b/homeassistant/components/screenlogic/light.py new file mode 100644 index 00000000000..298b8a914a8 --- /dev/null +++ b/homeassistant/components/screenlogic/light.py @@ -0,0 +1,29 @@ +"""Support for a ScreenLogic light 'circuit' switch.""" +import logging + +from screenlogicpy.const import DATA as SL_DATA, GENERIC_CIRCUIT_NAMES + +from homeassistant.components.light import LightEntity + +from . import ScreenLogicCircuitEntity +from .const import DOMAIN, LIGHT_CIRCUIT_FUNCTIONS + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up entry.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id] + async_add_entities( + [ + ScreenLogicLight( + coordinator, circuit_num, circuit["name"] not in GENERIC_CIRCUIT_NAMES + ) + for circuit_num, circuit in coordinator.data[SL_DATA.KEY_CIRCUITS].items() + if circuit["function"] in LIGHT_CIRCUIT_FUNCTIONS + ] + ) + + +class ScreenLogicLight(ScreenLogicCircuitEntity, LightEntity): + """Class to represent a ScreenLogic Light.""" diff --git a/homeassistant/components/screenlogic/sensor.py b/homeassistant/components/screenlogic/sensor.py index 357e771f6be..cd7ba068be0 100644 --- a/homeassistant/components/screenlogic/sensor.py +++ b/homeassistant/components/screenlogic/sensor.py @@ -51,7 +51,7 @@ SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS = { async def async_setup_entry(hass, config_entry, async_add_entities): """Set up entry.""" entities = [] - coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] + coordinator = hass.data[DOMAIN][config_entry.entry_id] equipment_flags = coordinator.data[SL_DATA.KEY_CONFIG]["equipment_flags"] # Generic sensors diff --git a/homeassistant/components/screenlogic/services.py b/homeassistant/components/screenlogic/services.py index 31e35788f44..d5edda12abb 100644 --- a/homeassistant/components/screenlogic/services.py +++ b/homeassistant/components/screenlogic/services.py @@ -51,7 +51,7 @@ def async_load_screenlogic_services(hass: HomeAssistant): ) color_num = SUPPORTED_COLOR_MODES[service_call.data[ATTR_COLOR_MODE]] for entry_id in screenlogic_entry_ids: - coordinator = hass.data[DOMAIN][entry_id]["coordinator"] + coordinator = hass.data[DOMAIN][entry_id] _LOGGER.debug( "Service %s called on %s with mode %s", SERVICE_SET_COLOR_MODE, diff --git a/homeassistant/components/screenlogic/switch.py b/homeassistant/components/screenlogic/switch.py index ff73afebb57..97c95be85ca 100644 --- a/homeassistant/components/screenlogic/switch.py +++ b/homeassistant/components/screenlogic/switch.py @@ -1,64 +1,29 @@ """Support for a ScreenLogic 'circuit' switch.""" import logging -from screenlogicpy.const import DATA as SL_DATA, GENERIC_CIRCUIT_NAMES, ON_OFF +from screenlogicpy.const import DATA as SL_DATA, GENERIC_CIRCUIT_NAMES from homeassistant.components.switch import SwitchEntity -from . import ScreenlogicEntity -from .const import DOMAIN +from . import ScreenLogicCircuitEntity +from .const import DOMAIN, LIGHT_CIRCUIT_FUNCTIONS _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass, config_entry, async_add_entities): """Set up entry.""" - entities = [] - coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] - - for circuit_num, circuit in coordinator.data[SL_DATA.KEY_CIRCUITS].items(): - enabled = circuit["name"] not in GENERIC_CIRCUIT_NAMES - entities.append(ScreenLogicSwitch(coordinator, circuit_num, enabled)) - - async_add_entities(entities) - - -class ScreenLogicSwitch(ScreenlogicEntity, SwitchEntity): - """ScreenLogic switch entity.""" - - @property - def name(self): - """Get the name of the switch.""" - return f"{self.gateway_name} {self.circuit['name']}" - - @property - def is_on(self) -> bool: - """Get whether the switch is in on state.""" - return self.circuit["value"] == 1 - - async def async_turn_on(self, **kwargs) -> None: - """Send the ON command.""" - return await self._async_set_circuit(ON_OFF.ON) - - async def async_turn_off(self, **kwargs) -> None: - """Send the OFF command.""" - return await self._async_set_circuit(ON_OFF.OFF) - - async def _async_set_circuit(self, circuit_value) -> None: - async with self.coordinator.api_lock: - success = await self.hass.async_add_executor_job( - self.gateway.set_circuit, self._data_key, circuit_value + coordinator = hass.data[DOMAIN][config_entry.entry_id] + async_add_entities( + [ + ScreenLogicSwitch( + coordinator, circuit_num, circuit["name"] not in GENERIC_CIRCUIT_NAMES ) + for circuit_num, circuit in coordinator.data[SL_DATA.KEY_CIRCUITS].items() + if circuit["function"] not in LIGHT_CIRCUIT_FUNCTIONS + ] + ) - if success: - _LOGGER.debug("Turn %s %s", self._data_key, circuit_value) - await self.coordinator.async_request_refresh() - else: - _LOGGER.warning( - "Failed to set_circuit %s %s", self._data_key, circuit_value - ) - @property - def circuit(self): - """Shortcut to access the circuit.""" - return self.coordinator.data[SL_DATA.KEY_CIRCUITS][self._data_key] +class ScreenLogicSwitch(ScreenLogicCircuitEntity, SwitchEntity): + """Class to represent a ScreenLogic Switch."""