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:
parent
b41d3b465c
commit
00c60151d4
6 changed files with 164 additions and 0 deletions
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
],
|
],
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
133
homeassistant/components/ezviz/siren.py
Normal file
133
homeassistant/components/ezviz/siren.py
Normal 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()
|
|
@ -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": {
|
||||||
|
|
Loading…
Add table
Reference in a new issue