This commit is contained in:
Koen Kanters
2020-07-28 22:12:22 +02:00
parent ae93e6fd0f
commit a7f990ef95
5 changed files with 71 additions and 11 deletions
+15
View File
@@ -3,6 +3,7 @@ const utils = require('../util/utils');
const Extension = require('./extension');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const settings = require('../util/settings');
const Transport = require('winston-transport');
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`);
@@ -10,6 +11,7 @@ class Bridge extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.lastJoinedDeviceIeeeAddr = null;
this.setupMQTTLogging();
this.requestLookup = {
'device/options': this.deviceOptions.bind(this),
@@ -39,6 +41,19 @@ class Bridge extends Extension {
await this.publishGroups();
}
setupMQTTLogging() {
const mqtt = this.mqtt;
class EventTransport extends Transport {
log(info, callback) {
const payload = JSON.stringify({message: info.message, level: info.level});
mqtt.publish(`bridge/logging`, payload, {}, settings.get().mqtt.base_topic, true);
callback();
}
}
logger.add(new EventTransport());
}
async onMQTTMessage(topic, message) {
const match = topic.match(requestRegex);
if (match && this.requestLookup[match[1].toLowerCase()]) {
+8 -4
View File
@@ -99,17 +99,21 @@ class MQTT extends events.EventEmitter {
return this.client && !this.client.reconnecting;
}
async publish(topic, payload, options, base=settings.get().mqtt.base_topic) {
async publish(topic, payload, options, base=settings.get().mqtt.base_topic, skipLog=false) {
topic = `${base}/${topic}`;
options = {qos: 0, retain: false, ...options};
if (!this.isConnected()) {
logger.error(`Not connected to MQTT server!`);
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`);
if (!skipLog) {
logger.error(`Not connected to MQTT server!`);
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`);
}
return;
}
logger.info(`MQTT publish: topic '${topic}', payload '${payload}'`);
if (!skipLog) {
logger.info(`MQTT publish: topic '${topic}', payload '${payload}'`);
}
return new Promise((resolve) => {
this.client.publish(topic, payload, options, () => resolve());
+29 -1
View File
@@ -5,7 +5,6 @@ const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const {coordinator, bulb, unsupported} = zigbeeHerdsman.devices;
zigbeeHerdsman.returnDevices.push(coordinator.ieeeAddr);
@@ -16,6 +15,7 @@ describe('Bridge', () => {
let controller;
beforeEach(async () => {
MQTT.mock.reconnecting = false;
data.writeDefaultConfiguration();
settings._reRead();
settings.set(['advanced', 'legacy_api'], false);
@@ -23,6 +23,7 @@ describe('Bridge', () => {
data.writeDefaultState();
logger.info.mockClear();
logger.warn.mockClear();
logger.setTransportsEnabled(false);
MQTT.publish.mockClear();
const device = zigbeeHerdsman.devices.bulb;
device.removeFromDatabase.mockClear();
@@ -51,7 +52,34 @@ describe('Bridge', () => {
);
});
it('Should log to MQTT', async () => {
logger.setTransportsEnabled(true);
MQTT.publish.mockClear();
logger.info.mockClear();
logger.info("this is a test");
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/logging',
JSON.stringify({message: 'this is a test', level: 'info'}),
{ retain: false, qos: 0 },
expect.any(Function)
);
expect(logger.info).toHaveBeenCalledTimes(1);
});
it('Shouldnt log to MQTT when not connected', async () => {
logger.setTransportsEnabled(true);
MQTT.mock.reconnecting = true;
MQTT.publish.mockClear();
logger.info.mockClear();
logger.error.mockClear();
logger.info("this is a test");
expect(MQTT.publish).toHaveBeenCalledTimes(0);
expect(logger.info).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledTimes(0);
});
it('Should publish devices on startup', async () => {
logger.setTransportsEnabled(true);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/groups',
JSON.stringify([{"id":1,"friendly_name":"group_1","members":[]},{"id":15071,"friendly_name":"group_tradfri_remote","members":[]},{"id":99,"friendly_name":99,"members":[]},{"id":11,"friendly_name":"group_with_tradfri","members":[]},{"id":2,"friendly_name":"group_2","members":[]}]),
+17 -5
View File
@@ -1,14 +1,26 @@
let level = 'info';
const transports = [];
let transportsEnabled = false;
const callTransports = (level, message) => {
if (transportsEnabled) {
for (const transport of transports) {
transport.log({level, message}, () => {});
}
}
}
const mock = {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
trace: jest.fn(),
info: jest.fn().mockImplementation((msg) => callTransports('info', msg)),
warn: jest.fn().mockImplementation((msg) => callTransports('warn', msg)),
error: jest.fn().mockImplementation((msg) => callTransports('error', msg)),
debug: jest.fn().mockImplementation((msg) => callTransports('debug', msg)),
cleanup: jest.fn(),
add: (transport) => transports.push(transport),
setLevel: (newLevel) => {level = newLevel},
getLevel: () => level,
setTransportsEnabled: (value) => {transportsEnabled = value},
};
jest.mock('../../lib/util/logger', () => (mock));
+2 -1
View File
@@ -4,6 +4,7 @@ const mock = {
publish: jest.fn().mockImplementation((topic, payload, options, cb) => cb()),
end: jest.fn(),
subscribe: jest.fn(),
reconnecting: false,
on: (type, handler) => {
if (type === 'connect') {
handler();
@@ -20,5 +21,5 @@ jest.mock('mqtt', () => {
});
module.exports = {
events, ...mock, connect: mockConnect,
events, ...mock, connect: mockConnect, mock,
};