Skip to content

Commit

Permalink
Add new platform siren (#300)
Browse files Browse the repository at this point in the history
  • Loading branch information
SukramJ authored Feb 15, 2022
1 parent 6f5efb3 commit dff095a
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 7 deletions.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Version 0.34.0 (2022-02-15)
- Use backported StrEnum
- Sort constants to identify HA constants
- Add new platform siren

Version 0.33.0 (2022-02-14)
- Make parameter availability more robust
Expand Down
2 changes: 2 additions & 0 deletions hahomematic/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class HmPlatform(StrEnum):
NUMBER = "number"
SELECT = "select"
SENSOR = "sensor"
SIREN = "siren"
SWITCH = "switch"
TEXT = "text"

Expand Down Expand Up @@ -245,5 +246,6 @@ class HmInterfaceEventType(StrEnum):
HmPlatform.NUMBER,
HmPlatform.SELECT,
HmPlatform.SENSOR,
HmPlatform.SIREN,
HmPlatform.SWITCH,
]
4 changes: 3 additions & 1 deletion hahomematic/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
from collections.abc import Callable
from typing import Any

from hahomematic.devices import climate, cover, light, lock, switch
from hahomematic.devices import climate, cover, light, lock, siren, switch

_ALL_DEVICES = [
cover.DEVICES,
climate.DEVICES,
light.DEVICES,
lock.DEVICES,
siren.DEVICES,
switch.DEVICES,
]

Expand All @@ -19,6 +20,7 @@
climate.BLACKLISTED_DEVICES,
light.BLACKLISTED_DEVICES,
lock.BLACKLISTED_DEVICES,
siren.BLACKLISTED_DEVICES,
switch.BLACKLISTED_DEVICES,
]

Expand Down
18 changes: 18 additions & 0 deletions hahomematic/devices/entity_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@
FIELD_VALVE_STATE = "valve_state"
FIELD_VOLTAGE = "voltage"

FIELD_ACOUSTIC_ALARM_ACTIVE = "acoustic_alarm_active"
FIELD_ACOUSTIC_ALARM_SELECTION = "acoustic_alarm_selection"
FIELD_OPTICAL_ALARM_ACTIVE = "optical_alarm_active"
FIELD_OPTICAL_ALARM_SELECTION = "optical_alarm_selection"

_LOGGER = logging.getLogger(__name__)


Expand All @@ -95,12 +100,14 @@ class EntityDefinition(StrEnum):
IP_LOCK = "IPLock"
IP_THERMOSTAT = "IPThermostat"
IP_THERMOSTAT_GROUP = "IPThermostatGroup"
IP_SIREN = "IPSiren"
RF_COVER = "RfCover"
RF_DIMMER = "RfDimmer"
RF_DIMMER_WITH_VIRT_CHANNEL = "RfDimmerWithVirtChannel"
RF_LOCK = "RfLock"
RF_THERMOSTAT = "RfThermostat"
RF_THERMOSTAT_GROUP = "RfThermostatGroup"
RF_SIREN = "RfSiren"
SIMPLE_RF_THERMOSTAT = "SimpleRfThermostat"


Expand Down Expand Up @@ -274,6 +281,17 @@ class EntityDefinition(StrEnum):
},
},
},
EntityDefinition.IP_SIREN: {
ED_DEVICE_GROUP: {
ED_PRIMARY_CHANNEL: 3,
ED_REPEATABLE_FIELDS: {
FIELD_ACOUSTIC_ALARM_ACTIVE: "ACOUSTIC_ALARM_ACTIVE",
FIELD_ACOUSTIC_ALARM_SELECTION: "ACOUSTIC_ALARM_SELECTION",
FIELD_OPTICAL_ALARM_ACTIVE: "ACOUSTIC_ALARM_ACTIVE",
FIELD_OPTICAL_ALARM_SELECTION: "ACOUSTIC_ALARM_SELECTION",
},
},
},
EntityDefinition.IP_THERMOSTAT: {
ED_DEVICE_GROUP: {
ED_PRIMARY_CHANNEL: 0,
Expand Down
207 changes: 207 additions & 0 deletions hahomematic/devices/siren.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
"""Code to create the required entities for siren devices."""

from __future__ import annotations

from abc import abstractmethod
import logging
from typing import Any, cast

from hahomematic.const import HmPlatform
import hahomematic.device as hm_device
from hahomematic.devices.entity_definition import (
FIELD_ACOUSTIC_ALARM_ACTIVE,
FIELD_ACOUSTIC_ALARM_SELECTION,
FIELD_OPTICAL_ALARM_ACTIVE,
FIELD_OPTICAL_ALARM_SELECTION,
EntityDefinition,
make_custom_entity,
)
import hahomematic.entity as hm_entity
from hahomematic.entity import CustomEntity
from hahomematic.internal.action import HmAction

_LOGGER = logging.getLogger(__name__)

# HM constants
HMIP_ACOUSTIC_ALARM_SELECTION = "ACOUSTIC_ALARM_SELECTION"
HMIP_OPTICAL_ALARM_SELECTION = "OPTICAL_ALARM_SELECTION"
HMIP_DURATION_UNIT = "DURATION_UNIT"
HMIP_DURATION_VALUE = "DURATION_VALUE"

DEFAULT_ACOUSTIC_ALARM_SELECTION = "FREQUENCY_RISING_AND_FALLING"
DEFAULT_OPTICAL_ALARM_SELECTION = "BLINKING_ALTERNATELY_REPEATING"
DISABLE_ACOUSTIC_SIGNAL = "DISABLE_ACOUSTIC_SIGNAL"
DISABLE_OPTICAL_SIGNAL = "DISABLE_OPTICAL_SIGNAL"
DEFAULT_DURATION_UNIT = "S"
DEFAULT_DURATION_VALUE = 60


class BaseSiren(CustomEntity):
"""Class for homematic siren entities."""

def __init__(
self,
device: hm_device.HmDevice,
device_address: str,
unique_id: str,
device_enum: EntityDefinition,
device_def: dict[str, Any],
entity_def: dict[int, set[str]],
channel_no: int,
):
super().__init__(
device=device,
unique_id=unique_id,
device_address=device_address,
device_enum=device_enum,
device_def=device_def,
entity_def=entity_def,
platform=HmPlatform.SIREN,
channel_no=channel_no,
)
_LOGGER.debug(
"HMSiren.__init__(%s, %s, %s)",
self._device.interface_id,
device_address,
unique_id,
)

@property
def is_on(self) -> bool:
"""Return true if siren is on."""
return False

@property
def available_tones(self) -> list[str] | None:
"""Return a list of available tones.."""
return None

@abstractmethod
async def turn_on(
self, acoustic_alarm: str, optical_alarm: str, duration: int
) -> None:
"""Turn the device on."""
...

@abstractmethod
async def turn_off(self) -> None:
"""Turn the device off."""
...


class CeIpSiren(BaseSiren):
"""Class for homematic ip siren entities."""

@property
def _acoustic_alarm_active(self) -> bool:
"""Return if the acoustic alarm is active the siren."""
return self._get_entity_value(field_name=FIELD_ACOUSTIC_ALARM_ACTIVE) is True

@property
def _e_acoustic_alarm_selection(self) -> HmAction:
"""Return the available accoustic alarms entity of the siren."""
return self._get_entity(
field_name=FIELD_ACOUSTIC_ALARM_SELECTION, entity_type=HmAction
)

@property
def _optical_alarm_active(self) -> bool:
"""Return if the optical alarm is active the siren."""
return self._get_entity_value(field_name=FIELD_OPTICAL_ALARM_ACTIVE) is True

@property
def _e_optical_alarm_selection(self) -> HmAction:
"""Return the available optical alarms entity of the siren."""
return self._get_entity(
field_name=FIELD_OPTICAL_ALARM_SELECTION, entity_type=HmAction
)

@property
def is_on(self) -> bool:
"""Return true if siren is on."""
return self._acoustic_alarm_active is True or self._optical_alarm_active is True

@property
def available_tones(self) -> list[str] | None:
"""Return a list of available tones."""
return cast(list[str], self._e_acoustic_alarm_selection.value_list)

async def turn_on(
self,
acoustic_alarm: str,
optical_alarm: str,
duration: int = DEFAULT_DURATION_VALUE,
) -> None:
"""Turn the device on."""
await self._client.put_paramset(
channel_address=f"{self.device_address}:3",
paramset_key="VALUES",
value={
HMIP_ACOUSTIC_ALARM_SELECTION: acoustic_alarm,
HMIP_OPTICAL_ALARM_SELECTION: optical_alarm,
HMIP_DURATION_UNIT: DEFAULT_DURATION_UNIT,
HMIP_DURATION_VALUE: duration,
},
)

async def turn_off(self) -> None:
"""Turn the device off."""
await self._client.put_paramset(
channel_address=f"{self.device_address}:3",
paramset_key="VALUES",
value={
HMIP_ACOUSTIC_ALARM_SELECTION: DISABLE_ACOUSTIC_SIGNAL,
HMIP_OPTICAL_ALARM_SELECTION: DISABLE_OPTICAL_SIGNAL,
HMIP_DURATION_UNIT: DEFAULT_DURATION_UNIT,
HMIP_DURATION_VALUE: 1,
},
)


class CeRfSiren(BaseSiren):
"""Class for classic homematic siren entities."""

async def turn_on(
self, acoustic_alarm: str, optical_alarm: str, duration: int
) -> None:
"""Turn the device on."""
...

async def turn_off(self) -> None:
"""Turn the device off."""
...


def make_ip_siren(
device: hm_device.HmDevice, device_address: str, group_base_channels: list[int]
) -> list[hm_entity.BaseEntity]:
"""Creates homematic ip siren entities."""
return make_custom_entity(
device=device,
device_address=device_address,
custom_entity_class=CeIpSiren,
device_enum=EntityDefinition.IP_SIREN,
group_base_channels=group_base_channels,
)


def make_rf_siren(
device: hm_device.HmDevice, device_address: str, group_base_channels: list[int]
) -> list[hm_entity.BaseEntity]:
"""Creates homematic rf siren entities."""
return make_custom_entity(
device=device,
device_address=device_address,
custom_entity_class=CeRfSiren,
device_enum=EntityDefinition.RF_SIREN,
group_base_channels=group_base_channels,
)


# Case for device model is not relevant
# device_type and sub_type(IP-only) can be used here
DEVICES: dict[str, tuple[Any, list[int]]] = {
"HmIP-ASIR": (make_ip_siren, [0]),
}

BLACKLISTED_DEVICES: list[str] = []
12 changes: 6 additions & 6 deletions tests/test_central.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,17 @@ async def test_central(central, loop) -> None:
counter = usage_types[entity.usage]
usage_types[entity.usage] = counter + 1

assert usage_types[HmEntityUsage.ENTITY_NO_CREATE] == 1831
assert usage_types[HmEntityUsage.CE_PRIMARY] == 161
assert usage_types[HmEntityUsage.ENTITY] == 2468
assert usage_types[HmEntityUsage.ENTITY_NO_CREATE] == 1843
assert usage_types[HmEntityUsage.CE_PRIMARY] == 163
assert usage_types[HmEntityUsage.ENTITY] == 2456
assert usage_types[HmEntityUsage.CE_SENSOR] == 63
assert usage_types[HmEntityUsage.CE_SECONDARY] == 126

assert len(central.hm_devices) == 362
assert len(central.hm_entities) == 4649
assert len(central.hm_entities) == 4651
assert len(data) == 362
assert len(custom_entities) == 287
assert len(ce_channels) == 99
assert len(custom_entities) == 289
assert len(ce_channels) == 101
assert len(entity_types) == 6
assert len(parameters) == 178

Expand Down

0 comments on commit dff095a

Please sign in to comment.