2018-04-18 09:25:40 -07:00
|
|
|
const MQTT = require('./mqtt');
|
|
|
|
const Zigbee = require('./zigbee');
|
2020-01-09 13:47:19 -07:00
|
|
|
const EventBus = require('./eventBus');
|
2018-08-05 09:38:33 -07:00
|
|
|
const State = require('./state');
|
2018-04-18 09:25:40 -07:00
|
|
|
const logger = require('./util/logger');
|
2018-04-18 10:09:59 -07:00
|
|
|
const settings = require('./util/settings');
|
2018-11-16 12:23:11 -07:00
|
|
|
const objectAssignDeep = require('object-assign-deep');
|
2019-03-17 12:04:51 -07:00
|
|
|
const utils = require('./util/utils');
|
2020-04-19 11:08:24 -07:00
|
|
|
const fs = require('fs');
|
|
|
|
const data = require('./util/data');
|
|
|
|
const path = require('path');
|
2018-11-16 12:23:11 -07:00
|
|
|
|
|
|
|
// Extensions
|
2020-04-19 09:10:53 -07:00
|
|
|
const ExtensionPublish = require('./extension/publish');
|
2020-04-15 13:34:59 -07:00
|
|
|
const ExtensionReceive = require('./extension/receive');
|
2018-08-28 12:55:00 -07:00
|
|
|
const ExtensionNetworkMap = require('./extension/networkMap');
|
2020-04-11 08:44:31 -07:00
|
|
|
const ExtensionSoftReset = require('./extension/legacy/softReset');
|
2018-11-16 12:23:11 -07:00
|
|
|
const ExtensionHomeAssistant = require('./extension/homeassistant');
|
2020-04-11 11:58:22 -07:00
|
|
|
const ExtensionConfigure = require('./extension/configure');
|
2020-04-11 06:18:17 -07:00
|
|
|
const ExtensionDeviceGroupMembership = require('./extension/legacy/deviceGroupMembership');
|
|
|
|
const ExtensionBridgeLegacy = require('./extension/legacy/bridgeLegacy');
|
2020-05-24 09:16:39 -07:00
|
|
|
const ExtensionBridge = require('./extension/bridge');
|
2018-12-21 16:07:53 -07:00
|
|
|
const ExtensionGroups = require('./extension/groups');
|
2020-04-11 09:14:40 -07:00
|
|
|
const ExtensionAvailability = require('./extension/availability');
|
2020-04-15 11:36:40 -07:00
|
|
|
const ExtensionBind = require('./extension/bind');
|
2020-04-12 09:29:52 -07:00
|
|
|
const ExtensionReport = require('./extension/report');
|
2020-04-11 09:31:57 -07:00
|
|
|
const ExtensionOnEvent = require('./extension/onEvent');
|
2020-02-08 11:55:27 -07:00
|
|
|
const ExtensionOTAUpdate = require('./extension/otaUpdate');
|
2018-05-28 12:10:58 -07:00
|
|
|
|
2018-05-17 08:20:46 -07:00
|
|
|
class Controller {
|
2018-04-18 09:25:40 -07:00
|
|
|
constructor() {
|
2018-11-16 12:23:11 -07:00
|
|
|
this.zigbee = new Zigbee();
|
2018-04-18 09:25:40 -07:00
|
|
|
this.mqtt = new MQTT();
|
2020-01-09 13:47:19 -07:00
|
|
|
this.eventBus = new EventBus();
|
2020-04-05 06:48:23 -07:00
|
|
|
this.state = new State(this.eventBus);
|
2018-04-18 09:25:40 -07:00
|
|
|
|
2019-02-04 10:36:49 -07:00
|
|
|
this.publishEntityState = this.publishEntityState.bind(this);
|
2019-09-09 10:48:09 -07:00
|
|
|
this.onZigbeeAdapterDisconnected = this.onZigbeeAdapterDisconnected.bind(this);
|
2018-06-11 11:31:05 -07:00
|
|
|
|
2018-08-28 12:55:00 -07:00
|
|
|
// Initialize extensions.
|
2020-04-04 13:47:23 -07:00
|
|
|
const args = [this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus];
|
2018-08-28 12:55:00 -07:00
|
|
|
this.extensions = [
|
2020-04-19 09:10:53 -07:00
|
|
|
new ExtensionPublish(...args),
|
2020-04-15 13:34:59 -07:00
|
|
|
new ExtensionReceive(...args),
|
2020-04-04 13:47:23 -07:00
|
|
|
new ExtensionDeviceGroupMembership(...args),
|
2020-04-11 11:58:22 -07:00
|
|
|
new ExtensionConfigure(...args),
|
2020-04-04 13:47:23 -07:00
|
|
|
new ExtensionNetworkMap(...args),
|
|
|
|
new ExtensionGroups(...args),
|
2020-04-15 11:36:40 -07:00
|
|
|
new ExtensionBind(...args),
|
2020-04-11 09:31:57 -07:00
|
|
|
new ExtensionOnEvent(...args),
|
2020-04-04 13:47:23 -07:00
|
|
|
new ExtensionOTAUpdate(...args),
|
2018-08-28 12:55:00 -07:00
|
|
|
];
|
|
|
|
|
2020-05-24 09:16:39 -07:00
|
|
|
if (settings.get().experimental.new_api) {
|
|
|
|
this.extensions.push(new ExtensionBridge(...args));
|
|
|
|
}
|
|
|
|
|
2020-04-04 13:47:23 -07:00
|
|
|
if (settings.get().advanced.legacy_api) {
|
|
|
|
this.extensions.push(new ExtensionBridgeLegacy(...args));
|
|
|
|
}
|
|
|
|
|
2019-02-26 12:21:35 -07:00
|
|
|
if (settings.get().advanced.report) {
|
2020-04-12 09:29:52 -07:00
|
|
|
this.extensions.push(new ExtensionReport(...args));
|
2019-02-26 12:21:35 -07:00
|
|
|
}
|
|
|
|
|
2018-11-16 12:23:11 -07:00
|
|
|
if (settings.get().homeassistant) {
|
2020-04-04 13:47:23 -07:00
|
|
|
this.extensions.push(new ExtensionHomeAssistant(...args));
|
2018-05-16 10:29:47 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
/* istanbul ignore next */
|
2018-11-16 12:23:11 -07:00
|
|
|
if (settings.get().advanced.soft_reset_timeout !== 0) {
|
2020-04-04 13:47:23 -07:00
|
|
|
this.extensions.push(new ExtensionSoftReset(...args));
|
2018-05-28 11:40:30 -07:00
|
|
|
}
|
2018-12-29 11:55:59 -07:00
|
|
|
|
2019-01-29 12:17:56 -07:00
|
|
|
if (settings.get().advanced.availability_timeout) {
|
2020-04-11 09:14:40 -07:00
|
|
|
this.extensions.push(new ExtensionAvailability(...args));
|
2018-12-29 11:55:59 -07:00
|
|
|
}
|
2020-04-19 11:08:24 -07:00
|
|
|
|
|
|
|
const extensionPath = data.joinPath('extension');
|
|
|
|
if (fs.existsSync(extensionPath)) {
|
2020-04-22 09:52:09 -07:00
|
|
|
const extensions = fs.readdirSync(extensionPath).filter((f) => f.endsWith('.js'));
|
2020-04-19 11:08:24 -07:00
|
|
|
for (const extension of extensions) {
|
|
|
|
const Extension = require(path.join(extensionPath, extension.split('.')[0]));
|
2020-04-23 13:02:00 -07:00
|
|
|
this.extensions.push(new Extension(...args, settings, logger));
|
2020-04-19 11:08:24 -07:00
|
|
|
}
|
|
|
|
}
|
2018-05-16 10:29:47 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async start() {
|
2019-09-25 03:08:39 -07:00
|
|
|
const settingsErrors = settings.validate();
|
|
|
|
if (settingsErrors) {
|
|
|
|
logger.error(`Refusing to start, configuration.yaml is not valid, found the following errors:`);
|
|
|
|
for (const error of settingsErrors) {
|
|
|
|
logger.error(`\t - ${error}`);
|
|
|
|
}
|
|
|
|
logger.error(
|
2019-10-26 09:25:51 -07:00
|
|
|
`If you don't know how to solve this, read https://www.zigbee2mqtt.io/configuration/configuration.html`,
|
2019-09-25 03:08:39 -07:00
|
|
|
);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
this.state.start();
|
2018-09-20 12:42:50 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
const info = await utils.getZigbee2mqttVersion();
|
|
|
|
logger.info(`Starting zigbee2mqtt version ${info.version} (commit #${info.commitHash})`);
|
|
|
|
|
|
|
|
// Start zigbee
|
|
|
|
try {
|
2020-01-03 15:43:04 -07:00
|
|
|
await this.zigbee.start();
|
|
|
|
this.callExtensionMethod('onZigbeeStarted', []);
|
2019-09-09 10:48:09 -07:00
|
|
|
this.zigbee.on('event', this.onZigbeeEvent.bind(this));
|
|
|
|
this.zigbee.on('adapterDisconnected', this.onZigbeeAdapterDisconnected);
|
|
|
|
} catch (error) {
|
|
|
|
logger.error('Failed to start zigbee');
|
|
|
|
logger.error('Exiting...');
|
2019-09-10 13:07:31 -07:00
|
|
|
logger.error(error.stack);
|
2019-09-09 10:48:09 -07:00
|
|
|
process.exit(1);
|
|
|
|
}
|
2018-09-20 12:42:50 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
// Log zigbee clients on startup
|
2019-09-23 13:21:27 -07:00
|
|
|
const devices = this.zigbee.getClients();
|
2018-11-16 12:23:11 -07:00
|
|
|
logger.info(`Currently ${devices.length} devices are joined:`);
|
2019-09-09 10:48:09 -07:00
|
|
|
for (const device of devices) {
|
2019-09-23 13:21:27 -07:00
|
|
|
const entity = this.zigbee.resolveEntity(device);
|
2019-09-09 10:48:09 -07:00
|
|
|
logger.info(
|
|
|
|
(entity.settings ? entity.settings.friendlyName : entity.device.ieeeAddr) +
|
|
|
|
` (${entity.device.ieeeAddr}): ` +
|
2020-04-04 15:05:05 -07:00
|
|
|
(entity.definition ?
|
|
|
|
`${entity.definition.model} - ${entity.definition.vendor} ${entity.definition.description} ` :
|
2019-09-09 10:48:09 -07:00
|
|
|
'Not supported ') +
|
2019-10-26 09:25:51 -07:00
|
|
|
`(${entity.device.type})`,
|
2019-09-09 10:48:09 -07:00
|
|
|
);
|
|
|
|
}
|
2018-05-21 02:49:02 -07:00
|
|
|
|
2018-11-16 12:23:11 -07:00
|
|
|
// Enable zigbee join.
|
|
|
|
if (settings.get().permit_join) {
|
|
|
|
logger.warn('`permit_join` set to `true` in configuration.yaml.');
|
|
|
|
logger.warn('Allowing new devices to join.');
|
|
|
|
logger.warn('Set `permit_join` to `false` once you joined all devices.');
|
2018-09-10 09:06:29 -07:00
|
|
|
}
|
|
|
|
|
2020-01-31 14:52:39 -07:00
|
|
|
await this.zigbee.permitJoin(settings.get().permit_join);
|
2018-06-25 11:18:39 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
// MQTT
|
|
|
|
this.mqtt.on('message', this.onMQTTMessage.bind(this));
|
|
|
|
await this.mqtt.connect();
|
2018-04-23 12:44:06 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
// Send all cached states.
|
2019-09-25 04:15:30 -07:00
|
|
|
if (settings.get().advanced.cache_state) {
|
|
|
|
for (const device of this.zigbee.getClients()) {
|
|
|
|
if (this.state.exists(device.ieeeAddr)) {
|
|
|
|
this.publishEntityState(device.ieeeAddr, this.state.get(device.ieeeAddr));
|
|
|
|
}
|
2019-09-09 10:48:09 -07:00
|
|
|
}
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2020-04-04 10:15:24 -07:00
|
|
|
// Add devices which are in database but not in configuration to configuration
|
|
|
|
for (const device of this.zigbee.getClients()) {
|
|
|
|
if (!settings.getDevice(device.ieeeAddr)) {
|
|
|
|
settings.addDevice(device.ieeeAddr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-16 12:23:11 -07:00
|
|
|
// Call extensions
|
2019-09-09 10:48:09 -07:00
|
|
|
await this.callExtensionMethod('onMQTTConnected', []);
|
2018-11-16 12:23:11 -07:00
|
|
|
}
|
2018-04-20 10:53:40 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async stop() {
|
2018-11-16 12:23:11 -07:00
|
|
|
// Call extensions
|
2019-09-09 10:48:09 -07:00
|
|
|
await this.callExtensionMethod('stop', []);
|
2018-06-08 11:20:35 -07:00
|
|
|
|
2018-11-16 12:23:11 -07:00
|
|
|
// Wrap-up
|
2019-05-02 11:14:44 -07:00
|
|
|
this.state.stop();
|
2019-09-09 10:48:09 -07:00
|
|
|
await this.mqtt.disconnect();
|
|
|
|
|
|
|
|
try {
|
|
|
|
await this.zigbee.stop();
|
|
|
|
process.exit(0);
|
|
|
|
} catch (error) {
|
|
|
|
logger.error('Failed to stop zigbee');
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2018-11-16 12:23:11 -07:00
|
|
|
}
|
2018-04-18 09:25:40 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async onZigbeeAdapterDisconnected() {
|
|
|
|
logger.error('Adapter disconnected, stopping');
|
|
|
|
await this.stop();
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async onZigbeeEvent(type, data) {
|
2020-04-05 06:41:24 -07:00
|
|
|
const resolvedEntity = this.zigbee.resolveEntity(data.device || data.ieeeAddr);
|
2020-04-24 13:43:35 -07:00
|
|
|
if (data.device && !resolvedEntity.settings && data.device.type !== 'Coordinator') {
|
2019-09-09 10:48:09 -07:00
|
|
|
// Only deviceLeave doesn't have a device (not interesting to add to settings)
|
2020-04-05 06:41:24 -07:00
|
|
|
resolvedEntity.settings = settings.addDevice(data.device.ieeeAddr);
|
2018-11-16 12:23:11 -07:00
|
|
|
}
|
2018-07-24 09:25:16 -07:00
|
|
|
|
2020-04-05 06:41:24 -07:00
|
|
|
const name = resolvedEntity && resolvedEntity.settings ? resolvedEntity.settings.friendlyName : null;
|
2019-09-09 10:48:09 -07:00
|
|
|
|
|
|
|
if (type === 'message') {
|
|
|
|
logger.debug(
|
2019-10-01 11:58:08 -07:00
|
|
|
`Received Zigbee message from '${name}', type '${data.type}', cluster '${data.cluster}'` +
|
2019-10-01 11:50:05 -07:00
|
|
|
`, data '${JSON.stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
|
2019-10-26 09:25:51 -07:00
|
|
|
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``),
|
2019-09-09 10:48:09 -07:00
|
|
|
);
|
|
|
|
} else if (type === 'deviceJoined') {
|
2019-09-25 16:14:58 -07:00
|
|
|
logger.info(`Device '${name}' joined`);
|
2019-09-09 10:48:09 -07:00
|
|
|
} else if (type === 'deviceInterview') {
|
|
|
|
if (data.status === 'successful') {
|
2019-09-26 00:30:12 -07:00
|
|
|
logger.info(`Successfully interviewed '${name}', device has successfully been paired`);
|
2019-09-09 10:48:09 -07:00
|
|
|
|
2020-04-05 06:41:24 -07:00
|
|
|
if (resolvedEntity.definition) {
|
|
|
|
const {vendor, description, model} = resolvedEntity.definition;
|
|
|
|
logger.info(`Device '${name}' is supported, identified as: ${vendor} ${description} (${model})`);
|
2019-09-09 10:48:09 -07:00
|
|
|
} else {
|
|
|
|
logger.warn(
|
2019-09-25 16:14:58 -07:00
|
|
|
`Device '${name}' with Zigbee model '${data.device.modelID}' is NOT supported, ` +
|
2019-10-26 09:25:51 -07:00
|
|
|
`please follow https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html`,
|
2019-09-09 10:48:09 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
} else if (data.status === 'failed') {
|
2019-09-26 00:30:12 -07:00
|
|
|
logger.error(`Failed to interview '${name}', device has not successfully been paired`);
|
2019-09-09 10:48:09 -07:00
|
|
|
} else {
|
|
|
|
/* istanbul ignore else */
|
|
|
|
if (data.status === 'started') {
|
2019-09-25 16:14:58 -07:00
|
|
|
logger.info(`Starting interview of '${name}'`);
|
2019-09-09 10:48:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (type === 'deviceAnnounce') {
|
2019-09-25 16:14:58 -07:00
|
|
|
logger.debug(`Device '${name}' announced itself`);
|
2019-09-09 10:48:09 -07:00
|
|
|
} else {
|
|
|
|
/* istanbul ignore else */
|
|
|
|
if (type === 'deviceLeave') {
|
2019-09-25 16:14:58 -07:00
|
|
|
logger.warn(`Device '${name || data.ieeeAddr}' left the network`);
|
2019-09-09 10:48:09 -07:00
|
|
|
}
|
2018-11-16 12:23:11 -07:00
|
|
|
}
|
2018-07-24 09:25:16 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
// Call extensions
|
2020-04-05 06:41:24 -07:00
|
|
|
this.callExtensionMethod('onZigbeeEvent', [type, data, resolvedEntity]);
|
2018-11-16 12:23:11 -07:00
|
|
|
}
|
2018-07-24 09:25:16 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
onMQTTMessage(payload) {
|
|
|
|
const {topic, message} = payload;
|
|
|
|
logger.debug(`Received MQTT message on '${topic}' with data '${message}'`);
|
|
|
|
|
|
|
|
// Call extensions
|
|
|
|
this.callExtensionMethod('onMQTTMessage', [topic, message]);
|
2018-04-25 11:54:41 -07:00
|
|
|
}
|
|
|
|
|
2019-09-29 05:35:05 -07:00
|
|
|
async publishEntityState(IDorName, payload, stateChangeReason=null) {
|
2020-04-05 11:36:09 -07:00
|
|
|
const resolvedEntity = this.zigbee.resolveEntity(IDorName);
|
|
|
|
if (!resolvedEntity || !resolvedEntity.settings) {
|
2019-09-09 10:48:09 -07:00
|
|
|
logger.error(`'${IDorName}' does not exist, skipping publish`);
|
|
|
|
return;
|
|
|
|
}
|
2018-09-20 12:42:50 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
let messagePayload = {...payload};
|
2020-04-05 11:36:09 -07:00
|
|
|
const currentState = this.state.exists(resolvedEntity.settings.ID) ?
|
|
|
|
this.state.get(resolvedEntity.settings.ID) : {};
|
2019-05-02 11:14:44 -07:00
|
|
|
const newState = objectAssignDeep.noMutate(currentState, payload);
|
|
|
|
|
|
|
|
// Update state cache with new state.
|
2020-04-05 11:36:09 -07:00
|
|
|
this.state.set(resolvedEntity.settings.ID, newState, stateChangeReason);
|
2018-04-21 00:13:14 -07:00
|
|
|
|
2019-05-02 11:14:44 -07:00
|
|
|
if (settings.get().advanced.cache_state) {
|
|
|
|
// Add cached state to payload
|
|
|
|
messagePayload = newState;
|
2018-05-11 10:14:18 -07:00
|
|
|
}
|
2018-04-21 00:13:14 -07:00
|
|
|
|
2020-02-27 13:06:27 -07:00
|
|
|
const deviceOptions = settings.get().device_options;
|
2018-05-15 09:42:26 -07:00
|
|
|
const options = {
|
2020-04-05 11:36:09 -07:00
|
|
|
retain: utils.getObjectsProperty([resolvedEntity.settings, deviceOptions], 'retain', false),
|
|
|
|
qos: utils.getObjectsProperty([resolvedEntity.settings, deviceOptions], 'qos', 0),
|
2018-05-15 09:42:26 -07:00
|
|
|
};
|
|
|
|
|
2020-04-05 11:36:09 -07:00
|
|
|
const retention = utils.getObjectsProperty([resolvedEntity.settings, deviceOptions], 'retention', false);
|
2020-03-12 12:25:37 -07:00
|
|
|
if (retention !== false) {
|
|
|
|
options.properties = {messageExpiryInterval: retention};
|
|
|
|
}
|
|
|
|
|
2020-05-14 09:47:27 -07:00
|
|
|
const isDevice = resolvedEntity.type === 'device';
|
2020-04-05 11:36:09 -07:00
|
|
|
if (isDevice && settings.get().mqtt.include_device_information) {
|
2019-09-09 10:48:09 -07:00
|
|
|
const attributes = [
|
|
|
|
'ieeeAddr', 'networkAddress', 'type', 'manufacturerID', 'manufacturerName', 'powerSource',
|
|
|
|
'applicationVersion', 'stackVersion', 'zclVersion', 'hardwareVersion', 'dateCode', 'softwareBuildID',
|
|
|
|
];
|
|
|
|
|
2019-12-11 12:15:42 -07:00
|
|
|
messagePayload.device = {
|
2020-04-05 11:36:09 -07:00
|
|
|
friendlyName: resolvedEntity.name,
|
|
|
|
model: resolvedEntity.definition ? resolvedEntity.definition.model : 'unknown',
|
2019-12-11 12:15:42 -07:00
|
|
|
};
|
|
|
|
|
2020-04-05 11:36:09 -07:00
|
|
|
attributes.forEach((a) => messagePayload.device[a] = resolvedEntity.device[a]);
|
2018-09-20 12:42:50 -07:00
|
|
|
}
|
|
|
|
|
2020-05-14 09:47:27 -07:00
|
|
|
const lastSeen = settings.get().advanced.last_seen;
|
|
|
|
if (isDevice && lastSeen !== 'disable' && resolvedEntity.device.lastSeen) {
|
|
|
|
messagePayload.last_seen = utils.formatDate(resolvedEntity.device.lastSeen, lastSeen);
|
|
|
|
}
|
|
|
|
|
2020-03-02 12:08:51 -07:00
|
|
|
// filter mqtt message attributes
|
2020-03-03 10:30:54 -07:00
|
|
|
if (deviceOptions.filtered_attributes) {
|
|
|
|
deviceOptions.filtered_attributes.forEach((a) => delete messagePayload[a]);
|
|
|
|
}
|
2020-04-05 11:36:09 -07:00
|
|
|
if (resolvedEntity.settings.filtered_attributes) {
|
|
|
|
resolvedEntity.settings.filtered_attributes.forEach((a) => delete messagePayload[a]);
|
2020-03-02 12:08:51 -07:00
|
|
|
}
|
|
|
|
|
2019-06-19 09:11:35 -07:00
|
|
|
if (Object.entries(messagePayload).length) {
|
2020-01-12 07:07:06 -07:00
|
|
|
if (settings.get().experimental.output === 'attribute_and_json') {
|
2020-04-05 11:36:09 -07:00
|
|
|
await this.mqtt.publish(resolvedEntity.name, JSON.stringify(messagePayload), options);
|
|
|
|
await this.iteratePayloadAttributeOutput(`${resolvedEntity.name}/`, messagePayload, options);
|
2020-01-12 07:07:06 -07:00
|
|
|
} else if (settings.get().experimental.output === 'json') {
|
2020-04-05 11:36:09 -07:00
|
|
|
await this.mqtt.publish(resolvedEntity.name, JSON.stringify(messagePayload), options);
|
2019-09-09 10:48:09 -07:00
|
|
|
} else {
|
|
|
|
/* istanbul ignore else */
|
|
|
|
if (settings.get().experimental.output === 'attribute') {
|
2020-04-05 11:36:09 -07:00
|
|
|
await this.iteratePayloadAttributeOutput(`${resolvedEntity.name}/`, messagePayload, options);
|
2019-09-09 10:48:09 -07:00
|
|
|
}
|
2019-06-19 09:11:35 -07:00
|
|
|
}
|
2019-03-04 10:13:36 -07:00
|
|
|
}
|
2020-04-15 13:34:59 -07:00
|
|
|
|
2020-05-29 10:24:59 -07:00
|
|
|
this.eventBus.emit('publishEntityState', {payload: messagePayload, entity: resolvedEntity, stateChangeReason});
|
2018-04-21 00:13:14 -07:00
|
|
|
}
|
2018-06-15 08:48:10 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async iteratePayloadAttributeOutput(topicRoot, payload, options) {
|
|
|
|
for (const [key, value] of Object.entries(payload)) {
|
|
|
|
let subPayload = value;
|
2019-05-28 11:01:46 -07:00
|
|
|
let message;
|
|
|
|
|
|
|
|
// Special cases
|
2019-09-09 10:48:09 -07:00
|
|
|
if (key === 'color' && utils.objectHasProperties(subPayload, ['r', 'g', 'b'])) {
|
2019-05-28 11:01:46 -07:00
|
|
|
subPayload = [subPayload.r, subPayload.g, subPayload.b];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check Array first, since it is also an Object
|
|
|
|
if (Array.isArray(subPayload)) {
|
|
|
|
message = subPayload.map((x) => `${x}`).join(',');
|
|
|
|
} else if (typeof subPayload === 'object') {
|
2019-09-09 10:48:09 -07:00
|
|
|
return this.iteratePayloadAttributeOutput(`${topicRoot}${key}-`, subPayload, options);
|
2019-05-28 11:01:46 -07:00
|
|
|
} else {
|
|
|
|
message = typeof subPayload === 'string' ? subPayload : JSON.stringify(subPayload);
|
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
await this.mqtt.publish(`${topicRoot}${key}`, message, options);
|
|
|
|
}
|
2019-05-28 11:01:46 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async callExtensionMethod(method, parameters) {
|
|
|
|
for (const extension of this.extensions) {
|
|
|
|
if (extension[method]) {
|
|
|
|
try {
|
|
|
|
await extension[method](...parameters);
|
|
|
|
} catch (error) {
|
|
|
|
/* istanbul ignore next */
|
|
|
|
logger.error(`Failed to call '${extension.constructor.name}' '${method}' (${error.stack})`);
|
|
|
|
/* istanbul ignore next */
|
|
|
|
if (process.env.JEST_WORKER_ID !== undefined) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-15 08:48:10 -07:00
|
|
|
}
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2018-05-17 08:20:46 -07:00
|
|
|
module.exports = Controller;
|