fix: Home Assistant: expose siren entity for IAS warning devices (#31000)

This commit is contained in:
SnazzyBean
2026-02-09 20:42:03 +01:00
committed by GitHub
parent b585e31304
commit c7dae39799
2 changed files with 94 additions and 0 deletions
+55
View File
@@ -1220,6 +1220,61 @@ export class HomeAssistant extends Extension {
case "composite":
case "list": {
const firstExposeTyped = firstExpose as zhc.Text | zhc.Composite | zhc.List;
// Warning composite → HA siren entity
if (firstExposeTyped.type === "composite" && firstExposeTyped.name === "warning" && firstExposeTyped.access & ACCESS_SET) {
const warningExpose = firstExpose as zhc.Composite;
const modeFeature = warningExpose.features.filter(isEnumExpose).find((f) => f.name === "mode");
const levelFeature = warningExpose.features.filter(isEnumExpose).find((f) => f.name === "level");
const durationFeature = warningExpose.features.filter(isNumericExpose).find((f) => f.name === "duration");
const discoveryEntry: DiscoveryEntry = {
type: "siren",
object_id: endpoint ? /* v8 ignore next */ `siren_${endpoint}` : "siren",
mockProperties: [{property: warningExpose.property, value: null}],
discovery_payload: {
name: endpoint ? /* v8 ignore next */ utils.capitalize(endpoint) : null,
command_topic: true,
command_topic_prefix: endpoint,
state_topic: false,
optimistic: true,
},
};
if (modeFeature) {
const tones = modeFeature.values.filter((v) => v !== "stop");
if (tones.length) {
discoveryEntry.discovery_payload.available_tones = tones;
}
}
if (levelFeature) {
discoveryEntry.discovery_payload.support_volume_set = true;
}
if (durationFeature) {
discoveryEntry.discovery_payload.support_duration = true;
}
const levelTemplate =
"{% if volume_level is defined %}" +
"{% if volume_level | float <= 0.25 %}low" +
"{% elif volume_level | float <= 0.5 %}medium" +
"{% elif volume_level | float <= 0.75 %}high" +
"{% else %}very_high{% endif %}" +
"{% else %}medium{% endif %}";
discoveryEntry.discovery_payload.command_template =
`{"warning": {"mode": "{{ tone | default('emergency') }}", ` +
`"level": "${levelTemplate}", ` +
`"duration": {{ duration | default(10) }}}}`;
discoveryEntry.discovery_payload.command_off_template = '{"warning": {"mode": "stop"}}';
discoveryEntries.push(discoveryEntry);
break;
}
if (firstExposeTyped.type === "text" && firstExposeTyped.access & ACCESS_SET) {
discoveryEntries.push({
type: "text",
+39
View File
@@ -944,6 +944,45 @@ describe("Extension: HomeAssistant", () => {
});
});
it("Should discover siren (HS2WD-E)", () => {
const payload = {
available_tones: ["emergency"],
support_duration: true,
optimistic: true,
command_topic: "zigbee2mqtt/siren/set",
command_template:
'{"warning": {"mode": "{{ tone | default(\'emergency\') }}", ' +
'"level": "' +
"{% if volume_level is defined %}" +
"{% if volume_level | float <= 0.25 %}low" +
"{% elif volume_level | float <= 0.5 %}medium" +
"{% elif volume_level | float <= 0.75 %}high" +
"{% else %}very_high{% endif %}" +
'{% else %}medium{% endif %}", ' +
'"duration": {{ duration | default(10) }}}}',
command_off_template: '{"warning": {"mode": "stop"}}',
name: null,
object_id: "siren",
default_entity_id: "siren.siren",
unique_id: "0x0017880104e45549_siren_zigbee2mqtt",
origin: origin,
device: {
identifiers: ["zigbee2mqtt_0x0017880104e45549"],
name: "siren",
model: "Smart siren",
model_id: "HS2WD-E",
manufacturer: "Heiman",
via_device: "zigbee2mqtt_bridge_0x00124b00120144ae",
},
availability: [{topic: "zigbee2mqtt/bridge/state", value_template: "{{ value_json.state }}"}],
};
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/siren/0x0017880104e45549/siren/config", stringify(payload), {
retain: true,
qos: 1,
});
});
it("Should discover devices with speed-controlled fan", () => {
const payload = {
state_topic: "zigbee2mqtt/fanbee",