diff --git a/homeassistant/components/ezviz/strings.json b/homeassistant/components/ezviz/strings.json index d60c4816d24f88..590f95029c6a85 100644 --- a/homeassistant/components/ezviz/strings.json +++ b/homeassistant/components/ezviz/strings.json @@ -163,6 +163,44 @@ "last_alarm_type_name": { "name": "Last alarm type name" } + }, + "switch": { + "status_light": { + "name": "Status light" + }, + "privacy": { + "name": "Privacy" + }, + "infrared_light": { + "name": "Infrared light" + }, + "sleep": { + "name": "Sleep" + }, + "audio": { + "name": "Audio" + }, + "motion_tracking": { + "name": "Motion tracking" + }, + "all_day_video_recording": { + "name": "All day video recording" + }, + "auto_sleep": { + "name": "Auto sleep" + }, + "flicker_light_on_movement": { + "name": "Flicker light on movement" + }, + "pir_motion_activated_light": { + "name": "PIR motion activated light" + }, + "tamper_alarm": { + "name": "Tamper alarm" + }, + "follow_movement": { + "name": "Follow movement" + } } }, "services": { diff --git a/homeassistant/components/ezviz/switch.py b/homeassistant/components/ezviz/switch.py index 58b28477412c85..337a70805063da 100644 --- a/homeassistant/components/ezviz/switch.py +++ b/homeassistant/components/ezviz/switch.py @@ -1,14 +1,20 @@ """Support for EZVIZ Switch sensors.""" from __future__ import annotations +from dataclasses import dataclass from typing import Any -from pyezviz.constants import DeviceSwitchType +from pyezviz.constants import DeviceSwitchType, SupportExt from pyezviz.exceptions import HTTPError, PyEzvizError -from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity +from homeassistant.components.switch import ( + SwitchDeviceClass, + SwitchEntity, + SwitchEntityDescription, +) from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DATA_COORDINATOR, DOMAIN @@ -16,6 +22,96 @@ from .entity import EzvizEntity +@dataclass +class EzvizSwitchEntityDescriptionMixin: + """Mixin values for EZVIZ Switch entities.""" + + supported_ext: str | None + + +@dataclass +class EzvizSwitchEntityDescription( + SwitchEntityDescription, EzvizSwitchEntityDescriptionMixin +): + """Describe a EZVIZ switch.""" + + +SWITCH_TYPES: dict[int, EzvizSwitchEntityDescription] = { + 3: EzvizSwitchEntityDescription( + key="3", + translation_key="status_light", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=None, + ), + 7: EzvizSwitchEntityDescription( + key="7", + translation_key="privacy", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportPtzPrivacy.value), + ), + 10: EzvizSwitchEntityDescription( + key="10", + translation_key="infrared_light", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportCloseInfraredLight.value), + ), + 21: EzvizSwitchEntityDescription( + key="21", + translation_key="sleep", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportSleep.value), + ), + 22: EzvizSwitchEntityDescription( + key="22", + translation_key="audio", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportAudioOnoff.value), + ), + 25: EzvizSwitchEntityDescription( + key="25", + translation_key="motion_tracking", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportIntelligentTrack.value), + ), + 29: EzvizSwitchEntityDescription( + key="29", + translation_key="all_day_video_recording", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportFulldayRecord.value), + ), + 32: EzvizSwitchEntityDescription( + key="32", + translation_key="auto_sleep", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportAutoSleep.value), + ), + 301: EzvizSwitchEntityDescription( + key="301", + translation_key="flicker_light_on_movement", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportActiveDefense.value), + ), + 305: EzvizSwitchEntityDescription( + key="305", + translation_key="pir_motion_activated_light", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportLightRelate.value), + ), + 306: EzvizSwitchEntityDescription( + key="306", + translation_key="tamper_alarm", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportTamperAlarm.value), + ), + 650: EzvizSwitchEntityDescription( + key="650", + translation_key="follow_movement", + device_class=SwitchDeviceClass.SWITCH, + supported_ext=str(SupportExt.SupportTracking.value), + ), +} + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: @@ -24,61 +120,66 @@ async def async_setup_entry( DATA_COORDINATOR ] - supported_switches = {switches.value for switches in DeviceSwitchType} - async_add_entities( - [ - EzvizSwitch(coordinator, camera, switch) - for camera in coordinator.data - for switch in coordinator.data[camera].get("switches") - if switch in supported_switches - ] + EzvizSwitch(coordinator, camera, switch_number) + for camera in coordinator.data + for switch_number in coordinator.data[camera]["switches"] + if switch_number in SWITCH_TYPES + if SWITCH_TYPES[switch_number].supported_ext + in coordinator.data[camera]["supportExt"] + or SWITCH_TYPES[switch_number].supported_ext is None ) class EzvizSwitch(EzvizEntity, SwitchEntity): """Representation of a EZVIZ sensor.""" - _attr_device_class = SwitchDeviceClass.SWITCH + _attr_has_entity_name = True def __init__( - self, coordinator: EzvizDataUpdateCoordinator, serial: str, switch: str + self, coordinator: EzvizDataUpdateCoordinator, serial: str, switch_number: int ) -> None: """Initialize the switch.""" super().__init__(coordinator, serial) - self._name = switch - self._attr_name = f"{self._camera_name} {DeviceSwitchType(switch).name.title()}" + self._switch_number = switch_number self._attr_unique_id = ( - f"{serial}_{self._camera_name}.{DeviceSwitchType(switch).name}" + f"{serial}_{self._camera_name}.{DeviceSwitchType(switch_number).name}" ) - - @property - def is_on(self) -> bool: - """Return the state of the switch.""" - return self.data["switches"][self._name] + self.entity_description = SWITCH_TYPES[switch_number] + self._attr_is_on = self.data["switches"][switch_number] async def async_turn_on(self, **kwargs: Any) -> None: """Change a device switch on the camera.""" try: - update_ok = await self.hass.async_add_executor_job( - self.coordinator.ezviz_client.switch_status, self._serial, self._name, 1 - ) + if await self.hass.async_add_executor_job( + self.coordinator.ezviz_client.switch_status, + self._serial, + self._switch_number, + 1, + ): + self._attr_is_on = True + self.async_write_ha_state() except (HTTPError, PyEzvizError) as err: - raise PyEzvizError(f"Failed to turn on switch {self._name}") from err - - if update_ok: - await self.coordinator.async_request_refresh() + raise HomeAssistantError(f"Failed to turn on switch {self.name}") from err async def async_turn_off(self, **kwargs: Any) -> None: """Change a device switch on the camera.""" try: - update_ok = await self.hass.async_add_executor_job( - self.coordinator.ezviz_client.switch_status, self._serial, self._name, 0 - ) + if await self.hass.async_add_executor_job( + self.coordinator.ezviz_client.switch_status, + self._serial, + self._switch_number, + 0, + ): + self._attr_is_on = False + self.async_write_ha_state() except (HTTPError, PyEzvizError) as err: - raise PyEzvizError(f"Failed to turn off switch {self._name}") from err + raise HomeAssistantError(f"Failed to turn off switch {self.name}") from err - if update_ok: - await self.coordinator.async_request_refresh() + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._attr_is_on = self.data["switches"].get(self._switch_number) + super()._handle_coordinator_update()