diff --git a/.coveragerc b/.coveragerc index b4b84dd4025..7e3bd6c1e50 100644 --- a/.coveragerc +++ b/.coveragerc @@ -876,6 +876,7 @@ omit = homeassistant/components/overkiz/scene.py homeassistant/components/overkiz/select.py homeassistant/components/overkiz/sensor.py + homeassistant/components/overkiz/siren.py homeassistant/components/overkiz/switch.py homeassistant/components/ovo_energy/__init__.py homeassistant/components/ovo_energy/const.py diff --git a/homeassistant/components/overkiz/const.py b/homeassistant/components/overkiz/const.py index 370e56feeeb..60a9163df5f 100644 --- a/homeassistant/components/overkiz/const.py +++ b/homeassistant/components/overkiz/const.py @@ -27,6 +27,7 @@ PLATFORMS: list[Platform] = [ Platform.NUMBER, Platform.SCENE, Platform.SENSOR, + Platform.SIREN, Platform.SWITCH, ] @@ -36,7 +37,7 @@ IGNORED_OVERKIZ_DEVICES: list[UIClass | UIWidget] = [ ] # Used to map the Somfy widget and ui_class to the Home Assistant platform -OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform] = { +OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform | None] = { UIClass.ADJUSTABLE_SLATS_ROLLER_SHUTTER: Platform.COVER, UIClass.AWNING: Platform.COVER, UIClass.CURTAIN: Platform.COVER, @@ -51,6 +52,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform] = { UIClass.ROLLER_SHUTTER: Platform.COVER, UIClass.SCREEN: Platform.COVER, UIClass.SHUTTER: Platform.COVER, + UIClass.SIREN: Platform.SIREN, UIClass.SWIMMING_POOL: Platform.SWITCH, UIClass.SWINGING_SHUTTER: Platform.COVER, UIClass.VENETIAN_BLIND: Platform.COVER, @@ -60,6 +62,7 @@ OVERKIZ_DEVICE_TO_PLATFORM: dict[UIClass | UIWidget, Platform] = { UIWidget.RTD_INDOOR_SIREN: Platform.SWITCH, # widgetName, uiClass is Siren (not supported) UIWidget.RTD_OUTDOOR_SIREN: Platform.SWITCH, # widgetName, uiClass is Siren (not supported) UIWidget.RTS_GENERIC: Platform.COVER, # widgetName, uiClass is Generic (not supported) + UIWidget.SIREN_STATUS: None, # widgetName, uiClass is Siren (siren) UIWidget.STATELESS_ALARM_CONTROLLER: Platform.SWITCH, # widgetName, uiClass is Alarm (not supported) UIWidget.STATELESS_EXTERIOR_HEATING: Platform.SWITCH, # widgetName, uiClass is ExteriorHeatingSystem (not supported) } diff --git a/homeassistant/components/overkiz/siren.py b/homeassistant/components/overkiz/siren.py new file mode 100644 index 00000000000..8f9d2d6167f --- /dev/null +++ b/homeassistant/components/overkiz/siren.py @@ -0,0 +1,71 @@ +"""Support for Overkiz sirens.""" +from typing import Any + +from pyoverkiz.enums import OverkizState +from pyoverkiz.enums.command import OverkizCommand, OverkizCommandParam + +from homeassistant.components.siren import SirenEntity +from homeassistant.components.siren.const import ( + ATTR_DURATION, + SUPPORT_DURATION, + SUPPORT_TURN_OFF, + SUPPORT_TURN_ON, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import HomeAssistantOverkizData +from .const import DOMAIN +from .entity import OverkizEntity + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Overkiz sirens from a config entry.""" + data: HomeAssistantOverkizData = hass.data[DOMAIN][entry.entry_id] + + async_add_entities( + OverkizSiren(device.device_url, data.coordinator) + for device in data.platforms[Platform.SIREN] + ) + + +class OverkizSiren(OverkizEntity, SirenEntity): + """Representation an Overkiz Siren.""" + + _attr_supported_features = SUPPORT_TURN_OFF | SUPPORT_TURN_ON | SUPPORT_DURATION + + @property + def is_on(self) -> bool: + """Get whether the siren is in on state.""" + return ( + self.executor.select_state(OverkizState.CORE_ON_OFF) + == OverkizCommandParam.ON + ) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Send the on command.""" + if kwargs.get(ATTR_DURATION): + duration = kwargs[ATTR_DURATION] + else: + duration = 2 * 60 # 2 minutes + + duration_in_ms = duration * 1000 + + await self.executor.async_execute_command( + # https://www.tahomalink.com/enduser-mobile-web/steer-html5-client/vendor/somfy/io/siren/const.js + OverkizCommand.RING_WITH_SINGLE_SIMPLE_SEQUENCE, + duration_in_ms, # duration + 75, # 90 seconds bip, 30 seconds silence + 2, # repeat 3 times + OverkizCommandParam.MEMORIZED_VOLUME, + ) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Send the off command.""" + await self.executor.async_execute_command(OverkizCommand.OFF)