diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index 8ea62786d..c2f651ff9 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -781,7 +781,7 @@ export default class HomeAssistant extends Extension { const motorState = allExposes ?.filter(isEnumExpose) .find((e) => ['motor_state', 'moving'].includes(e.name) && e.access === ACCESS_STATE); - const running = allExposes?.find((e) => e.type === 'binary' && e.name === 'running'); + const running = allExposes?.filter(isBinaryExpose)?.find((e) => e.name === 'running'); const discoveryEntry: DiscoveryEntry = { type: 'cover', @@ -801,8 +801,8 @@ export default class HomeAssistant extends Extension { if (running) { assert(position, `Cover must have 'position' when it has 'running'`); discoveryEntry.discovery_payload.value_template = - `{% if "${running.property}" in value_json ` + - `and value_json.${running.property} %} {% if value_json.${position.property} > 0 %} closing ` + + `{% if "${featurePropertyWithoutEndpoint(running)}" in value_json ` + + `and value_json.${featurePropertyWithoutEndpoint(running)} %} {% if value_json.${featurePropertyWithoutEndpoint(position)} > 0 %} closing ` + `{% else %} opening {% endif %} {% else %} stopped {% endif %}`; } @@ -819,8 +819,8 @@ export default class HomeAssistant extends Extension { discoveryEntry.discovery_payload.state_closing = closingState; discoveryEntry.discovery_payload.state_stopped = stoppedState; discoveryEntry.discovery_payload.value_template = - `{% if "${motorState.property}" in value_json ` + - `and value_json.${motorState.property} %} {{ value_json.${motorState.property} }} {% else %} ` + + `{% if "${featurePropertyWithoutEndpoint(motorState)}" in value_json ` + + `and value_json.${featurePropertyWithoutEndpoint(motorState)} %} {{ value_json.${featurePropertyWithoutEndpoint(motorState)} }} {% else %} ` + `${stoppedState} {% endif %}`; } } diff --git a/test/extensions/homeassistant.test.ts b/test/extensions/homeassistant.test.ts index e7c361ea1..4bead6333 100644 --- a/test/extensions/homeassistant.test.ts +++ b/test/extensions/homeassistant.test.ts @@ -1007,6 +1007,76 @@ describe('Extension: HomeAssistant', () => { }); }); + it('Should discover dual cover devices', async () => { + const payload_left = { + availability: [ + { + topic: 'zigbee2mqtt/bridge/state', + value_template: '{{ value_json.state }}', + }, + ], + command_topic: 'zigbee2mqtt/0xa4c138018cf95021/left/set', + device: { + identifiers: ['zigbee2mqtt_0xa4c138018cf95021'], + manufacturer: 'Lonsonho', + model: 'Dual curtain/blind module (TS130F_dual)', + name: '0xa4c138018cf95021', + via_device: 'zigbee2mqtt_bridge_0x00124b00120144ae', + }, + name: 'Left', + object_id: '0xa4c138018cf95021_left', + origin: origin, + position_template: '{{ value_json.position }}', + position_topic: 'zigbee2mqtt/0xa4c138018cf95021/left', + set_position_template: '{ "position_left": {{ position }} }', + set_position_topic: 'zigbee2mqtt/0xa4c138018cf95021/left/set', + state_closing: 'DOWN', + 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 %}', + }; + const payload_right = { + availability: [ + { + topic: 'zigbee2mqtt/bridge/state', + value_template: '{{ value_json.state }}', + }, + ], + command_topic: 'zigbee2mqtt/0xa4c138018cf95021/right/set', + device: { + identifiers: ['zigbee2mqtt_0xa4c138018cf95021'], + manufacturer: 'Lonsonho', + model: 'Dual curtain/blind module (TS130F_dual)', + name: '0xa4c138018cf95021', + via_device: 'zigbee2mqtt_bridge_0x00124b00120144ae', + }, + name: 'Right', + object_id: '0xa4c138018cf95021_right', + origin: origin, + position_template: '{{ value_json.position }}', + position_topic: 'zigbee2mqtt/0xa4c138018cf95021/right', + set_position_template: '{ "position_right": {{ position }} }', + set_position_topic: 'zigbee2mqtt/0xa4c138018cf95021/right/set', + state_closing: 'DOWN', + 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 %}', + }; + + expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/cover/0xa4c138018cf95021/cover_left/config', stringify(payload_left), { + retain: true, + qos: 1, + }); + expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/cover/0xa4c138018cf95021/cover_right/config', stringify(payload_right), { + retain: true, + qos: 1, + }); + }); + it('Should discover devices with custom homeassistant.discovery_topic', async () => { settings.set(['homeassistant'], {discovery_topic: 'my_custom_discovery_topic'}); await resetExtension(); diff --git a/test/mocks/zigbeeHerdsman.ts b/test/mocks/zigbeeHerdsman.ts index f96c2a43f..48caa4fb7 100644 --- a/test/mocks/zigbeeHerdsman.ts +++ b/test/mocks/zigbeeHerdsman.ts @@ -983,6 +983,21 @@ export const devices = { 'Mains (single phase)', 'J1 (5502)', ), + TS130F_DUAL_COVER_SWITCH: new Device( + 'Router', + '0xa4c138018cf95021', + 46163, + 4417, + [ + new Endpoint(1, [0, 4, 5, 6, 258, 57345], [10, 25], '0xa4c138018cf95021'), + new Endpoint(2, [0, 4, 5, 6, 258, 57345], [10, 25], '0xa4c138018cf95021'), + ], + true, + 'Mains (single phase)', + 'TS130F', + false, + '_TZ3000_j1xl73iw', + ), TS0601_thermostat: TS0601_thermostat, TS0601_switch: TS0601_switch, TS0601_cover_switch: TS0601_cover_switch,