mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-07-17 00:51:55 +00:00
Implement lightWithPostfix Home Assistant configuration. #3003
This commit is contained in:
@@ -174,8 +174,12 @@ class EntityPublish extends BaseExtension {
|
||||
const msg = result.state;
|
||||
|
||||
if (postfix) {
|
||||
msg[`state_${postfix}`] = msg.state;
|
||||
delete msg.state;
|
||||
for (const key of ['state', 'brightness']) {
|
||||
if (msg.hasOwnProperty(key)) {
|
||||
msg[`${key}_${postfix}`] = msg[key];
|
||||
delete msg[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.publishEntityState(entity.settings.ID, msg);
|
||||
|
||||
@@ -3,6 +3,7 @@ const settings = require('../util/settings');
|
||||
const logger = require('../util/logger');
|
||||
const zigbee2mqttVersion = require('../../package.json').version;
|
||||
const BaseExtension = require('./baseExtension');
|
||||
const objectAssignDeep = require(`object-assign-deep`);
|
||||
|
||||
const cfg = {
|
||||
// Binary sensor
|
||||
@@ -539,25 +540,12 @@ const switchWithPostfix = (postfix) => {
|
||||
};
|
||||
};
|
||||
|
||||
const lightWithPostfix = (postfix) => {
|
||||
return {
|
||||
type: 'light',
|
||||
object_id: 'light',
|
||||
discovery_payload: {
|
||||
schema: 'template',
|
||||
command_topic: true,
|
||||
command_topic_prefix: postfix,
|
||||
command_on_template: `
|
||||
{"state": "on"
|
||||
{%- if brightness is defined -%}
|
||||
, "brightness": {{ brightness }}
|
||||
{%- endif -%}
|
||||
}`,
|
||||
command_off_template: '{"state": "off"}',
|
||||
state_template: `{{ value_json.state_${postfix} }}`,
|
||||
brightness_template: `{{ value_json.brightness_${postfix} }}`,
|
||||
},
|
||||
};
|
||||
const lightWithPostfix = (configType, postfix) => {
|
||||
const config = objectAssignDeep.noMutate(cfg[configType]);
|
||||
config['object_id'] = `light_${postfix}`;
|
||||
config['discovery_payload']['command_topic_prefix'] = postfix;
|
||||
config['discovery_payload']['state_topic_postfix'] = postfix;
|
||||
return config;
|
||||
};
|
||||
|
||||
// Map homeassitant configurations to devices.
|
||||
@@ -1303,7 +1291,7 @@ const mapping = {
|
||||
'GL-D-003ZS': [cfg.light_brightness_colortemp_colorxy],
|
||||
'66492-001': [cfg.lock, cfg.sensor_battery],
|
||||
'798.09': [cfg.light_brightness_colortemp_colorxy],
|
||||
'U202DST600ZB': [lightWithPostfix('l1'), lightWithPostfix('l2')],
|
||||
'U202DST600ZB': [lightWithPostfix('light_brightness', 'l1'), lightWithPostfix('light_brightness', 'l2')],
|
||||
'SM10ZW': [cfg.binary_sensor_contact, cfg.sensor_battery],
|
||||
'ICZB-R11D': [cfg.light_brightness],
|
||||
'SW2500ZB': [cfg.switch],
|
||||
@@ -1364,9 +1352,43 @@ class HomeAssistant extends BaseExtension {
|
||||
}
|
||||
|
||||
async onPublishEntityState(data) {
|
||||
const key = ['action', 'click'].find((k) => data.payload.hasOwnProperty(k) && data.payload[k] !== '');
|
||||
/**
|
||||
* In case we deal with a lightWithPostfix configuration Zigbee2mqtt publishes
|
||||
* e.g. {state_l1: ON, brightness_l1: 250} to zigbee2mqtt/mydevice.
|
||||
* As the Home Assistant MQTT JSON light cannot be configured to use state_l1/brightness_l1
|
||||
* as the state variables, the state topic is set to zigbee2mqtt/mydevice/l1.
|
||||
* Here we retrieve all the attributes with the _l1 values and republish them on
|
||||
* zigbee2mqtt/mydevice/l1.
|
||||
*/
|
||||
if (data.entity.mapped && mapping[data.entity.mapped.model]) {
|
||||
for (const config of mapping[data.entity.mapped.model]) {
|
||||
const match = /light_(.*)/.exec(config['object_id']);
|
||||
if (match) {
|
||||
const postfix = match[1];
|
||||
const posfixRegExp = new RegExp(`(.*)_${postfix}`);
|
||||
const payload = {};
|
||||
for (const key of Object.keys(data.payload)) {
|
||||
const keyMatch = posfixRegExp.exec(key);
|
||||
if (keyMatch) {
|
||||
payload[keyMatch[1]] = data.payload[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (key && data.entity.type === 'device') {
|
||||
await this.mqtt.publish(
|
||||
`${data.entity.name}/${postfix}`, JSON.stringify(payload), {},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the MQTT device trigger (https://www.home-assistant.io/integrations/device_trigger.mqtt/)
|
||||
* The MQTT device trigger does not support JSON parsing, so it cannot listen to zigbee2mqtt/my_device
|
||||
* Whenever a device publish an {action: *} we discover an MQTT device trigger sensor
|
||||
* and republish it to zigbee2mqtt/my_devic/action
|
||||
*/
|
||||
const key = ['action', 'click'].find((k) => data.payload.hasOwnProperty(k) && data.payload[k] !== '');
|
||||
if (data.entity.mapped && key) {
|
||||
const device = data.entity.device;
|
||||
if (!this.discoveredTriggers[device.ieeeAddr]) {
|
||||
this.discoveredTriggers[device.ieeeAddr] = new Set();
|
||||
@@ -1379,16 +1401,15 @@ class HomeAssistant extends BaseExtension {
|
||||
const config = cfg[`trigger_${key}`];
|
||||
config.object_id = `${key}_${value}`;
|
||||
const topic = this.getDiscoveryTopic(config, device);
|
||||
const mappedModel = zigbeeHerdsmanConverters.findByZigbeeModel(device.modelID);
|
||||
const payload = {
|
||||
...config.discovery_payload,
|
||||
subtype: value,
|
||||
payload: value,
|
||||
topic: `${settings.get().mqtt.base_topic}/${data.entity.name}/${key}`,
|
||||
device: this.getDevicePayload(data.entity.settings, mappedModel),
|
||||
device: this.getDevicePayload(data.entity.settings, data.entity.mapped),
|
||||
};
|
||||
|
||||
this.mqtt.publish(topic, JSON.stringify(payload), {retain: true, qos: 0}, this.discoveryTopic);
|
||||
await this.mqtt.publish(topic, JSON.stringify(payload), {retain: true, qos: 0}, this.discoveryTopic);
|
||||
this.discoveredTriggers[device.ieeeAddr].add(discoveredKey);
|
||||
}
|
||||
|
||||
@@ -1443,7 +1464,11 @@ class HomeAssistant extends BaseExtension {
|
||||
this.getConfigs(mappedModel).forEach((config) => {
|
||||
const topic = this.getDiscoveryTopic(config, device);
|
||||
const payload = {...config.discovery_payload};
|
||||
const stateTopic = `${settings.get().mqtt.base_topic}/${entity.friendlyName}`;
|
||||
let stateTopic = `${settings.get().mqtt.base_topic}/${entity.friendlyName}`;
|
||||
if (payload.state_topic_postfix) {
|
||||
stateTopic += `/${payload.state_topic_postfix}`;
|
||||
delete payload.state_topic_postfix;
|
||||
}
|
||||
|
||||
if (!payload.hasOwnProperty('state_topic') || payload.state_topic) {
|
||||
payload.state_topic = stateTopic;
|
||||
|
||||
@@ -893,4 +893,29 @@ describe('HomeAssistant extension', () => {
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('Should republish payload to postfix topic with lightWithPostfix config', async () => {
|
||||
controller = new Controller(false);
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
MQTT.publish.mockClear();
|
||||
|
||||
await MQTT.events.message('zigbee2mqtt/U202DST600ZB/l2/set', JSON.stringify({state: 'ON', brightness: 20}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB', JSON.stringify({state_l2:"ON", brightness_l2:20}), {"qos": 0, "retain": false}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB/l2', JSON.stringify({state:"ON", brightness:20}), {"qos": 0, "retain": false}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB/l1', JSON.stringify({}), {"qos": 0, "retain": false}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Shouldnt crash in onPublishEntityState on group publish', async () => {
|
||||
controller = new Controller(false);
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
logger.error.mockClear();
|
||||
MQTT.publish.mockClear();
|
||||
|
||||
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(logger.error).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
+4
-1
@@ -147,7 +147,10 @@ function writeDefaultConfiguration() {
|
||||
},
|
||||
'0x0017880104e45526': {
|
||||
friendly_name: 'GL-S-007ZS',
|
||||
}
|
||||
},
|
||||
'0x0017880104e43559': {
|
||||
friendly_name: 'U202DST600ZB'
|
||||
},
|
||||
},
|
||||
groups: {
|
||||
'1': {
|
||||
|
||||
@@ -144,6 +144,7 @@ const devices = {
|
||||
'SP600_NEW': new Device('Router', '0x90fd9ffffe4b64ab', 33901, 4476, [new Endpoint(1, [0,4,3,5,10,258,13,19,6,1,1030,8,768,1027,1029,1026], [0,3,4,6,8,5], '0x90fd9ffffe4b64aa', [], {seMetering: {"multiplier":1,"divisor":10000}})], true, "Mains (single phase)", "SP600", false, 'Salus', '20170220'),
|
||||
'MKS-CM-W5': new Device('Router', '0x90fd9ffffe4b64ac', 33901, 4476, [new Endpoint(1, [0,4,3,5,10,258,13,19,6,1,1030,8,768,1027,1029,1026], [0,3,4,6,8,5], '0x90fd9ffffe4b64aa', [], {})], true, "Mains (single phase)", "qnazj70", false),
|
||||
'GL-S-007ZS': new Device('Router', '0x0017880104e45526', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45526')], true, "Mains (single phase)", 'GL-S-007ZS'),
|
||||
'U202DST600ZB': new Device('Router', '0x0017880104e43559', 6540,4151, [new Endpoint(10, [0, 6], [], '0x0017880104e43559'), new Endpoint(11, [0, 6], [], '0x0017880104e43559')], true, "Mains (single phase)", 'U202DST600ZB'),
|
||||
}
|
||||
|
||||
const groups = {
|
||||
|
||||
Reference in New Issue
Block a user