Skip to content

Commit

Permalink
Only add event entities wen homeassistant/experimental_event_entities…
Browse files Browse the repository at this point in the history
… is true
  • Loading branch information
mundschenk-at committed Nov 2, 2024
1 parent cc0611d commit 48dabee
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 8 deletions.
11 changes: 10 additions & 1 deletion lib/extension/homeassistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ export default class HomeAssistant extends Extension {
private statusTopic: string;
private entityAttributes: boolean;
private legacyTrigger: boolean;
private experimentalEventEntities: boolean;
// @ts-expect-error initialized in `start`
private zigbee2MQTTVersion: string;
// @ts-expect-error initialized in `start`
Expand Down Expand Up @@ -472,6 +473,7 @@ export default class HomeAssistant extends Extension {
this.statusTopic = haSettings.status_topic;
this.entityAttributes = haSettings.legacy_entity_attributes;
this.legacyTrigger = haSettings.legacy_triggers;
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 @@ -1156,7 +1158,12 @@ export default class HomeAssistant extends Extension {
* If enum attribute does not have SET access and is named 'action', then expose
* as EVENT entity. Wildcard actions like `recall_*` are currently not supported.
*/
if (firstExpose.access & ACCESS_STATE && !(firstExpose.access & ACCESS_SET) && firstExpose.property == 'action') {
if (
this.experimentalEventEntities &&
firstExpose.access & ACCESS_STATE &&
!(firstExpose.access & ACCESS_SET) &&
firstExpose.property == 'action'
) {
discoveryEntries.push({
type: 'event',
object_id: firstExpose.property,
Expand Down Expand Up @@ -2214,11 +2221,13 @@ export default class HomeAssistant extends Extension {
private parseActionValue(action: string): ActionData {
const buttons = action.match(ACTION_BUTTON_PATTERN);
if (buttons?.groups?.action) {
//console.log('Recognized button actions', buttons.groups);
return {...buttons.groups, action: buttons.groups.action};
}

const scenes = action.match(ACTION_SCENE_PATTERN);
if (scenes?.groups?.action) {
//console.log('Recognized scene actions', scenes.groups);
return {...scenes.groups, action: scenes.groups.action};
}

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 @@ -118,6 +118,7 @@ declare global {
status_topic: string;
legacy_entity_attributes: boolean;
legacy_triggers: boolean;
experimental_event_entities: boolean;
};
permit_join: boolean;
availability?: {
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 @@ -40,6 +40,12 @@
"description": "Home Assistant status topic",
"requiresRestart": true,
"examples": ["homeassistant/status"]
},
"experimental_event_entities": {
"type": "boolean",
"title": "Home Assistant experimental event entities",
"description": "Home Assistant experimental event entities, when enabled Zigbee2MQTT will add event entities for exposed actions. The events and attributes are currently deemed experimental and subject to change.",
"default": false
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion lib/util/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,13 @@ function loadSettingsWithDefaults(): void {
}

if (_settingsWithDefaults.homeassistant) {
const defaults = {discovery_topic: 'homeassistant', status_topic: 'hass/status', legacy_entity_attributes: true, legacy_triggers: true};
const defaults = {
discovery_topic: 'homeassistant',
status_topic: 'hass/status',
legacy_entity_attributes: true,
legacy_triggers: true,
experimental_event_entities: false,
};
const sLegacy = {};
if (_settingsWithDefaults.advanced) {
for (const key of [
Expand Down
52 changes: 46 additions & 6 deletions test/homeassistant.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ describe('HomeAssistant extension', () => {
});

it('Should discover devices and groups', async () => {
settings.set(['homeassistant'], {experimental_event_entities: true});
await resetExtension();

let payload;

payload = {
Expand Down Expand Up @@ -1719,10 +1722,9 @@ describe('HomeAssistant extension', () => {

it('Should discover trigger when click is published', async () => {
const discovered = MQTT.publish.mock.calls.filter((c) => c[0].includes('0x0017880104e45520')).map((c) => c[0]);
expect(discovered.length).toBe(8);
expect(discovered.length).toBe(7);
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/click/config');
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/action/config');
expect(discovered).toContain('homeassistant/event/0x0017880104e45520/action/config');

MQTT.publish.mockClear();

Expand Down Expand Up @@ -1903,9 +1905,8 @@ describe('HomeAssistant extension', () => {
await resetExtension();

const discovered = MQTT.publish.mock.calls.filter((c) => c[0].includes('0x0017880104e45520')).map((c) => c[0]);
expect(discovered.length).toBe(7);
expect(discovered.length).toBe(6);
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/action/config');
expect(discovered).toContain('homeassistant/event/0x0017880104e45520/action/config');
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/battery/config');
expect(discovered).toContain('homeassistant/sensor/0x0017880104e45520/linkquality/config');
});
Expand All @@ -1915,10 +1916,9 @@ describe('HomeAssistant extension', () => {
await resetExtension();

const discovered = MQTT.publish.mock.calls.filter((c) => c[0].includes('0x0017880104e45520')).map((c) => c[0]);
expect(discovered.length).toBe(6);
expect(discovered.length).toBe(5);
expect(discovered).not.toContain('homeassistant/sensor/0x0017880104e45520/click/config');
expect(discovered).not.toContain('homeassistant/sensor/0x0017880104e45520/action/config');
expect(discovered).toContain('homeassistant/event/0x0017880104e45520/action/config');

MQTT.publish.mockClear();

Expand Down Expand Up @@ -1964,6 +1964,46 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(3);
});

it('Should enable experimental event entities', async () => {
settings.set(['homeassistant'], {experimental_event_entities: true});
settings.set(['devices', '0x0017880104e45520'], {
legacy: false,
friendly_name: 'button',
retain: false,
});
await resetExtension();

const payload = {
availability: [{topic: 'zigbee2mqtt/bridge/state'}],
device: {
identifiers: ['zigbee2mqtt_0x0017880104e45520'],
manufacturer: 'Aqara',
model: 'Wireless mini switch (WXKG11LM)',
name: 'button',
sw_version: null,
via_device: 'zigbee2mqtt_bridge_0x00124b00120144ae',
},
event_types: ['single', 'double', 'triple', 'quadruple', 'hold', 'release'],
icon: 'mdi:gesture-double-tap',
json_attributes_topic: 'zigbee2mqtt/button',
name: 'Action',
object_id: 'button_action',
origin: origin,
state_topic: 'zigbee2mqtt/button',
unique_id: '0x0017880104e45520_action_zigbee2mqtt',
// Needs to be updated whenever one of the ACTION_*_PATTERN constants changes.
value_template:
'{%- set buttons = value_json.action|regex_findall_index(^(?P<button>[a-z]+)_(?P<action>(?:press|hold)(?:_release)?)$) -%}{%- set scenes = value_json.action|regex_findall_index(^(?P<action>recall|scene)_(?P<scene>[0-2][0-9]{0,2})$) -%}{%- set regions = value_json.action|regex_findall_index(^region_(?P<region>[1-9]|10)_(?P<action>enter|leave|occupied|unoccupied)$) -%}{%- if buttons -%}\n {%- set d = dict(event_type = "{{buttons[1]}}", button = "{{buttons[0]}}_button" -%}\n{%- elif scenes -%}\n {%- set d = dict(event_type = "{{scenes[0]}}", scene = "{{scenes[1]}}" -%}\n{%- elif regions -%}\n {%- set d = dict(event_type = "region_{{regions[1]}}", region = "{{regions[0]}}" -%}\n{%- else -%}\n {%- set d = dict(event_type = "{{value_json.action}}" ) -%}\n{%- endif -%}\n{{d|to_json}}',
};

expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/event/0x0017880104e45520/action/config',
stringify(payload),
{retain: true, qos: 1},
expect.any(Function),
);
});

it('Should republish payload to postfix topic with lightWithPostfix config', async () => {
MQTT.publish.mockClear();

Expand Down
1 change: 1 addition & 0 deletions test/settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,7 @@ describe('Settings', () => {
settings.reRead();
expect(settings.get().homeassistant).toStrictEqual({
discovery_topic: 'new',
experimental_event_entities: false,
legacy_entity_attributes: true,
legacy_triggers: true,
status_topic: 'olds',
Expand Down

0 comments on commit 48dabee

Please sign in to comment.