From c867ca5182d457b15ca0176b1eece83a6e677055 Mon Sep 17 00:00:00 2001 From: Michal Feix Date: Sat, 22 Aug 2026 14:46:46 +0200 Subject: [PATCH] fix: Improve Home Assistant cover state discovery --- lib/extension/homeassistant.ts | 16 +++++++++++++++- test/extensions/homeassistant.test.ts | 16 ++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index c8bfaa057..c40c9f01e 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -987,6 +987,9 @@ export class HomeAssistant extends Extension { // If curtains have `motor_state` or `moving` property, lookup for possible // state names to detect movement direction and use this in discovery. if (motorState) { + const motorStateProperty = featurePropertyWithoutEndpoint(motorState); + const stateProperty = featurePropertyWithoutEndpoint(state); + const openingState = motorState.values.find((s) => COVER_OPENING_LOOKUP.includes(s.toString().toLowerCase())); const closingState = motorState.values.find((s) => COVER_CLOSING_LOOKUP.includes(s.toString().toLowerCase())); const stoppedState = motorState.values.find((s) => COVER_STOPPED_LOOKUP.includes(s.toString().toLowerCase())); @@ -994,8 +997,19 @@ export class HomeAssistant extends Extension { if (openingState && closingState && stoppedState) { discoveryEntry.discovery_payload.state_opening = openingState; discoveryEntry.discovery_payload.state_closing = closingState; + discoveryEntry.discovery_payload.state_open = "OPEN"; + discoveryEntry.discovery_payload.state_closed = "CLOSE"; discoveryEntry.discovery_payload.state_stopped = stoppedState; - discoveryEntry.discovery_payload.value_template = `{% if "${featurePropertyWithoutEndpoint(motorState)}" in value_json and value_json["${featurePropertyWithoutEndpoint(motorState)}"] %} {{ value_json["${featurePropertyWithoutEndpoint(motorState)}"] }} {% else %} ${stoppedState} {% endif %}`; + discoveryEntry.discovery_payload.value_template = + `{% if "${motorStateProperty}" in value_json and value_json["${motorStateProperty}"] == "${openingState}" %}` + + `${openingState}` + + `{% elif "${motorStateProperty}" in value_json and value_json["${motorStateProperty}"] == "${closingState}" %}` + + `${closingState}` + + `{% elif "${stateProperty}" in value_json %}` + + `{{ value_json["${stateProperty}"] }}` + + `{% else %}` + + `${stoppedState}` + + `{% endif %}`; } } diff --git a/test/extensions/homeassistant.test.ts b/test/extensions/homeassistant.test.ts index 913a4e8b0..565fb3029 100644 --- a/test/extensions/homeassistant.test.ts +++ b/test/extensions/homeassistant.test.ts @@ -1669,12 +1669,14 @@ describe("Extension: HomeAssistant", () => { position_topic: "zigbee2mqtt/0xa4c138018cf95021/left", set_position_template: '{ "position_left": {{ position }} }', set_position_topic: "zigbee2mqtt/0xa4c138018cf95021/left/set", + state_closed: "CLOSE", state_closing: "DOWN", + state_open: "OPEN", state_opening: "UP", state_stopped: "STOP", state_topic: "zigbee2mqtt/0xa4c138018cf95021/left", unique_id: "0xa4c138018cf95021_cover_left_zigbee2mqtt", - value_template: '{% if "moving" in value_json and value_json["moving"] %} {{ value_json["moving"] }} {% else %} STOP {% endif %}', + value_template: '{% if "moving" in value_json and value_json["moving"] == "UP" %}UP{% elif "moving" in value_json and value_json["moving"] == "DOWN" %}DOWN{% elif "state" in value_json %}{{ value_json["state"] }}{% else %}STOP{% endif %}', }; const payload_right = { availability: [ @@ -1700,14 +1702,24 @@ describe("Extension: HomeAssistant", () => { position_topic: "zigbee2mqtt/0xa4c138018cf95021/right", set_position_template: '{ "position_right": {{ position }} }', set_position_topic: "zigbee2mqtt/0xa4c138018cf95021/right/set", + state_closed: "CLOSE", state_closing: "DOWN", + state_open: "OPEN", state_opening: "UP", state_stopped: "STOP", state_topic: "zigbee2mqtt/0xa4c138018cf95021/right", unique_id: "0xa4c138018cf95021_cover_right_zigbee2mqtt", - value_template: '{% if "moving" in value_json and value_json["moving"] %} {{ value_json["moving"] }} {% else %} STOP {% endif %}', + value_template: '{% if "moving" in value_json and value_json["moving"] == "UP" %}UP{% elif "moving" in value_json and value_json["moving"] == "DOWN" %}DOWN{% elif "state" in value_json %}{{ value_json["state"] }}{% else %}STOP{% endif %}', }; + const coverLeftCalls = mockMQTTPublishAsync.mock.calls.filter( + ([topic]) => topic === "homeassistant/cover/0xa4c138018cf95021/cover_left/config", + ); + + for (const [, actualPayload] of coverLeftCalls) { + console.log(JSON.parse(actualPayload)); + } + expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/cover/0xa4c138018cf95021/cover_left/config", stringify(payload_left), { retain: true, qos: 1,