diff --git a/lib/extension/publish.ts b/lib/extension/publish.ts index b53ad96be9..ac61412610 100644 --- a/lib/extension/publish.ts +++ b/lib/extension/publish.ts @@ -10,7 +10,13 @@ import Group from '../model/group'; import Device from '../model/device'; import bind from 'bind-decorator'; -const topicGetSetRegex = new RegExp(`^(.+?)/(get|set)(?:/(.+))?`); +let topicGetSetRegex: RegExp; +// Used by `publish.test.js` to reload regex when changing `mqtt.base_topic`. +export const loadTopicGetSetRegex = (): void => { + topicGetSetRegex = new RegExp(`^${settings.get().mqtt.base_topic}/(?!bridge)(.+?)/(get|set)(?:/(.+))?$`); +}; +loadTopicGetSetRegex(); + const stateValues = ['on', 'off', 'toggle', 'open', 'close', 'stop', 'lock', 'unlock']; const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all', 'scene_rename']; @@ -45,12 +51,6 @@ export default class Publish extends Extension { // - /device_name/endpoint/set (attribute is defined in the payload) // - /device_name/endpoint/set/attribute (payload is the value) - // The first step is to get rid of base topic part - topic = topic.replace(`${settings.get().mqtt.base_topic}/`, ''); - - // Also bridge requests are something we don't care about - if (topic.match(/bridge/)) return null; - // Make the rough split on get/set keyword. // Before the get/set is the device name and optional endpoint name. // After it there will be an optional attribute name. diff --git a/test/publish.test.js b/test/publish.test.js index d41820358b..2691bc0469 100644 --- a/test/publish.test.js +++ b/test/publish.test.js @@ -2,6 +2,7 @@ const data = require('./stub/data'); const sleep = require('./stub/sleep'); const logger = require('./stub/logger'); const zigbeeHerdsman = require('./stub/zigbeeHerdsman'); +const {loadTopicGetSetRegex} = require('../lib/extension/publish'); const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters'); const stringify = require('json-stable-stringify-without-jsonify'); const MQTT = require('./stub/mqtt'); @@ -40,6 +41,7 @@ describe('Publish', () => { data.writeDefaultConfiguration(); controller.state.state = {}; settings.reRead(); + loadTopicGetSetRegex(); mocksClear.forEach((m) => m.mockClear()); Object.values(zigbeeHerdsman.devices).forEach((d) => { d.endpoints.forEach((e) => { @@ -594,6 +596,7 @@ describe('Publish', () => { it('Should parse topic with when base topic has multiple slashes', async () => { settings.set(['mqtt', 'base_topic'], 'zigbee2mqtt/at/my/home'); + loadTopicGetSetRegex(); const device = zigbeeHerdsman.devices.bulb_color; const endpoint = device.getEndpoint(1); await MQTT.events.message('zigbee2mqtt/at/my/home/bulb_color/get', stringify({state: ''})); @@ -614,6 +617,7 @@ describe('Publish', () => { it('Should parse topic with when base and deviceID have multiple slashes', async () => { settings.set(['mqtt', 'base_topic'], 'zigbee2mqtt/at/my/basement'); + loadTopicGetSetRegex(); const device = zigbeeHerdsman.devices.bulb_color; settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'floor0/basement/my_device_id2'); const endpoint = device.getEndpoint(1); @@ -712,6 +716,7 @@ describe('Publish', () => { it('Should parse set with and slashes in base and deviceID postfix topic', async () => { settings.set(['mqtt', 'base_topic'], 'zigbee2mqtt/at/my/home') + loadTopicGetSetRegex(); const device = zigbeeHerdsman.devices.QBKG03LM; settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'in/basement/wall_switch_double'); const endpoint = device.getEndpoint(2); @@ -1536,4 +1541,10 @@ describe('Publish', () => { {retain: false, qos: 0}, expect.any(Function) ); }); + + it('Log an error when entity is not found', async () => { + await MQTT.events.message('zigbee2mqtt/an_unknown_entity/set', stringify({})); + await flushPromises(); + expect(logger.error).toHaveBeenCalledWith("Entity 'an_unknown_entity' is unknown"); + }); });