Add Ezviz siren entity (#93612)

* Initial commit

* Add siren entity

* Update coveragerc

* Cleanup unused entity description.

* Add restore and fix entity property to standards.

* Schedule turn off to match camera firmware

* Only add siren for devices that support capability

* Removed unused attribute and import.

* Add translation

* Update camera.py

* Update strings.json

* Update camera.py

* Cleanup

* Update homeassistant/components/ezviz/siren.py

Co-authored-by: G Johansson <goran.johansson@shiftit.se>

* use description

* Apply suggestions from code review

Co-authored-by: G Johansson <goran.johansson@shiftit.se>

* Update strings.json

* Dont inherit coordinator class.

* Add assumed state

* Update homeassistant/components/ezviz/siren.py

Co-authored-by: G Johansson <goran.johansson@shiftit.se>

* Reset delay listener if trigered

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
This commit is contained in:
Renier Moorcroft 2023-08-13 13:10:53 +02:00 committed by GitHub
parent b41d3b465c
commit 00c60151d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 164 additions and 0 deletions

View file

@ -341,6 +341,7 @@ omit =
homeassistant/components/ezviz/entity.py homeassistant/components/ezviz/entity.py
homeassistant/components/ezviz/select.py homeassistant/components/ezviz/select.py
homeassistant/components/ezviz/sensor.py homeassistant/components/ezviz/sensor.py
homeassistant/components/ezviz/siren.py
homeassistant/components/ezviz/switch.py homeassistant/components/ezviz/switch.py
homeassistant/components/ezviz/update.py homeassistant/components/ezviz/update.py
homeassistant/components/faa_delays/__init__.py homeassistant/components/faa_delays/__init__.py

View file

@ -42,6 +42,7 @@ PLATFORMS_BY_TYPE: dict[str, list] = {
Platform.NUMBER, Platform.NUMBER,
Platform.SELECT, Platform.SELECT,
Platform.SENSOR, Platform.SENSOR,
Platform.SIREN,
Platform.SWITCH, Platform.SWITCH,
Platform.UPDATE, Platform.UPDATE,
], ],

View file

@ -288,6 +288,17 @@ class EzvizCamera(EzvizEntity, Camera):
def perform_sound_alarm(self, enable: int) -> None: def perform_sound_alarm(self, enable: int) -> None:
"""Sound the alarm on a camera.""" """Sound the alarm on a camera."""
ir.async_create_issue(
self.hass,
DOMAIN,
"service_depreciation_sound_alarm",
breaks_in_ha_version="2024.3.0",
is_fixable=True,
is_persistent=True,
severity=ir.IssueSeverity.WARNING,
translation_key="service_depreciation_sound_alarm",
)
try: try:
self.coordinator.ezviz_client.sound_alarm(self._serial, enable) self.coordinator.ezviz_client.sound_alarm(self._serial, enable)
except HTTPError as err: except HTTPError as err:

View file

@ -45,6 +45,8 @@ class EzvizEntity(CoordinatorEntity[EzvizDataUpdateCoordinator], Entity):
class EzvizBaseEntity(Entity): class EzvizBaseEntity(Entity):
"""Generic entity for EZVIZ individual poll entities.""" """Generic entity for EZVIZ individual poll entities."""
_attr_has_entity_name = True
def __init__( def __init__(
self, self,
coordinator: EzvizDataUpdateCoordinator, coordinator: EzvizDataUpdateCoordinator,

View file

@ -0,0 +1,133 @@
"""Support for EZVIZ sirens."""
from __future__ import annotations
from collections.abc import Callable
from datetime import datetime, timedelta
from typing import Any
from pyezviz import HTTPError, PyEzvizError, SupportExt
from homeassistant.components.siren import (
SirenEntity,
SirenEntityDescription,
SirenEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_ON
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.helpers.event as evt
from homeassistant.helpers.restore_state import RestoreEntity
from .const import DATA_COORDINATOR, DOMAIN
from .coordinator import EzvizDataUpdateCoordinator
from .entity import EzvizBaseEntity
PARALLEL_UPDATES = 1
OFF_DELAY = timedelta(seconds=60) # Camera firmware has hard coded turn off.
SIREN_ENTITY_TYPE = SirenEntityDescription(
key="siren",
translation_key="siren",
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up EZVIZ sensors based on a config entry."""
coordinator: EzvizDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
DATA_COORDINATOR
]
async_add_entities(
EzvizSirenEntity(coordinator, camera, SIREN_ENTITY_TYPE)
for camera in coordinator.data
for capability, value in coordinator.data[camera]["supportExt"].items()
if capability == str(SupportExt.SupportActiveDefense.value)
if value != "0"
)
class EzvizSirenEntity(EzvizBaseEntity, SirenEntity, RestoreEntity):
"""Representation of a EZVIZ Siren entity."""
_attr_supported_features = SirenEntityFeature.TURN_ON | SirenEntityFeature.TURN_OFF
_attr_should_poll = False
_attr_assumed_state = True
def __init__(
self,
coordinator: EzvizDataUpdateCoordinator,
serial: str,
description: SirenEntityDescription,
) -> None:
"""Initialize the Siren."""
super().__init__(coordinator, serial)
self._attr_unique_id = f"{serial}_{description.key}"
self.entity_description = description
self._attr_is_on = False
self._delay_listener: Callable | None = None
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
if not (last_state := await self.async_get_last_state()):
return
self._attr_is_on = last_state.state == STATE_ON
if self._attr_is_on:
evt.async_call_later(self.hass, OFF_DELAY, self.off_delay_listener)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off camera siren."""
try:
result = await self.hass.async_add_executor_job(
self.coordinator.ezviz_client.sound_alarm, self._serial, 1
)
except (HTTPError, PyEzvizError) as err:
raise HomeAssistantError(
f"Failed to turn siren off for {self.name}"
) from err
if result:
if self._delay_listener is not None:
self._delay_listener()
self._delay_listener = None
self._attr_is_on = False
self.async_write_ha_state()
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on camera siren."""
try:
result = self.hass.async_add_executor_job(
self.coordinator.ezviz_client.sound_alarm, self._serial, 2
)
except (HTTPError, PyEzvizError) as err:
raise HomeAssistantError(
f"Failed to turn siren on for {self.name}"
) from err
if result:
if self._delay_listener is not None:
self._delay_listener()
self._delay_listener = None
self._attr_is_on = True
self._delay_listener = evt.async_call_later(
self.hass, OFF_DELAY, self.off_delay_listener
)
self.async_write_ha_state()
@callback
def off_delay_listener(self, now: datetime) -> None:
"""Switch device off after a delay.
Camera firmware has hard coded turn off after 60 seconds.
"""
self._attr_is_on = False
self._delay_listener = None
self.async_write_ha_state()

View file

@ -92,6 +92,17 @@
} }
} }
} }
},
"service_depreciation_sound_alarm": {
"title": "Ezviz Sound alarm service is being removed",
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::ezviz::issues::service_depreciation_sound_alarm::title%]",
"description": "Ezviz Sound alarm service is deprecated and will be removed.\nTo sound the alarm, you can instead use the `siren.toggle` service targeting the Siren entity.\n\nPlease remove the use of this service from your automations and scripts and select **submit** to fix this issue."
}
}
}
} }
}, },
"entity": { "entity": {
@ -216,6 +227,11 @@
"firmware": { "firmware": {
"name": "[%key:component::update::entity_component::firmware::name%]" "name": "[%key:component::update::entity_component::firmware::name%]"
} }
},
"siren": {
"siren": {
"name": "[%key:component::siren::title%]"
}
} }
}, },
"services": { "services": {