From 4210edf60e90bbd17afc50f6d241d6df174c15bf Mon Sep 17 00:00:00 2001 From: Gernot Messow Date: Wed, 3 Jul 2024 21:27:45 +0200 Subject: [PATCH] fix: Allow definition to override HA discovery payload (#23075) * Added function to override payloads for non-conforming devices * override implementation for Bosch BTH-RA * unit test including mockup for this device * Experimental and broken integration of overrideHaConfig * Not working experiments; mqtt publish debug output * fixes * Updates * fix --------- Co-authored-by: Koen Kanters --- lib/extension/homeassistant.ts | 4 +++ test/homeassistant.test.js | 48 +++++++++++++++++++++++++++++++--- test/stub/data.js | 3 +++ test/stub/zigbeeHerdsman.js | 42 +++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index be0d3a815..87f279170 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -1700,6 +1700,10 @@ export default class HomeAssistant extends Extension { } } + if (entity.isDevice()) { + entity.definition.meta?.overrideHaDiscoveryPayload?.(payload); + } + const topic = this.getDiscoveryTopic(config, entity); const payloadStr = stringify(payload); newDiscoveredTopics.add(topic); diff --git a/test/homeassistant.test.js b/test/homeassistant.test.js index 63f903ccd..ad7b70587 100644 --- a/test/homeassistant.test.js +++ b/test/homeassistant.test.js @@ -854,9 +854,7 @@ describe('HomeAssistant extension', () => { }); it('Should discover thermostat devices', async () => { - let payload; - - payload = { + const payload = { action_template: "{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}", action_topic: 'zigbee2mqtt/TS0601_thermostat', @@ -905,6 +903,50 @@ describe('HomeAssistant extension', () => { ); }); + it('Should discover Bosch BTH-RA with a compatibility mapping', async () => { + const payload = { + action_template: + "{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}", + action_topic: 'zigbee2mqtt/bosch_radiator', + availability: [{topic: 'zigbee2mqtt/bridge/state'}], + current_temperature_template: '{{ value_json.local_temperature }}', + current_temperature_topic: 'zigbee2mqtt/bosch_radiator', + device: { + identifiers: ['zigbee2mqtt_0x18fc2600000d7ae2'], + manufacturer: 'Bosch', + model: 'Radiator thermostat II (BTH-RA)', + name: 'bosch_radiator', + sw_version: '3.05.09', + via_device: 'zigbee2mqtt_bridge_0x00124b00120144ae', + }, + json_attributes_topic: 'zigbee2mqtt/bosch_radiator', + max_temp: '30', + min_temp: '5', + mode_command_template: `{% set values = { 'auto':'schedule','heat':'manual','off':'pause'} %}{\\"operating_mode\\": \\"{{ values[value] if value in values.keys() else 'pause' }}\\"}`, + mode_command_topic: 'zigbee2mqtt/bosch_radiator/set', + mode_state_template: + "{% set values = {'schedule':'auto','manual':'heat','pause':'off'} %}{% set value = value_json.operating_mode %}{{ values[value] if value in values.keys() else 'off' }}", + mode_state_topic: 'zigbee2mqtt/bosch_radiator', + modes: ['off', 'heat', 'auto'], + name: null, + object_id: 'bosch_radiator', + origin: origin, + temp_step: 0.5, + temperature_command_topic: 'zigbee2mqtt/bosch_radiator/set/occupied_heating_setpoint', + temperature_state_template: '{{ value_json.occupied_heating_setpoint }}', + temperature_state_topic: 'zigbee2mqtt/bosch_radiator', + temperature_unit: 'C', + unique_id: '0x18fc2600000d7ae2_climate_zigbee2mqtt', + }; + + expect(MQTT.publish).toHaveBeenCalledWith( + 'homeassistant/climate/0x18fc2600000d7ae2/climate/config', + stringify(payload), + {qos: 1, retain: true}, + expect.any(Function), + ); + }); + it('Should discover devices with cover_position', async () => { let payload; diff --git a/test/stub/data.js b/test/stub/data.js index a2435532a..66a9f80e4 100644 --- a/test/stub/data.js +++ b/test/stub/data.js @@ -19,6 +19,9 @@ function writeDefaultConfiguration() { port: '/dev/dummy', }, devices: { + '0x18fc2600000d7ae2': { + friendly_name: 'bosch_radiator', + }, '0x000b57fffec6a5b2': { retain: true, friendly_name: 'bulb', diff --git a/test/stub/zigbeeHerdsman.js b/test/stub/zigbeeHerdsman.js index 3f7ccba12..fc65ba205 100644 --- a/test/stub/zigbeeHerdsman.js +++ b/test/stub/zigbeeHerdsman.js @@ -48,6 +48,33 @@ const custom_clusters = { }, }; +const customClusterBTHRA = { + custom_1: { + ID: 513, + attributes: { + attribute_0: {ID: 16391, type: 48, manufacturerCode: 4617}, + attribute_1: {ID: 16416, type: 48, manufacturerCode: 4617}, + attribute_2: {ID: 16418, type: 48, manufacturerCode: 4617}, + attribute_3: {ID: 16448, type: 41, manufacturerCode: 4617}, + attribute_4: {ID: 16450, type: 48, manufacturerCode: 4617}, + attribute_5: {ID: 16451, type: 48, manufacturerCode: 4617}, + }, + commands: {}, + commandsResponse: {}, + }, + custom_2: { + ID: 516, + attributes: { + attribute_0: {ID: 16395, type: 32, manufacturerCode: 4617}, + attribute_1: {ID: 16441, type: 48, manufacturerCode: 4617}, + attribute_2: {ID: 16442, type: 48, manufacturerCode: 4617}, + attribute_3: {ID: 16443, type: 48, manufacturerCode: 4617}, + }, + commands: {}, + commandsResponse: {}, + }, +}; + class Endpoint { constructor( ID, @@ -365,6 +392,21 @@ const devices = { 'Mains (single phase)', 'TRADFRI bulb E27 WS opal 980lm', ), + 'RBSH-TRV0-ZB-EU': new Device( + 'EndDevice', + '0x18fc2600000d7ae2', + 35902, + 4617, // 0x1209, + [new Endpoint(1, [0, 1, 3, 4, 32, 513, 516, 2821], [10, 25], '0x18fc2600000d7ae2')], + true, + 'Battery', + 'RBSH-TRV0-ZB-EU', + false, + 'BOSCH', + '20231122', + '3.05.09', + customClusterBTHRA, + ), bulb_color: bulb_color, bulb_2: bulb_2, bulb_color_2: bulb_color_2,