Skip to content

Commit

Permalink
fix(ignore): Add back Home Assistant legacy action sensor (#25192)
Browse files Browse the repository at this point in the history
  • Loading branch information
Koenkk authored Dec 14, 2024
1 parent 5e71cf2 commit 39c007d
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 3 deletions.
18 changes: 15 additions & 3 deletions lib/extension/homeassistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ export default class HomeAssistant extends Extension {
private discoveryRegex: RegExp;
private discoveryRegexWoTopic = new RegExp(`(.*)/(.*)/(.*)/config`);
private statusTopic: string;
private legacyActionSensor: boolean;
private experimentalEventEntities: boolean;
// @ts-expect-error initialized in `start`
private zigbee2MQTTVersion: string;
Expand Down Expand Up @@ -416,6 +417,7 @@ export default class HomeAssistant extends Extension {
this.discoveryTopic = haSettings.discovery_topic;
this.discoveryRegex = new RegExp(`${haSettings.discovery_topic}/(.*)/(.*)/(.*)/config`);
this.statusTopic = haSettings.status_topic;
this.legacyActionSensor = haSettings.legacy_action_sensor;
this.experimentalEventEntities = haSettings.experimental_event_entities;
if (haSettings.discovery_topic === settings.get().mqtt.base_topic) {
throw new Error(`'homeassistant.discovery_topic' cannot not be equal to the 'mqtt.base_topic' (got '${settings.get().mqtt.base_topic}')`);
Expand Down Expand Up @@ -1079,8 +1081,9 @@ export default class HomeAssistant extends Extension {
},
});
}
// Don't expose action sensor, use MQTT device trigger instead
break;
if (!this.legacyActionSensor) {
break;
}
}

const valueTemplate = firstExpose.access & ACCESS_STATE ? `{{ value_json.${firstExpose.property} }}` : undefined;
Expand Down Expand Up @@ -1274,13 +1277,22 @@ export default class HomeAssistant extends Extension {
}
}

/**
* Publish an empty value for click and action payload, in this way Home Assistant
* can use Home Assistant entities in automations.
* https://github.com/Koenkk/zigbee2mqtt/issues/959#issuecomment-480341347
*/
if (this.legacyActionSensor && data.message.action) {
await this.publishEntityState(data.entity, {action: ''});
}

/**
* 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_device/action
*/
if (entity.isDevice() && entity.definition && 'action' in data.message) {
if (entity.isDevice() && entity.definition && data.message.action) {
const value = data.message['action'].toString();
await this.publishDeviceTriggerDiscover(entity, 'action', value);
await this.mqtt.publish(`${data.entity.name}/action`, value, {});
Expand Down
1 change: 1 addition & 0 deletions lib/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ declare global {
discovery_topic: string;
status_topic: string;
experimental_event_entities: boolean;
legacy_action_sensor: boolean;
};
availability?: {
active: {timeout: number};
Expand Down
6 changes: 6 additions & 0 deletions lib/util/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
"requiresRestart": true,
"examples": ["homeassistant/status"]
},
"legacy_action_sensor": {
"type": "boolean",
"title": "Home Assistant legacy action sensors",
"description": "Home Assistant legacy actions sensor, when enabled a action sensor will be discoverd and an empty `action` will be send after every published action.",
"default": false
},
"experimental_event_entities": {
"type": "boolean",
"title": "Home Assistant experimental event entities",
Expand Down
1 change: 1 addition & 0 deletions lib/util/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ function loadSettingsWithDefaults(): void {
const defaults = {
discovery_topic: 'homeassistant',
status_topic: 'hass/status',
legacy_action_sensor: false,
experimental_event_entities: false,
};
const s = typeof _settingsWithDefaults.homeassistant === 'object' ? _settingsWithDefaults.homeassistant : {};
Expand Down
46 changes: 46 additions & 0 deletions test/extensions/homeassistant.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,12 @@ describe('Extension: HomeAssistant', () => {
retain: true,
qos: 1,
});

// Should NOT discovery leagcy action sensor as option is not enabled.
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45520/action/config', expect.any(String), {
retain: true,
qos: 1,
});
});

it.each([
Expand Down Expand Up @@ -2458,4 +2464,44 @@ describe('Extension: HomeAssistant', () => {
qos: 1,
});
});

it('Legacy action sensor', async () => {
settings.set(['homeassistant'], {legacy_action_sensor: true});
await resetExtension();

// Should discovery action sensor
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45520/action/config', expect.any(String), {
retain: true,
qos: 1,
});

// Should counter an action payload with an empty payload
mockMQTT.publishAsync.mockClear();
const device = devices.WXKG11LM;
const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
await mockZHEvents.message(payload);
await flushPromises();
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button');
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({
action: 'single',
battery: null,
linkquality: null,
voltage: null,
power_outage_count: null,
device_temperature: null,
});
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/button');
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({
action: '',
battery: null,
linkquality: null,
voltage: null,
power_outage_count: null,
device_temperature: null,
});
expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: false});
expect(mockMQTT.publishAsync.mock.calls[2][0]).toStrictEqual('homeassistant/device_automation/0x0017880104e45520/action_single/config');
expect(mockMQTT.publishAsync.mock.calls[3][0]).toStrictEqual('zigbee2mqtt/button/action');
});
});

0 comments on commit 39c007d

Please sign in to comment.