From 174ba64449b04777d1d5743fbb7b498a29c40ae2 Mon Sep 17 00:00:00 2001 From: David Dreschner <34408442+DerDreschner@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:30:17 +0200 Subject: [PATCH] fix: Home Assistant: make PI heating demand from writable (#29188) --- lib/extension/homeassistant.ts | 24 ++++++--- test/extensions/homeassistant.test.ts | 71 +++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index 41cab4205..34da7e66a 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -706,20 +706,32 @@ export class HomeAssistant extends Extension { const piHeatingDemand = (firstExpose as zhc.Climate).features.filter(isNumericExpose).find((f) => f.name === "pi_heating_demand"); if (piHeatingDemand) { - const discoveryEntry: DiscoveryEntry = { - type: "sensor", - object_id: endpoint ? /* v8 ignore next */ `${piHeatingDemand.name}_${endpoint}` : `${piHeatingDemand.name}`, + const discoveryEntry: Partial = { + object_id: endpoint ? `${piHeatingDemand.name}_${endpoint}` : `${piHeatingDemand.name}`, mockProperties: [{property: piHeatingDemand.property, value: null}], discovery_payload: { - name: endpoint ? /* v8 ignore next */ `${piHeatingDemand.label} ${endpoint}` : piHeatingDemand.label, + name: endpoint ? `${piHeatingDemand.label} ${endpoint}` : piHeatingDemand.label, value_template: `{{ value_json.${piHeatingDemand.property} }}`, ...(piHeatingDemand.unit && {unit_of_measurement: piHeatingDemand.unit}), - entity_category: "diagnostic", icon: "mdi:radiator", }, }; - discoveryEntries.push(discoveryEntry); + assert(discoveryEntry.discovery_payload); + + if (piHeatingDemand.access & ACCESS_SET) { + discoveryEntry.type = "number"; + discoveryEntry.discovery_payload.command_topic = true; + discoveryEntry.discovery_payload.command_topic_prefix = endpoint; + discoveryEntry.discovery_payload.command_topic_postfix = piHeatingDemand.property; + discoveryEntry.discovery_payload.min = piHeatingDemand.value_min; + discoveryEntry.discovery_payload.max = piHeatingDemand.value_max; + } else { + discoveryEntry.type = "sensor"; + discoveryEntry.discovery_payload.entity_category = "diagnostic"; + } + + discoveryEntries.push(discoveryEntry); } const piCoolingDemand = (firstExpose as zhc.Climate).features.filter(isNumericExpose).find((f) => f.name === "pi_cooling_demand"); diff --git a/test/extensions/homeassistant.test.ts b/test/extensions/homeassistant.test.ts index d19096bd3..1e688a54a 100644 --- a/test/extensions/homeassistant.test.ts +++ b/test/extensions/homeassistant.test.ts @@ -1027,6 +1027,77 @@ describe("Extension: HomeAssistant", () => { }); }); + it("Should discover thermostat devices with read-only PI heating demand", () => { + const payload = { + availability: [ + { + topic: "zigbee2mqtt/bridge/state", + value_template: "{{ value_json.state }}", + }, + ], + default_entity_id: "sensor.thermostat_pi_heating_demand", + device: { + identifiers: ["zigbee2mqtt_0x0017880104e45550"], + manufacturer: "eCozy", + model: "Smart heating thermostat", + model_id: "1TST-EU", + name: "thermostat", + via_device: "zigbee2mqtt_bridge_0x00124b00120144ae", + }, + entity_category: "diagnostic", + icon: "mdi:radiator", + name: "PI heating demand", + object_id: "thermostat_pi_heating_demand", + origin: origin, + state_topic: "zigbee2mqtt/thermostat", + unique_id: "0x0017880104e45550_pi_heating_demand_zigbee2mqtt", + unit_of_measurement: "%", + value_template: "{{ value_json.pi_heating_demand }}", + }; + + expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/sensor/0x0017880104e45550/pi_heating_demand/config", stringify(payload), { + retain: true, + qos: 1, + }); + }); + + it("Should discover thermostat devices with writable PI heating demand", () => { + const payload = { + availability: [ + { + topic: "zigbee2mqtt/bridge/state", + value_template: "{{ value_json.state }}", + }, + ], + command_topic: "zigbee2mqtt/bosch_radiator/set/pi_heating_demand", + default_entity_id: "number.bosch_radiator_pi_heating_demand", + device: { + identifiers: ["zigbee2mqtt_0x18fc2600000d7ae2"], + manufacturer: "Bosch", + model: "Radiator thermostat II", + model_id: "BTH-RA", + name: "bosch_radiator", + sw_version: "3.05.09", + via_device: "zigbee2mqtt_bridge_0x00124b00120144ae", + }, + icon: "mdi:radiator", + max: 100, + min: 0, + name: "PI heating demand", + object_id: "bosch_radiator_pi_heating_demand", + origin: origin, + state_topic: "zigbee2mqtt/bosch_radiator", + unique_id: "0x18fc2600000d7ae2_pi_heating_demand_zigbee2mqtt", + unit_of_measurement: "%", + value_template: "{{ value_json.pi_heating_demand }}", + }; + + expect(mockMQTTPublishAsync).toHaveBeenCalledWith("homeassistant/number/0x18fc2600000d7ae2/pi_heating_demand/config", stringify(payload), { + retain: true, + qos: 1, + }); + }); + it("Should discover Bosch BTH-RA with a compatibility mapping", () => { const payload = { action_template: