This commit is contained in:
Koen Kanters
2020-04-19 18:10:53 +02:00
parent d00a1fed30
commit 11fd5ffde6
3 changed files with 42 additions and 44 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ const objectAssignDeep = require('object-assign-deep');
const utils = require('./util/utils');
// Extensions
const ExtensionEntityPublish = require('./extension/entityPublish');
const ExtensionPublish = require('./extension/publish');
const ExtensionReceive = require('./extension/receive');
const ExtensionNetworkMap = require('./extension/networkMap');
const ExtensionSoftReset = require('./extension/legacy/softReset');
@@ -36,7 +36,7 @@ class Controller {
// Initialize extensions.
const args = [this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus];
this.extensions = [
new ExtensionEntityPublish(...args),
new ExtensionPublish(...args),
new ExtensionReceive(...args),
new ExtensionDeviceGroupMembership(...args),
new ExtensionConfigure(...args),
@@ -6,8 +6,7 @@ const utils = require('../util/utils');
const assert = require('assert');
const Extension = require('./extension');
const postfixes = utils.getEndpointNames();
const topicRegex = new RegExp(`^(.+?)(?:/(${postfixes.join('|')}))?/(get|set)(?:/(.+))?`);
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.getEndpointNames().join('|')}))?/(get|set)(?:/(.+))?`);
const groupConverters = [
zigbeeHerdsmanConverters.toZigbeeConverters.light_onoff_brightness,
@@ -46,7 +45,7 @@ class EntityPublish extends Extension {
return null;
}
return {ID: ID, postfix: match[2] || '', type: match[3], attribute: match[4]};
return {ID: ID, endpointName: match[2] || '', type: match[3], attribute: match[4]};
}
async onMQTTMessage(topic, message) {
@@ -55,20 +54,20 @@ class EntityPublish extends Extension {
return false;
}
const entityName = `${topic.ID}` + (topic.postfix ? `/${topic.postfix}` : '');
const entity = this.zigbee.resolveEntity(entityName);
const entityKey = `${topic.ID}` + (topic.endpointName ? `/${topic.endpointName}` : '');
const resolvedEntity = this.zigbee.resolveEntity(entityKey);
if (!entity) {
if (!resolvedEntity) {
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const message = {friendly_name: entityName};
const message = {friendly_name: entityKey};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `entity_not_found`, message}),
);
}
logger.error(`Entity '${entityName}' is unknown`);
logger.error(`Entity '${entityKey}' is unknown`);
return;
}
@@ -77,27 +76,26 @@ class EntityPublish extends Extension {
let target = null;
let options = {};
let device = null;
let mapped = null;
let definition = null;
assert(entity.type === 'device' || entity.type === 'group');
if (entity.type === 'device') {
// Map device to a model
if (!entity.definition) {
logger.warn(`Device with modelID '${entity.device.modelID}' is not supported.`);
assert(resolvedEntity.type === 'device' || resolvedEntity.type === 'group');
if (resolvedEntity.type === 'device') {
if (!resolvedEntity.definition) {
logger.warn(`Device with modelID '${resolvedEntity.device.modelID}' is not supported.`);
logger.warn(`Please see: https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html`);
return;
}
device = entity.device;
mapped = entity.definition;
target = entity.endpoint;
converters = entity.definition.toZigbee;
options = entity.settings;
device = resolvedEntity.device;
definition = resolvedEntity.definition;
target = resolvedEntity.endpoint;
converters = resolvedEntity.definition.toZigbee;
options = resolvedEntity.settings;
} else {
converters = groupConverters;
target = entity.group;
options = entity.settings;
mapped = entity.group.members.map((e) => zigbeeHerdsmanConverters.findByDevice(e.getDevice()));
target = resolvedEntity.group;
options = resolvedEntity.settings;
definition = resolvedEntity.group.members.map((e) => zigbeeHerdsmanConverters.findByDevice(e.getDevice()));
}
// Convert the MQTT message to a Zigbee message.
@@ -113,7 +111,7 @@ class EntityPublish extends Extension {
}
}
const deviceState = this.state.get(entity.settings.ID) || {};
const deviceState = this.state.get(resolvedEntity.settings.ID) || {};
const isOn = deviceState && deviceState.state === 'ON' ? true : false;
/**
@@ -148,18 +146,18 @@ class EntityPublish extends Extension {
// For each attribute call the corresponding converter
const usedConverters = [];
for (let [key, value] of entries) {
let postfix = topic.postfix;
let endpointName = topic.endpointName;
let actualTarget = target;
// When the key has a postfix included (e.g. state_right), this will override the target.
if (entity.type === 'device' && key.includes('_')) {
// When the key has a endpointName included (e.g. state_right), this will override the target.
if (resolvedEntity.type === 'device' && key.includes('_')) {
const underscoreIndex = key.lastIndexOf('_');
const possiblePostfix = key.substring(underscoreIndex + 1, key.length);
if (utils.getEndpointNames().includes(possiblePostfix)) {
postfix = possiblePostfix;
const possibleEndpointName = key.substring(underscoreIndex + 1, key.length);
if (utils.getEndpointNames().includes(possibleEndpointName)) {
endpointName = possibleEndpointName;
key = key.substring(0, underscoreIndex);
const device = target.getDevice();
actualTarget = device.getEndpoint(mapped.endpoint(device)[postfix]);
actualTarget = device.getEndpoint(definition.endpoint(device)[endpointName]);
}
}
@@ -177,32 +175,32 @@ class EntityPublish extends Extension {
// Converter didn't return a result, skip
const meta = {
endpoint_name: postfix,
endpoint_name: endpointName,
options,
message: json,
logger,
device,
state: deviceState,
mapped,
mapped: definition,
};
try {
if (topic.type === 'set' && converter.convertSet) {
logger.debug(`Publishing '${topic.type}' '${key}' to '${entity.name}'`);
logger.debug(`Publishing '${topic.type}' '${key}' to '${resolvedEntity.name}'`);
const result = await converter.convertSet(actualTarget, key, value, meta);
if (result && result.state) {
const msg = result.state;
if (postfix) {
if (endpointName) {
for (const key of ['state', 'brightness']) {
if (msg.hasOwnProperty(key)) {
msg[`${key}_${postfix}`] = msg[key];
msg[`${key}_${endpointName}`] = msg[key];
delete msg[key];
}
}
}
this.publishEntityState(entity.settings.ID, msg);
this.publishEntityState(resolvedEntity.settings.ID, msg);
}
// It's possible for devices to get out of sync when writing an attribute that's not reportable.
@@ -213,13 +211,13 @@ class EntityPublish extends Extension {
// ever issue a read here, as we assume the device will properly report changes.
// Only do this when the retrieve_state option is enabled for this device.
if (
entity.type === 'device' && result && result.hasOwnProperty('readAfterWriteTime') &&
entity.settings.retrieve_state
resolvedEntity.type === 'device' && result && result.hasOwnProperty('readAfterWriteTime') &&
resolvedEntity.settings.retrieve_state
) {
setTimeout(() => converter.convertGet(actualTarget, key, meta), result.readAfterWriteTime);
}
} else if (topic.type === 'get' && converter.convertGet) {
logger.debug(`Publishing get '${topic.type}' '${key}' to '${entity.name}'`);
logger.debug(`Publishing get '${topic.type}' '${key}' to '${resolvedEntity.name}'`);
await converter.convertGet(actualTarget, key, meta);
} else {
logger.error(`No converter available for '${topic.type}' '${key}' (${json[key]})`);
@@ -227,13 +225,13 @@ class EntityPublish extends Extension {
}
} catch (error) {
const message =
`Publish '${topic.type}' '${key}' to '${entity.name}' failed: '${error}'`;
`Publish '${topic.type}' '${key}' to '${resolvedEntity.name}' failed: '${error}'`;
logger.error(message);
logger.debug(error.stack);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {friendly_name: entity.name};
const meta = {friendly_name: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `zigbee_publish_error`, message, meta}),
@@ -21,7 +21,7 @@ const expectNothingPublished = () => {
})
}
describe('Entity publish', () => {
describe('Publish', () => {
let controller;
beforeAll(async () => {