Skip to content

Add attribute-controlled siren entity for quirks v2#816

Draft
TheJulianJES wants to merge 1 commit into
devfrom
zigpy-bot/attribute-siren
Draft

Add attribute-controlled siren entity for quirks v2#816
TheJulianJES wants to merge 1 commit into
devfrom
zigpy-bot/attribute-siren

Conversation

@TheJulianJES

@TheJulianJES TheJulianJES commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

DRAFT / EXPERIMENTAL.

Companion to quirks PR zigpy/zha-device-handlers#5143 — that PR adds the .siren() builder method + metadata + discovery wiring and applies it to four Heiman smoke/CO sensors. This PR adds the entity primitive those quirks rely on.

Summary

Some devices sound a siren by writing an attribute (a tone value) rather than issuing IAS WD start_warning commands. Unlike start_warning, this keeps sounding until it is turned off (or the device resets the attribute itself), and it can carry a tone selection — neither of which the existing IAS WD sirens can express.

This adds AttributeSiren, a generic siren controlled by writing an enum attribute:

  • On → writes the requested tone (or the configured default_tone) to the attribute.
  • Off → writes off_value.
  • State is derived from the cached attribute value, so a device that resets the attribute on its own (e.g. a firmware timeout) keeps the entity in sync without any user action.
  • Advertises TONES when available_tones is provided.

It is only instantiated from quirks v2 metadata (no @register_entity, no cluster match) — the same pattern as the existing ConfigurableAttributeSwitch / NumberConfigurationEntity / EnumSensor / ZCLEnumSelectEntity classes.

Changes
  • AttributeSiren(BaseSiren) in zha/application/platforms/siren.py — attribute-write siren described above.
  • EntityPlatform.SIREN added (zha/application/__init__.py) so quirks v2 metadata can target the siren platform.
  • async_squawk moved from BaseSirenBaseZclSiren. Squawk is an IAS WD command; it does not apply to an attribute siren. No callers exist outside the IAS WD sirens (checked zha + HA Core), so this is a safe refactor and lets AttributeSiren subclass BaseSiren without an inapplicable abstract method.
  • test_siren.py: test_attribute_siren builds a quirk device end-to-end (builder → metadata → discovery → entity) and checks: the IAS WD siren is suppressed and replaced with the AttributeSiren reusing its unique_id; TURN_ON | TURN_OFF | TONES; tone selection writes the right value; and a device-driven report back to the off value flips the state off (the auto-reset case).

Relationship to #667 / #668

This would likely replace the two earlier draft attempts at the same capability:

Both need the same EntityPlatform.SIREN addition included here. The design difference: #816 introduces a dedicated SirenMetadata + AttributeSiren and a .siren() builder method, whereas #667/#668 deliberately reuse the existing switch/enum metadata + entity plumbing. #668's write-up explicitly preferred reuse ("creating separate SirenMetadata would confuse things more… reusing existing SwitchMetadata and ZCLEnumMetadata works well enough"), so this is a genuine design call to settle before any of the three leaves draft — the tradeoff is a purpose-built API/entity (explicit tones + off value, no unused switch "inverter" logic) vs. less new surface area. If #816 is preferred, #667 and #668 can be closed.

Open question — should this live in ZHA or in quirks?

AttributeSiren is quirk-only, so an alternative is to define the entity class inside zha-device-handlers (subclassing BaseSiren) and keep only EntityPlatform.SIREN here in ZHA.

Argument for keeping it in ZHA (current form): it's exactly analogous to ConfigurableAttributeSwitch, NumberConfigurationEntity, EnumSensor and ZCLEnumSelectEntity — all of which are register_entity=False / no _cluster_match (i.e. never created by ZHA's own discovery, only by quirks metadata) yet live in ZHA. It's also generic (any "write a value to sound an alarm / pick a tone" device can use it), and the live-entity test harness only exists in ZHA's test suite. Flagging for a maintainer call before this leaves draft.

Translations / strings

  • Tone values are not translated by HA (verified in core siren/strings.json and the frontend ha-more-info-siren-advanced-controls.ts, which renders the available_tones dict values raw). So the quirk supplies human-readable tone names ("Smoke siren" / "CO siren"), consistent with the existing AdvancedSiren tones ("Burglar", "Fire", …). Using a slug like co_siren would render the literal co_siren in the UI. If translated tone labels are wanted, the alternative is a select entity (its options are slug-translated) instead of a siren — a different UX than requested here.
  • Entity name: the quirk uses translation_key="siren". HA's zha strings.json has no entity.siren platform section yet, so this would be the first siren-platform quirk key and will need adding on the next ZHA→HA strings bump (auto-added with "Siren" as the English name, per the usual process).

Required HA Core change (siren tones)

HA Core's ZHASiren currently hardcodes the tone list to the IAS WD warning modes and ignores the entity's available_tones, so quirk-defined tones would not surface. The small change below makes it read available_tones from the entity (and de-duplicates — the hardcoded list just mirrored AdvancedSiren's tones). It is not pushed yet; included here for review.

HA Core diff (homeassistant/components/zha/siren.py)
diff --git a/homeassistant/components/zha/siren.py b/homeassistant/components/zha/siren.py
index b2ac0469af8..559804eb06a 100644
--- a/homeassistant/components/zha/siren.py
+++ b/homeassistant/components/zha/siren.py
@@ -5,7 +5,6 @@ from typing import Any, override
 
 from zha.application.platforms.siren import (
     SirenEntityFeature as ZHASirenEntityFeature,
-    WarningMode,
 )
 
 from homeassistant.components.siren import (
@@ -53,19 +52,16 @@ async def async_setup_entry(
 class ZHASiren(ZHAEntity, SirenEntity):
     """Representation of a ZHA siren."""
 
-    _attr_available_tones: list[int | str] | dict[int, str] | None = {
-        WarningMode.Burglar: "Burglar",
-        WarningMode.Fire: "Fire",
-        WarningMode.Emergency: "Emergency",
-        WarningMode.Police_Panic: "Police Panic",
-        WarningMode.Fire_Panic: "Fire Panic",
-        WarningMode.Emergency_Panic: "Emergency Panic",
-    }
-
     def __init__(self, entity_data: EntityData, **kwargs: Any) -> None:
         """Initialize the ZHA siren."""
         super().__init__(entity_data, **kwargs)
 
+        # Tones are defined by the underlying ZHA entity (e.g. the IAS WD warning
+        # modes, or quirk-defined tones), not hardcoded here.
+        self._attr_available_tones: list[int | str] | dict[int, str] | None = (
+            self.entity_data.entity.available_tones or None
+        )
+
         features: SirenEntityFeature = SirenEntityFeature(0)
         zha_features: ZHASirenEntityFeature = self.entity_data.entity.supported_features

Release ordering

Siren on/off + state work with just this PR + the quirks PR. Tone selection in the UI additionally needs the HA Core change above. The quirks PR depends on this one (imports AttributeSiren / EntityPlatform.SIREN), so it can't merge/release first.

Add AttributeSiren, a siren entity that turns on/off by writing an enum
attribute (e.g. a manufacturer-specific tone attribute) rather than issuing
IAS WD start_warning commands. Its state is derived from the cached attribute
value, so a device that resets the attribute on its own keeps the entity in
sync. Only created from quirks v2 metadata; no default cluster match.

Add the SIREN entity platform and move async_squawk from BaseSiren to
BaseZclSiren, since it is specific to IAS WD sirens.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants