This commit is contained in:
Koen Kanters
2020-04-05 00:05:05 +02:00
parent e0076367d3
commit 3ef7555ca2
10 changed files with 68 additions and 53 deletions
+6 -6
View File
@@ -109,8 +109,8 @@ class Controller {
logger.info(
(entity.settings ? entity.settings.friendlyName : entity.device.ieeeAddr) +
` (${entity.device.ieeeAddr}): ` +
(entity.mapped ?
`${entity.mapped.model} - ${entity.mapped.vendor} ${entity.mapped.description} ` :
(entity.definition ?
`${entity.definition.model} - ${entity.definition.vendor} ${entity.definition.description} ` :
'Not supported ') +
`(${entity.device.type})`,
);
@@ -192,8 +192,8 @@ class Controller {
if (data.status === 'successful') {
logger.info(`Successfully interviewed '${name}', device has successfully been paired`);
if (entity.mapped) {
const {vendor, description, model} = entity.mapped;
if (entity.definition) {
const {vendor, description, model} = entity.definition;
logger.info(
`Device '${name}' is supported, identified as: ${vendor} ${description} (${model})`,
);
@@ -223,7 +223,7 @@ class Controller {
// Call extensions
this.callExtensionMethod(
'onZigbeeEvent',
[type, data, entity ? entity.mapped : null, entity ? entity.settings : null],
[type, data, entity ? entity.definition : null, entity ? entity.settings : null],
);
}
@@ -278,7 +278,7 @@ class Controller {
messagePayload.device = {
friendlyName: entity.name,
model: entity.mapped ? entity.mapped.model : 'unknown',
model: entity.definition ? entity.definition.model : 'unknown',
};
attributes.forEach((a) => messagePayload.device[a] = device[a]);
+2 -2
View File
@@ -380,8 +380,8 @@ class BridgeLegacy extends BaseExtension {
this.mqtt.log('device_connected', {friendly_name: name});
} else if (type === 'deviceInterview') {
if (data.status === 'successful') {
if (entity.mapped) {
const {vendor, description, model} = entity.mapped;
if (entity.definition) {
const {vendor, description, model} = entity.definition;
const log = {friendly_name: name, model, vendor, description, supported: true};
this.mqtt.log('pairing', 'interview_successful', log);
} else {
+5 -5
View File
@@ -6,7 +6,7 @@ const utils = require('../util/utils');
const assert = require('assert');
const BaseExtension = require('./baseExtension');
const postfixes = utils.getPostfixes();
const postfixes = utils.getEndpointNames();
const topicRegex = new RegExp(`^(.+?)(?:/(${postfixes.join('|')}))?/(get|set)(?:/(.+))?`);
const groupConverters = [
@@ -74,16 +74,16 @@ class EntityPublish extends BaseExtension {
assert(entity.type === 'device' || entity.type === 'group');
if (entity.type === 'device') {
// Map device to a model
if (!entity.mapped) {
if (!entity.definition) {
logger.warn(`Device with modelID '${entity.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.mapped;
mapped = entity.definition;
target = entity.endpoint;
converters = entity.mapped.toZigbee;
converters = entity.definition.toZigbee;
options = entity.settings;
} else {
converters = groupConverters;
@@ -136,7 +136,7 @@ class EntityPublish extends BaseExtension {
if (entity.type === 'device' && key.includes('_')) {
const underscoreIndex = key.lastIndexOf('_');
const possiblePostfix = key.substring(underscoreIndex + 1, key.length);
if (utils.getPostfixes().includes(possiblePostfix)) {
if (utils.getEndpointNames().includes(possiblePostfix)) {
postfix = possiblePostfix;
key = key.substring(0, underscoreIndex);
const device = target.getDevice();
+11 -4
View File
@@ -1,6 +1,8 @@
const settings = require('../util/settings');
const logger = require('../util/logger');
const BaseExtension = require('./baseExtension');
const utils = require('../util/utils');
const postfixes = utils.getEndpointNames();
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/(.+)/(remove|add|remove_all)$`);
const topicRegexRemoveAll = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/remove_all$`);
@@ -167,12 +169,17 @@ class Groups extends BaseExtension {
`${entity.name}/${entity.endpoint.ID}`,
];
if (entity.endpointName) {
keys.push(`${entity.device.ieeeAddr}/${entity.endpointName}`);
keys.push(`${entity.name}/${entity.endpointName}`);
const definition = entity.definition;
const endpoints = definition && definition.endpoint ? definition.endpoint(entity.device) : null;
const endpointName = endpoints ? Object.entries(endpoints).find((e) => e[1] === entity.endpoint.ID)[0] : null;
if (endpointName) {
keys.push(`${entity.device.ieeeAddr}/${endpointName}`);
keys.push(`${entity.name}/${endpointName}`);
}
if (entity.isDefaultEndpoint) {
const hasEndpointName = postfixes.find((p) => message.endsWith(`/${p}`));
if (!hasEndpointName) {
keys.push(entity.name);
keys.push(entity.device.ieeeAddr);
}
+7 -7
View File
@@ -1551,8 +1551,8 @@ class HomeAssistant extends BaseExtension {
* 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]) {
if (data.entity.definition && mapping[data.entity.definition.model]) {
for (const config of mapping[data.entity.definition.model]) {
const match = /light_(.*)/.exec(config['object_id']);
if (match) {
const postfix = match[1];
@@ -1579,7 +1579,7 @@ class HomeAssistant extends BaseExtension {
* 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) {
if (data.entity.definition && key) {
const device = data.entity.device;
if (!this.discoveredTriggers[device.ieeeAddr]) {
this.discoveredTriggers[device.ieeeAddr] = new Set();
@@ -1597,7 +1597,7 @@ class HomeAssistant extends BaseExtension {
subtype: value,
payload: value,
topic: `${settings.get().mqtt.base_topic}/${data.entity.name}/${key}`,
device: this.getDevicePayload(data.entity.settings, data.entity.mapped),
device: this.getDevicePayload(data.entity.settings, data.entity.definition),
};
await this.mqtt.publish(topic, JSON.stringify(payload), {retain: true, qos: 0}, this.discoveryTopic);
@@ -1614,13 +1614,13 @@ class HomeAssistant extends BaseExtension {
const mockedValues = [
{
property: 'update_available',
condition: data.entity.device && data.entity.mapped && data.entity.mapped.hasOwnProperty('ota'),
condition: data.entity.device && data.entity.definition && data.entity.definition.hasOwnProperty('ota'),
value: false,
},
{
property: 'water_leak',
condition: data.entity.device && data.entity.mapped &&
mapping[data.entity.mapped.model].includes(cfg.binary_sensor_water_leak),
condition: data.entity.device && data.entity.definition &&
mapping[data.entity.definition.model].includes(cfg.binary_sensor_water_leak),
value: false,
},
];
+3 -3
View File
@@ -71,7 +71,7 @@ class OTAUpdate extends BaseExtension {
const device = this.zigbee.resolveEntity(message);
assert(device != null && device.type === 'device', 'Device not found or not a device');
if (!device.mapped || !device.mapped.ota) {
if (!device.definition || !device.definition.ota) {
const message = `Device '${device.name}' does not support OTA updates`;
logger.error(message);
this.mqtt.log('ota_update', message, {status: `not_supported`, device: device.name});
@@ -90,7 +90,7 @@ class OTAUpdate extends BaseExtension {
logger.info(message);
this.mqtt.log('ota_update', message, {status: `checking_if_available`, device: device.name});
try {
const available = await device.mapped.ota.isUpdateAvailable(device.device, logger);
const available = await device.definition.ota.isUpdateAvailable(device.device, logger);
const message=(available ?
`Update available for '${device.name}'` : `No update available for '${device.name}'`);
logger.info(message);
@@ -119,7 +119,7 @@ class OTAUpdate extends BaseExtension {
};
const from_ = await this.readSoftwareBuildIDAndDateCode(device.device, false);
await device.mapped.ota.updateToLatest(device.device, logger, onProgress);
await device.definition.ota.updateToLatest(device.device, logger, onProgress);
const to = await this.readSoftwareBuildIDAndDateCode(device.device, true);
const [fromS, toS] = [JSON.stringify(from_), JSON.stringify(to)];
const message = `Finished update of '${device.name}'` + (to ? `, from '${fromS}' to '${toS}'` : ``);
+1 -1
View File
@@ -310,7 +310,7 @@ function write() {
function validate() {
const validate = ajv.compile(schema);
const valid = validate(_settings);
const postfixes = utils.getPostfixes();
const postfixes = utils.getEndpointNames();
// Verify that all friendly names are unique
const names = [];
+2 -2
View File
@@ -27,7 +27,7 @@ function toLocalISOString(dDate) {
':' + pad(tzOffset % 60);
}
const postfixes = [
const endpointNames = [
'left', 'right', 'center', 'bottom_left', 'bottom_right', 'default',
'top_left', 'top_right', 'white', 'rgb', 'system', 'top', 'bottom', 'center_left', 'center_right',
'ep1', 'ep2', 'row_1', 'row_2', 'row_3', 'row_4', 'relay',
@@ -125,7 +125,7 @@ module.exports = {
getZigbee2mqttVersion,
objectHasProperties,
getObjectsProperty,
getPostfixes: () => postfixes,
getEndpointNames: () => endpointNames,
isXiaomiDevice: (device) => {
return device.modelID !== 'lumi.router' && xiaomiManufacturerID.includes(device.manufacturerID) &&
(!device.manufacturerName || !device.manufacturerName.startsWith('Trust'));
+30 -22
View File
@@ -8,7 +8,7 @@ const events = require('events');
const objectAssignDeep = require('object-assign-deep');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const postfixes = utils.getPostfixes();
const endpointNames = utils.getEndpointNames();
const keyEndpointByNumber = new RegExp(`.*/([0-9]*)$`);
const herdsmanSettings = {
@@ -131,6 +131,17 @@ class Zigbee extends events.EventEmitter {
return this.herdsman.getDevicesByType(type);
}
/**
* @param {string} key
* @return {object} {
* type: device | coordinator
* device|group: zigbee-herdsman entity
* endpoint: selected endpoint
* settings: from configuration.yaml
* name: name of the entity
* definition: zigbee-herdsman-converters definition
* }
*/
resolveEntity(key) {
assert(
typeof key === 'string' || typeof key === 'number' ||
@@ -153,13 +164,13 @@ class Zigbee extends events.EventEmitter {
};
}
let postfix = postfixes.find((p) => key.endsWith(`/${p}`));
const postfixByNumber = key.match(keyEndpointByNumber);
if (!postfix && postfixByNumber) {
postfix = Number(postfixByNumber[1]);
let endpointKey = endpointNames.find((p) => key.endsWith(`/${p}`));
const endpointByNumber = key.match(keyEndpointByNumber);
if (!endpointKey && endpointByNumber) {
endpointKey = Number(endpointByNumber[1]);
}
if (postfix) {
key = key.replace(`/${postfix}`, '');
if (endpointKey) {
key = key.replace(`/${endpointKey}`, '');
}
const entity = settings.getEntity(key);
@@ -171,19 +182,17 @@ class Zigbee extends events.EventEmitter {
return null;
}
const mapped = zigbeeHerdsmanConverters.findByZigbeeModel(device.modelID);
const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
let isDefaultEndpoint = true;
const definition = zigbeeHerdsmanConverters.findByZigbeeModel(device.modelID);
const endpoints = definition && definition.endpoint ? definition.endpoint(device) : null;
let endpoint;
if (postfix) {
isDefaultEndpoint = false;
if (postfixByNumber) {
endpoint = device.getEndpoint(postfix);
if (endpointKey) {
if (endpointByNumber) {
endpoint = device.getEndpoint(endpointKey);
} else {
assert(mapped != null, `Postfix '${postfix}' is given but device is unsupported`);
assert(endpoints != null, `Postfix '${postfix}' is given but device defines no endpoints`);
const endpointID = endpoints[postfix];
assert(endpointID, `Postfix '${postfix}' is given but device has no such endpoint`);
assert(definition != null, `Endpoint name '${endpointKey}' is given but device is unsupported`);
assert(endpoints != null, `Endpoint name '${endpointKey}' is given but no endpoints defined`);
const endpointID = endpoints[endpointKey];
assert(endpointID, `Endpoint name '${endpointKey}' is given but device has no such endpoint`);
endpoint = device.getEndpoint(endpointID);
}
} else if (endpoints && endpoints['default']) {
@@ -192,10 +201,8 @@ class Zigbee extends events.EventEmitter {
endpoint = device.endpoints[0];
}
const endpointName = endpoints ? Object.entries(endpoints).find((e) => e[1] === endpoint.ID)[0] : null;
return {
type: 'device', device, settings: entity, mapped, endpoint, name: entity.friendlyName,
isDefaultEndpoint, endpointName,
type: 'device', device, endpoint, settings: entity, name: entity.friendlyName, definition,
};
} else {
let group = this.getGroupByID(entity.ID);
@@ -207,9 +214,10 @@ class Zigbee extends events.EventEmitter {
return {
type: 'device',
device: key,
endpoint: key.endpoints[0],
settings: setting,
mapped: zigbeeHerdsmanConverters.findByZigbeeModel(key.modelID),
name: setting ? setting.friendlyName : (key.type === 'Coordinator' ? 'Coordinator' : key.ieeeAddr),
definition: zigbeeHerdsmanConverters.findByZigbeeModel(key.modelID),
};
}
}
+1 -1
View File
@@ -595,7 +595,7 @@ describe('Settings', () => {
expect(() => {
settings.validate();
}).toThrowError(`Following friendly_name are not allowed: '${utils.getPostfixes()}'`);
}).toThrowError(`Following friendly_name are not allowed: '${utils.getEndpointNames()}'`);
});
it('Configuration shouldnt be valid when duplicate friendly_name are used', async () => {