Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions zhaquirks/builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
PreventDefaultEntityCreationMetadata,
QuirkDefinition,
ReportingConfig,
SirenMetadata,
SwitchMetadata,
WriteAttributeButtonMetadata,
ZCLCommandButtonMetadata,
Expand Down Expand Up @@ -750,6 +751,59 @@ def switch(
)
return self

def siren(
self,
attribute_name: str,
cluster_id: int,
cluster_type: ClusterType = ClusterType.Server,
endpoint_id: int = 1,
available_tones: dict[int, str] | None = None,
off_value: int = 0,
default_tone: int | None = None,
entity_type: EntityType = EntityType.STANDARD,
initially_disabled: bool = False,
attribute_initialized_from_cache: bool = True,
reporting_config: ReportingConfig | None = None,
unique_id_suffix: str | None = None,
translation_key: str | None = None,
fallback_name: str | None = None,
primary: bool | None = None,
*,
translation_placeholders: dict[str, str] | None = None,
) -> Self:
"""Add an EntityMetadata containing SirenMetadata and return self.

This exposes a siren entity in Home Assistant that is controlled by
writing ``attribute_name``: turning on writes a tone value (the requested
tone, else ``default_tone``), turning off writes ``off_value``. The
entity's state follows the cached attribute value, so a device that
resets the attribute on its own keeps the entity in sync. Pass
``available_tones`` (a ``{value: name}`` mapping) to let the user pick a
tone in Home Assistant.
"""
self._add_entity_metadata(
SirenMetadata(
endpoint_id=endpoint_id,
cluster_id=cluster_id,
cluster_type=cluster_type,
entity_platform=EntityPlatform.SIREN,
entity_type=entity_type,
initially_disabled=initially_disabled,
attribute_initialized_from_cache=attribute_initialized_from_cache,
reporting_config=reporting_config,
unique_id_suffix=unique_id_suffix,
translation_key=translation_key,
translation_placeholders=translation_placeholders or {},
fallback_name=fallback_name,
attribute_name=attribute_name,
available_tones=available_tones or {},
off_value=off_value,
default_tone=default_tone,
primary=primary,
)
)
return self

def number(
self,
attribute_name: str,
Expand Down
10 changes: 10 additions & 0 deletions zhaquirks/builder/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
number,
select,
sensor,
siren,
switch,
)
from zigpy.zcl import ClusterType, ReportingConfig
Expand All @@ -31,6 +32,7 @@
BinarySensorMetadata,
EntityMetadata,
NumberMetadata,
SirenMetadata,
SwitchMetadata,
WriteAttributeButtonMetadata,
ZCLCommandButtonMetadata,
Expand All @@ -53,6 +55,7 @@
(Platform.SENSOR, ZCLSensorMetadata): sensor.Sensor,
(Platform.SELECT, ZCLEnumMetadata): select.ZCLEnumSelectEntity,
(Platform.NUMBER, NumberMetadata): number.NumberConfigurationEntity,
(Platform.SIREN, SirenMetadata): siren.AttributeSiren,
(Platform.SWITCH, SwitchMetadata): switch.ConfigurableAttributeSwitch,
}

Expand Down Expand Up @@ -108,6 +111,13 @@ def _platform_kwargs(entity_metadata: EntityMetadata) -> dict[str, Any]:
"attribute_converter": entity_metadata.attribute_converter,
"device_class": entity_metadata.device_class,
}
if isinstance(entity_metadata, SirenMetadata):
return {
"attribute_name": entity_metadata.attribute_name,
"available_tones": dict(entity_metadata.available_tones),
"off_value": entity_metadata.off_value,
"default_tone": entity_metadata.default_tone,
}
if isinstance(entity_metadata, ZCLEnumMetadata):
return {
"attribute_name": entity_metadata.attribute_name,
Expand Down
13 changes: 13 additions & 0 deletions zhaquirks/builder/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,19 @@ class BinarySensorMetadata(EntityMetadata):
device_class: BinarySensorDeviceClass | None = attrs.field(default=None)


@attrs.define(frozen=True, kw_only=True, repr=True)
class SirenMetadata(EntityMetadata):
"""Metadata for an exposed attribute-controlled siren entity."""

attribute_name: str = attrs.field()
available_tones: frozendict[int, str] = attrs.field(
factory=frozendict, converter=frozendict
)
off_value: int = attrs.field(default=0)
default_tone: int | None = attrs.field(default=None)
reporting_config: ReportingConfig | None = attrs.field(default=None)


@attrs.define(frozen=True, kw_only=True, repr=True)
class WriteAttributeButtonMetadata(EntityMetadata):
"""Metadata for exposed button entity that writes an attribute when pressed."""
Expand Down
24 changes: 16 additions & 8 deletions zhaquirks/heiman/hm_722_esy_e_plus.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Heiman HM-722-ESY-E-PLUS Co sensor."""

from zha.quirks import SIREN_BASIC
import zigpy.types as t
from zigpy.zcl.clusters.security import IasWd, IasZone
from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef
Expand Down Expand Up @@ -87,14 +86,23 @@ class AttributeDefs(BaseAttributeDefs):
.applies_to("HEIMAN", "HM-722ESY-E-PLUS")
.friendly_name(manufacturer="HEIMAN", model="HM-722ESY-E-PLUS")
.replaces(CustomHeimanCluster)
.exposes_feature(SIREN_BASIC)
.change_entity_metadata(
endpoint_id=1,
cluster_id=IasWd.cluster_id,
new_primary=False,
new_entity_category=EntityType.CONFIG,
# Replace the IAS WD siren (fixed tone, ZHA-limited to ~30 s) with an
# attribute-controlled siren that supports tone selection and sounds until
# turned off. Reuse the IAS WD siren's unique_id so existing entities migrate.
.prevent_default_entity_creation(endpoint_id=1, cluster_id=IasWd.cluster_id)
.siren(
CustomHeimanCluster.AttributeDefs.siren_for_automation.name,
CustomHeimanCluster.cluster_id,
available_tones={
SmokeCoSirenEnum.Smoke_siren: "Smoke siren",
SmokeCoSirenEnum.CO_siren: "CO siren",
},
off_value=SmokeCoSirenEnum.Stop,
default_tone=SmokeCoSirenEnum.CO_siren,
unique_id_suffix=str(IasWd.cluster_id),
translation_key="siren",
fallback_name="Siren",
)
# XXX: siren_for_automation should be added as a siren entity, needs zigpy API
.binary_sensor(
CustomHeimanCluster.AttributeDefs.sensor_self_check_state.name,
CustomHeimanCluster.cluster_id,
Expand Down
24 changes: 16 additions & 8 deletions zhaquirks/heiman/hs1ca_e_plus.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Heiman HS1CA-E-PLUS CO sensor."""

from zha.quirks import SIREN_BASIC
import zigpy.types as t
from zigpy.zcl.clusters.security import IasWd, IasZone
from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef
Expand Down Expand Up @@ -88,14 +87,23 @@ class AttributeDefs(BaseAttributeDefs):
.applies_to("HEIMAN", "HS1CA-E-PLUS")
.friendly_name(manufacturer="HEIMAN", model="HS1CA-E-PLUS")
.replaces(CustomHeimanCluster)
.exposes_feature(SIREN_BASIC)
.change_entity_metadata(
endpoint_id=1,
cluster_id=IasWd.cluster_id,
new_primary=False,
new_entity_category=EntityType.CONFIG,
# Replace the IAS WD siren (fixed tone, ZHA-limited to ~30 s) with an
# attribute-controlled siren that supports tone selection and sounds until
# turned off. Reuse the IAS WD siren's unique_id so existing entities migrate.
.prevent_default_entity_creation(endpoint_id=1, cluster_id=IasWd.cluster_id)
.siren(
CustomHeimanCluster.AttributeDefs.siren_for_automation.name,
CustomHeimanCluster.cluster_id,
available_tones={
SmokeCoSirenEnum.Smoke_siren: "Smoke siren",
SmokeCoSirenEnum.CO_siren: "CO siren",
},
off_value=SmokeCoSirenEnum.Stop,
default_tone=SmokeCoSirenEnum.CO_siren,
unique_id_suffix=str(IasWd.cluster_id),
translation_key="siren",
fallback_name="Siren",
)
# XXX: siren_for_automation should be added as a siren entity, needs zigpy API
.binary_sensor(
CustomHeimanCluster.AttributeDefs.sensor_self_check_state.name,
CustomHeimanCluster.cluster_id,
Expand Down
24 changes: 16 additions & 8 deletions zhaquirks/heiman/hs1sa_e_lover.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Heiman HS1SA-E Lover smoke sensor."""

from zha.quirks import SIREN_BASIC
import zigpy.types as t
from zigpy.zcl.clusters.security import IasWd, IasZone
from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef
Expand Down Expand Up @@ -52,14 +51,23 @@ class AttributeDefs(BaseAttributeDefs):
.applies_to("HEIMAN", "SmokeSensor-EF2-3.0")
.friendly_name(manufacturer="HEIMAN", model="HS1SA-E-Lover")
.replaces(CustomHeimanCluster)
.exposes_feature(SIREN_BASIC)
.change_entity_metadata(
endpoint_id=1,
cluster_id=IasWd.cluster_id,
new_primary=False,
new_entity_category=EntityType.CONFIG,
# Replace the IAS WD siren (fixed tone, ZHA-limited to ~30 s) with an
# attribute-controlled siren that supports tone selection and sounds until
# turned off. Reuse the IAS WD siren's unique_id so existing entities migrate.
.prevent_default_entity_creation(endpoint_id=1, cluster_id=IasWd.cluster_id)
.siren(
CustomHeimanCluster.AttributeDefs.siren_for_automation.name,
CustomHeimanCluster.cluster_id,
available_tones={
SmokeSirenEnum.Smoke_siren: "Smoke siren",
SmokeSirenEnum.CO_siren: "CO siren",
},
off_value=SmokeSirenEnum.Stop,
default_tone=SmokeSirenEnum.Smoke_siren,
unique_id_suffix=str(IasWd.cluster_id),
translation_key="siren",
fallback_name="Siren",
)
# XXX: siren_for_automation should be added as a siren entity, needs zigpy API
.command_button(
IasZone.ServerCommandDefs.init_test_mode.name,
IasZone.cluster_id,
Expand Down
24 changes: 16 additions & 8 deletions zhaquirks/heiman/hs1sa_efa.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Heiman HS1SA-E smoke sensor."""

from zha.quirks import SIREN_BASIC
import zigpy.types as t
from zigpy.zcl.clusters.security import IasWd, IasZone
from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef
Expand Down Expand Up @@ -125,20 +124,29 @@ class AttributeDefs(BaseAttributeDefs):
.applies_to("HEIMAN", "HS1SA-E-PLUS")
.friendly_name(manufacturer="HEIMAN", model="HS1SA-E-PLUS") # Used by newer fw
.replaces(CustomHeimanCluster)
.exposes_feature(SIREN_BASIC)
.change_entity_metadata(
endpoint_id=1,
cluster_id=IasWd.cluster_id,
new_primary=False,
new_entity_category=EntityType.CONFIG,
# Replace the IAS WD siren (fixed tone, ZHA-limited to ~30 s) with an
# attribute-controlled siren that supports tone selection and sounds until
# turned off. Reuse the IAS WD siren's unique_id so existing entities migrate.
.prevent_default_entity_creation(endpoint_id=1, cluster_id=IasWd.cluster_id)
.siren(
CustomHeimanCluster.AttributeDefs.siren_for_automation.name,
CustomHeimanCluster.cluster_id,
available_tones={
SmokeSirenEnum.Smoke_siren: "Smoke siren",
SmokeSirenEnum.CO_siren: "CO siren",
},
off_value=SmokeSirenEnum.Stop,
default_tone=SmokeSirenEnum.Smoke_siren,
unique_id_suffix=str(IasWd.cluster_id),
translation_key="siren",
fallback_name="Siren",
)
.switch(
CustomHeimanCluster.AttributeDefs.heartbeat_indicator.name,
CustomHeimanCluster.cluster_id,
translation_key="heartbeat_indicator",
fallback_name="Heartbeat indicator",
)
# XXX: siren_for_automation should be added as a siren entity, needs zigpy API
.enum(
CustomHeimanCluster.AttributeDefs.chamber_contamination.name,
ChamberContaminationEnum,
Expand Down
Loading