diff --git a/.coveragerc b/.coveragerc index 178c130ac13a0e58b9a37f852d5f10ffb9959902..ce11e4822a8f665f46da441efb1dfdc1b5138319 100644 --- a/.coveragerc +++ b/.coveragerc @@ -129,6 +129,7 @@ omit = homeassistant/components/bosch_shc/cover.py homeassistant/components/bosch_shc/entity.py homeassistant/components/bosch_shc/sensor.py + homeassistant/components/bosch_shc/switch.py homeassistant/components/braviatv/__init__.py homeassistant/components/braviatv/const.py homeassistant/components/braviatv/media_player.py diff --git a/homeassistant/components/bosch_shc/__init__.py b/homeassistant/components/bosch_shc/__init__.py index afcf2571c31a2ae7e7020f5608636b99b855b057..2b95702e44ce11070852c2fe3a957cd89cdecf09 100644 --- a/homeassistant/components/bosch_shc/__init__.py +++ b/homeassistant/components/bosch_shc/__init__.py @@ -19,7 +19,7 @@ from .const import ( DOMAIN, ) -PLATFORMS = [Platform.BINARY_SENSOR, Platform.COVER, Platform.SENSOR] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.COVER, Platform.SENSOR, Platform.SWITCH] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/bosch_shc/sensor.py b/homeassistant/components/bosch_shc/sensor.py index dc1806f0ce5573d60b5d5784cec0dd23a54bf41b..331a5ebb5f39f26d11b8226285405df0f96fdd98 100644 --- a/homeassistant/components/bosch_shc/sensor.py +++ b/homeassistant/components/bosch_shc/sensor.py @@ -145,6 +145,13 @@ async def async_setup_entry( entry_id=config_entry.entry_id, ) ) + entities.append( + CommunicationQualitySensor( + device=sensor, + parent_id=session.information.unique_id, + entry_id=config_entry.entry_id, + ) + ) if entities: async_add_entities(entities) @@ -241,6 +248,23 @@ class TemperatureRatingSensor(SHCEntity, SensorEntity): return self._device.temperature_rating.name +class CommunicationQualitySensor(SHCEntity, SensorEntity): + """Representation of an SHC communication quality reporting sensor.""" + + _attr_icon = "mdi:wifi" + + def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: + """Initialize an SHC communication quality reporting sensor.""" + super().__init__(device, parent_id, entry_id) + self._attr_name = f"{device.name} Communication Quality" + self._attr_unique_id = f"{device.serial}_communication_quality" + + @property + def native_value(self): + """Return the state of the sensor.""" + return self._device.communicationquality.name + + class HumidityRatingSensor(SHCEntity, SensorEntity): """Representation of an SHC humidity rating sensor.""" diff --git a/homeassistant/components/bosch_shc/switch.py b/homeassistant/components/bosch_shc/switch.py new file mode 100644 index 0000000000000000000000000000000000000000..666eb6554d9d25fd8a6af00d9bac60ca6e8b4648 --- /dev/null +++ b/homeassistant/components/bosch_shc/switch.py @@ -0,0 +1,235 @@ +"""Platform for switch integration.""" +from __future__ import annotations + +from dataclasses import dataclass + +from boschshcpy import ( + SHCCamera360, + SHCCameraEyes, + SHCLightSwitch, + SHCSession, + SHCSmartPlug, + SHCSmartPlugCompact, +) +from boschshcpy.device import SHCDevice + +from homeassistant.components.switch import ( + SwitchDeviceClass, + SwitchEntity, + SwitchEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType + +from .const import DATA_SESSION, DOMAIN +from .entity import SHCEntity + + +@dataclass +class SHCSwitchRequiredKeysMixin: + """Mixin for SHC switch required keys.""" + + on_key: str + on_value: StateType + should_poll: bool + + +@dataclass +class SHCSwitchEntityDescription( + SwitchEntityDescription, + SHCSwitchRequiredKeysMixin, +): + """Class describing SHC switch entities.""" + + +SWITCH_TYPES: dict[str, SHCSwitchEntityDescription] = { + "smartplug": SHCSwitchEntityDescription( + key="smartplug", + device_class=SwitchDeviceClass.OUTLET, + on_key="state", + on_value=SHCSmartPlug.PowerSwitchService.State.ON, + should_poll=False, + ), + "smartplugcompact": SHCSwitchEntityDescription( + key="smartplugcompact", + device_class=SwitchDeviceClass.OUTLET, + on_key="state", + on_value=SHCSmartPlugCompact.PowerSwitchService.State.ON, + should_poll=False, + ), + "lightswitch": SHCSwitchEntityDescription( + key="lightswitch", + device_class=SwitchDeviceClass.SWITCH, + on_key="state", + on_value=SHCLightSwitch.PowerSwitchService.State.ON, + should_poll=False, + ), + "cameraeyes": SHCSwitchEntityDescription( + key="cameraeyes", + device_class=SwitchDeviceClass.SWITCH, + on_key="cameralight", + on_value=SHCCameraEyes.CameraLightService.State.ON, + should_poll=True, + ), + "camera360": SHCSwitchEntityDescription( + key="camera360", + device_class=SwitchDeviceClass.SWITCH, + on_key="privacymode", + on_value=SHCCamera360.PrivacyModeService.State.DISABLED, + should_poll=True, + ), +} + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the SHC switch platform.""" + entities: list[SwitchEntity] = [] + session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION] + + for switch in session.device_helper.smart_plugs: + + entities.append( + SHCSwitch( + device=switch, + parent_id=session.information.unique_id, + entry_id=config_entry.entry_id, + description=SWITCH_TYPES["smartplug"], + ) + ) + entities.append( + SHCRoutingSwitch( + device=switch, + parent_id=session.information.unique_id, + entry_id=config_entry.entry_id, + ) + ) + + for switch in session.device_helper.light_switches: + + entities.append( + SHCSwitch( + device=switch, + parent_id=session.information.unique_id, + entry_id=config_entry.entry_id, + description=SWITCH_TYPES["lightswitch"], + ) + ) + + for switch in session.device_helper.smart_plugs_compact: + + entities.append( + SHCSwitch( + device=switch, + parent_id=session.information.unique_id, + entry_id=config_entry.entry_id, + description=SWITCH_TYPES["smartplugcompact"], + ) + ) + + for switch in session.device_helper.camera_eyes: + + entities.append( + SHCSwitch( + device=switch, + parent_id=session.information.unique_id, + entry_id=config_entry.entry_id, + description=SWITCH_TYPES["cameraeyes"], + ) + ) + + for switch in session.device_helper.camera_360: + + entities.append( + SHCSwitch( + device=switch, + parent_id=session.information.unique_id, + entry_id=config_entry.entry_id, + description=SWITCH_TYPES["camera360"], + ) + ) + + if entities: + async_add_entities(entities) + + +class SHCSwitch(SHCEntity, SwitchEntity): + """Representation of a SHC switch.""" + + entity_description: SHCSwitchEntityDescription + + def __init__( + self, + device: SHCDevice, + parent_id: str, + entry_id: str, + description: SHCSwitchEntityDescription, + ) -> None: + """Initialize a SHC switch.""" + super().__init__(device, parent_id, entry_id) + self.entity_description = description + + @property + def is_on(self) -> bool: + """Return the state of the switch.""" + return ( + getattr(self._device, self.entity_description.on_key) + == self.entity_description.on_value + ) + + def turn_on(self, **kwargs) -> None: + """Turn the switch on.""" + setattr(self._device, self.entity_description.on_key, True) + + def turn_off(self, **kwargs) -> None: + """Turn the switch off.""" + setattr(self._device, self.entity_description.on_key, False) + + def toggle(self, **kwargs) -> None: + """Toggle the switch.""" + setattr(self._device, self.entity_description.on_key, not self.is_on) + + @property + def should_poll(self) -> bool: + """Switch needs polling.""" + return self.entity_description.should_poll + + def update(self) -> None: + """Trigger an update of the device.""" + self._device.update() + + +class SHCRoutingSwitch(SHCEntity, SwitchEntity): + """Representation of a SHC routing switch.""" + + _attr_icon = "mdi:wifi" + _attr_entity_category = EntityCategory.CONFIG + + def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: + """Initialize an SHC communication quality reporting sensor.""" + super().__init__(device, parent_id, entry_id) + self._attr_name = f"{device.name} Routing" + self._attr_unique_id = f"{device.serial}_routing" + + @property + def is_on(self) -> bool: + """Return the state of the switch.""" + return self._device.routing.name == "ENABLED" + + def turn_on(self, **kwargs) -> None: + """Turn the switch on.""" + self._device.routing = True + + def turn_off(self, **kwargs) -> None: + """Turn the switch off.""" + self._device.routing = False + + def toggle(self, **kwargs) -> None: + """Toggle the switch.""" + self._device.routing = not self.is_on