diff --git a/lib/extension/bind.ts b/lib/extension/bind.ts index 1a254e082a..36ba776c97 100755 --- a/lib/extension/bind.ts +++ b/lib/extension/bind.ts @@ -4,7 +4,7 @@ import utils from '../util/utils'; import Extension from './extension'; import stringify from 'json-stable-stringify-without-jsonify'; import debounce from 'debounce'; -import * as zigbeeHerdsman from 'zigbee-herdsman/dist'; +import {Zcl} from 'zigbee-herdsman'; import bind from 'bind-decorator'; import Device from '../model/device'; import Group from '../model/group'; @@ -97,12 +97,12 @@ const pollOnMessage: PollOnMessage = [ read: {cluster: 'genLevelCtrl', attributes: ['currentLevel']}, // When the bound devices/members of group have the following manufacturerIDs manufacturerIDs: [ - zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V, - zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL, - zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD, - zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC, - zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO, - zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO, + Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V, + Zcl.ManufacturerCode.ATMEL, + Zcl.ManufacturerCode.GLEDOPTO_CO_LTD, + Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC, + Zcl.ManufacturerCode.TELINK_MICRO, + Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO, ], manufacturerNames: [ 'GLEDOPTO', @@ -133,12 +133,12 @@ const pollOnMessage: PollOnMessage = [ }, read: {cluster: 'genOnOff', attributes: ['onOff']}, manufacturerIDs: [ - zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V, - zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL, - zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD, - zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC, - zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO, - zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO, + Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V, + Zcl.ManufacturerCode.ATMEL, + Zcl.ManufacturerCode.GLEDOPTO_CO_LTD, + Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC, + Zcl.ManufacturerCode.TELINK_MICRO, + Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO, ], manufacturerNames: [ 'GLEDOPTO', @@ -165,11 +165,11 @@ const pollOnMessage: PollOnMessage = [ }, }, manufacturerIDs: [ - zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V, - zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL, - zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD, - zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC, - zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO, + Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V, + Zcl.ManufacturerCode.ATMEL, + Zcl.ManufacturerCode.GLEDOPTO_CO_LTD, + Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC, + Zcl.ManufacturerCode.TELINK_MICRO, // Note: ManufacturerCode.BUSCH_JAEGER is left out intentionally here as their devices don't support colors ], manufacturerNames: [ diff --git a/lib/extension/bridge.ts b/lib/extension/bridge.ts index 4a9ea41d74..beed0193b7 100644 --- a/lib/extension/bridge.ts +++ b/lib/extension/bridge.ts @@ -61,23 +61,40 @@ export default class Bridge extends Extension { 'config/log_level': this.configLogLevel, }; - const mqtt = this.mqtt; - // eslint-disable-next-line @typescript-eslint/no-this-alias - const self = this; - class EventTransport extends Transport { - log(info: {message: string, level: string}, next: () => void): void { - if (info.level !== 'debug') { - const payload = stringify({message: info.message, level: info.level}); - if (payload !== self.lastBridgeLoggingPayload) { - self.lastBridgeLoggingPayload = payload; - mqtt.publish(`bridge/logging`, payload, {}, settings.get().mqtt.base_topic, true); + const debugToMQTTFrontend = settings.get().advanced.log_debug_to_mqtt_frontend; + const baseTopic = settings.get().mqtt.base_topic; + + const bridgeLogging = (message: string, level: string, namespace: string): void => { + const payload = stringify({message, level, namespace}); + + if (payload !== this.lastBridgeLoggingPayload) { + this.lastBridgeLoggingPayload = payload; + this.mqtt.publish(`bridge/logging`, payload, {}, baseTopic, true); + } + }; + + if (debugToMQTTFrontend) { + class DebugEventTransport extends Transport { + log(info: {message: string, level: string, namespace: string}, next: () => void): void { + bridgeLogging(info.message, info.level, info.namespace); + next(); + } + } + + this.logTransport = new DebugEventTransport(); + } else { + class EventTransport extends Transport { + log(info: {message: string, level: string, namespace: string}, next: () => void): void { + if (info.level !== 'debug') { + bridgeLogging(info.message, info.level, info.namespace); } + next(); } - next(); } + + this.logTransport = new EventTransport(); } - this.logTransport = new EventTransport(); logger.addTransport(this.logTransport); this.zigbee2mqttVersion = await utils.getZigbee2MQTTVersion(); @@ -174,18 +191,22 @@ export default class Bridge extends Extension { if (restartRequired) this.restartRequired = true; // Apply some settings on-the-fly. - if (newSettings.hasOwnProperty('permit_join')) { + if (newSettings.permit_join != undefined) { await this.zigbee.permitJoin(newSettings.permit_join); } - if (newSettings.hasOwnProperty('homeassistant')) { + if (newSettings.homeassistant != undefined) { await this.enableDisableExtension(newSettings.homeassistant, 'HomeAssistant'); } - if (newSettings.hasOwnProperty('advanced') && newSettings.advanced.hasOwnProperty('log_level')) { + if (newSettings.advanced?.log_level != undefined) { logger.setLevel(newSettings.advanced.log_level); } + if (newSettings.advanced?.log_debug_namespace_ignore != undefined) { + logger.setDebugNamespaceIgnore(newSettings.advanced.log_debug_namespace_ignore); + } + logger.info('Successfully changed options'); this.publishInfo(); return utils.getResponse(message, {restart_required: this.restartRequired}, null); diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index e65856d29d..c190d7d508 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -196,6 +196,8 @@ declare global { log_file: string, log_level: 'debug' | 'info' | 'error' | 'warn', log_syslog: KeyValue, + log_debug_to_mqtt_frontend: boolean, + log_debug_namespace_ignore: string, pan_id: number | 'GENERATE', ext_pan_id: number[] | 'GENERATE', channel: number, diff --git a/lib/util/logger.ts b/lib/util/logger.ts index 24cc3467dc..f61820872a 100644 --- a/lib/util/logger.ts +++ b/lib/util/logger.ts @@ -16,6 +16,7 @@ class Logger { private readonly directory: string; private readonly logger: winston.Logger; private readonly fileTransport: winston.transports.FileTransportInstance; + private debugNamespaceIgnoreRegex?: RegExp; constructor() { // What transports to enable @@ -120,6 +121,8 @@ class Logger { this.logger.add(new winston.transports.Syslog(options)); } + this.setDebugNamespaceIgnore(settings.get().advanced.log_debug_namespace_ignore); + this.info(logging); } @@ -136,6 +139,14 @@ class Logger { this.logger.remove(transport); } + public getDebugNamespaceIgnore(): string { + return this.debugNamespaceIgnoreRegex?.toString().slice(1, -1)/* remove slashes */ ?? ''; + } + + public setDebugNamespaceIgnore(value: string): void { + this.debugNamespaceIgnoreRegex = value != '' ? new RegExp(value) : undefined; + } + // TODO refactor Z2M level to 'warning' to simplify logic public getLevel(): LogLevel | 'warn' { return this.level === 'warning' ? 'warn' : this.level; @@ -162,6 +173,9 @@ class Logger { if (this.level !== 'debug') { return; } + if (this.debugNamespaceIgnoreRegex?.test(namespace)) { + return; + } this.logger.debug(message, {namespace}); } diff --git a/lib/util/settings.schema.json b/lib/util/settings.schema.json index cdbf9c7def..a85a3a6da6 100644 --- a/lib/util/settings.schema.json +++ b/lib/util/settings.schema.json @@ -497,6 +497,23 @@ "description": "Logging level", "default": "info" }, + "log_debug_to_mqtt_frontend": { + "type": "boolean", + "title": "Log debug to MQTT and frontend", + "description": "Log debug level to MQTT and frontend (may decrease overall performance)", + "requiresRestart": true, + "default": false + }, + "log_debug_namespace_ignore": { + "type": "string", + "title": "Log debug namespace ignore", + "description": "Do not log these namespaces (regex-based) for debug level", + "default": "", + "examples": [ + "^zhc:legacy:fz:(tuya|moes)", + "^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller" + ] + }, "log_syslog": { "type": "object", "title": "syslog", diff --git a/lib/util/settings.ts b/lib/util/settings.ts index 7f09ebd04a..2c5441e6d3 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -83,6 +83,8 @@ const defaults: RecursivePartial = { log_file: 'log.log', log_level: /* istanbul ignore next */ process.env.DEBUG ? 'debug' : 'info', log_syslog: {}, + log_debug_to_mqtt_frontend: false, + log_debug_namespace_ignore: '', pan_id: 0x1a62, ext_pan_id: [0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD], channel: 11, diff --git a/test/bridge.test.js b/test/bridge.test.js index ac0798a337..8089e5d33a 100644 --- a/test/bridge.test.js +++ b/test/bridge.test.js @@ -85,7 +85,7 @@ describe('Bridge', () => { // console.log(MQTT.publish.mock.calls.find((c) => c[0] === 'zigbee2mqtt/bridge/info')[1]) expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/info', - stringify({ "restart_required": false, "commit": version.commitHash, "config": { "advanced": { "legacy_availability_payload": true,"output":"json","adapter_concurrent": null, "adapter_delay": null, "availability_blacklist": [], "availability_blocklist": [], "availability_passlist": [], "availability_whitelist": [], "cache_state": true, "cache_state_persistent": true, "cache_state_send_on_startup": true, "channel": 11, "elapsed": false, "ext_pan_id": [221, 221, 221, 221, 221, 221, 221, 221], "last_seen": "disable", "legacy_api": false, "log_directory": directory, "log_file": "log.log", "log_level": "info", "log_output": ["console", "file"], "log_rotation": true, "log_symlink_current": false, "log_syslog": {}, "pan_id": 6754, "report": false, "soft_reset_timeout": 0, "timestamp_format": "YYYY-MM-DD HH:mm:ss" }, "blocklist": [], "device_options": {}, "devices": { "0x000b57fffec6a5b2": { "friendly_name": "bulb", "description": "this is my bulb", "retain": true }, "0x000b57fffec6a5b3": { "friendly_name": "bulb_color", "retain": false }, "0x000b57fffec6a5b4": { "friendly_name": "bulb_color_2", "retain": false }, "0x000b57fffec6a5b7": { "friendly_name": "bulb_2", "retain": false }, "0x0017880104a44559": { "friendly_name": "J1_cover" }, "0x0017880104e43559": { "friendly_name": "U202DST600ZB" }, "0x0017880104e44559": { "friendly_name": "3157100_thermostat" }, "0x0017880104e45517": { "friendly_name": "remote", "retain": true }, "0x0017880104e45520": { "friendly_name": "button", "retain": false }, "0x0017880104e45521": { "friendly_name": "button_double_key", "retain": false }, "0x0017880104e45522": { "friendly_name": "weather_sensor", "qos": 1, "retain": false }, "0x0017880104e45523": { "friendly_name": "occupancy_sensor", "retain": false }, "0x0017880104e45524": { "friendly_name": "power_plug", "retain": false }, "0x0017880104e45526": { "friendly_name": "GL-S-007ZS" }, "0x0017880104e45529": { "friendly_name": "unsupported2", "retain": false }, "0x0017880104e45530": { "friendly_name": "button_double_key_interviewing", "retain": false }, "0x0017880104e45540": { "friendly_name": "ikea_onoff" }, "0x0017880104e45541": { "friendly_name": "wall_switch", "retain": false }, "0x0017880104e45542": { "friendly_name": "wall_switch_double", "retain": false }, "0x0017880104e45543": { "friendly_name": "led_controller_1", "retain": false }, "0x0017880104e45544": { "friendly_name": "led_controller_2", "retain": false }, "0x0017880104e45545": { "friendly_name": "dimmer_wall_switch", "retain": false }, "0x0017880104e45547": { "friendly_name": "curtain", "retain": false }, "0x0017880104e45548": { "friendly_name": "fan", "retain": false }, "0x0017880104e45549": { "friendly_name": "siren", "retain": false }, "0x0017880104e45550": { "friendly_name": "thermostat", "retain": false }, "0x0017880104e45551": { "friendly_name": "smart vent", "retain": false }, "0x0017880104e45552": { "friendly_name": "j1", "retain": false }, "0x0017880104e45553": { "friendly_name": "bulb_enddevice", "retain": false }, "0x0017880104e45559": { "friendly_name": "cc2530_router", "retain": false }, "0x0017880104e45560": { "friendly_name": "livolo", "retain": false }, "0x0017880104e45724":{"friendly_name":"GLEDOPTO_2ID"}, "0xf4ce368a38be56a1":{"cover_1_enabled":"true","cover_1_tilt_enabled":"true","cover_2_enabled":"true","cover_2_tilt_enabled":"true","dimmer_1_dimming_enabled":"true","dimmer_1_enabled":"true","dimmer_2_dimming_enabled":"true","dimmer_2_enabled":"true","dimmer_3_dimming_enabled":"true","dimmer_3_enabled":"true","dimmer_4_dimming_enabled":"true","dimmer_4_enabled":"true","friendly_name":"zigfred_plus","front_surface_enabled":"true","retain":false}, "0x0017882104a44559": { "friendly_name": "TS0601_thermostat" }, "0x0017882104a44560":{"friendly_name":"TS0601_switch"}, "0x0017882104a44562":{"friendly_name":"TS0601_cover_switch"}, "0x0017882194e45543": { "friendly_name": "QS-Zigbee-D02-TRIAC-2C-LN" }, "0x90fd9ffffe4b64aa": { "friendly_name": "SP600_OLD" }, "0x90fd9ffffe4b64ab": { "friendly_name": "SP600_NEW" }, "0x90fd9ffffe4b64ac": { "friendly_name": "MKS-CM-W5" }, "0x90fd9ffffe4b64ae": { "friendly_name": "tradfri_remote", "retain": false }, "0x90fd9ffffe4b64af": { "friendly_name": "roller_shutter" }, "0x90fd9ffffe4b64ax": { "friendly_name": "ZNLDP12LM" }, "0x0017880104e45561": { "friendly_name": "temperature_sensor" }, "0x0017880104e45562": { "friendly_name": "heating_actuator" } }, "external_converters": [], "groups": { "1": { "friendly_name": "group_1", "retain": false }, "9": { "devices": ['bulb_color_2', 'bulb_2', 'wall_switch_double/right'], "friendly_name": "ha_discovery_group"}, "11": { "devices": ["bulb_2"], "friendly_name": "group_with_tradfri", "retain": false }, "14": { "devices": ["power_plug", "bulb_2"], "friendly_name": "switch_group", "retain": false }, "12": { "devices": ["TS0601_thermostat"], "friendly_name": "thermostat_group", "retain": false }, "15071": { "devices": ["bulb_color_2", "bulb_2"], "friendly_name": "group_tradfri_remote", "retain": false }, "2": { "friendly_name": "group_2", "retain": false }, "21":{"devices":["GLEDOPTO_2ID/cct"],"friendly_name":"gledopto_group"} }, "homeassistant": false, "map_options": { "graphviz": { "colors": { "fill": { "coordinator": "#e04e5d", "enddevice": "#fff8ce", "router": "#4ea3e0" }, "font": { "coordinator": "#ffffff", "enddevice": "#000000", "router": "#ffffff" }, "line": { "active": "#009900", "inactive": "#994444" } } } }, "mqtt": { "base_topic": "zigbee2mqtt", "include_device_information": false, "server": "mqtt://localhost", "force_disable_retain": false, }, "ota":{"disable_automatic_update_check":false,"update_check_interval":1440}, "passlist": [], "permit_join": true, "serial": { "disable_led": false, "port": "/dev/dummy" } }, "coordinator": { "ieee_address":"0x00124b00120144ae","meta": { "revision": 20190425, "version": 1 }, "type": "z-Stack" }, "log_level": "info", "network": { "channel": 15, "extended_pan_id": [0, 11, 22], "pan_id": 5674 }, "permit_join": false, "version": version.version, "zigbee_herdsman_converters": zhcVersion, "zigbee_herdsman": zhVersion, config_schema: settings.schema }), + stringify({ "restart_required": false, "commit": version.commitHash, "config": { "advanced": { "legacy_availability_payload": true,"output":"json","adapter_concurrent": null, "adapter_delay": null, "availability_blacklist": [], "availability_blocklist": [], "availability_passlist": [], "availability_whitelist": [], "cache_state": true, "cache_state_persistent": true, "cache_state_send_on_startup": true, "channel": 11, "elapsed": false, "ext_pan_id": [221, 221, 221, 221, 221, 221, 221, 221], "last_seen": "disable", "legacy_api": false, "log_directory": directory, "log_file": "log.log", "log_level": "info", "log_output": ["console", "file"], "log_rotation": true, "log_symlink_current": false, "log_syslog": {}, "log_debug_namespace_ignore": "", "log_debug_to_mqtt_frontend": false, "pan_id": 6754, "report": false, "soft_reset_timeout": 0, "timestamp_format": "YYYY-MM-DD HH:mm:ss" }, "blocklist": [], "device_options": {}, "devices": { "0x000b57fffec6a5b2": { "friendly_name": "bulb", "description": "this is my bulb", "retain": true }, "0x000b57fffec6a5b3": { "friendly_name": "bulb_color", "retain": false }, "0x000b57fffec6a5b4": { "friendly_name": "bulb_color_2", "retain": false }, "0x000b57fffec6a5b7": { "friendly_name": "bulb_2", "retain": false }, "0x0017880104a44559": { "friendly_name": "J1_cover" }, "0x0017880104e43559": { "friendly_name": "U202DST600ZB" }, "0x0017880104e44559": { "friendly_name": "3157100_thermostat" }, "0x0017880104e45517": { "friendly_name": "remote", "retain": true }, "0x0017880104e45520": { "friendly_name": "button", "retain": false }, "0x0017880104e45521": { "friendly_name": "button_double_key", "retain": false }, "0x0017880104e45522": { "friendly_name": "weather_sensor", "qos": 1, "retain": false }, "0x0017880104e45523": { "friendly_name": "occupancy_sensor", "retain": false }, "0x0017880104e45524": { "friendly_name": "power_plug", "retain": false }, "0x0017880104e45526": { "friendly_name": "GL-S-007ZS" }, "0x0017880104e45529": { "friendly_name": "unsupported2", "retain": false }, "0x0017880104e45530": { "friendly_name": "button_double_key_interviewing", "retain": false }, "0x0017880104e45540": { "friendly_name": "ikea_onoff" }, "0x0017880104e45541": { "friendly_name": "wall_switch", "retain": false }, "0x0017880104e45542": { "friendly_name": "wall_switch_double", "retain": false }, "0x0017880104e45543": { "friendly_name": "led_controller_1", "retain": false }, "0x0017880104e45544": { "friendly_name": "led_controller_2", "retain": false }, "0x0017880104e45545": { "friendly_name": "dimmer_wall_switch", "retain": false }, "0x0017880104e45547": { "friendly_name": "curtain", "retain": false }, "0x0017880104e45548": { "friendly_name": "fan", "retain": false }, "0x0017880104e45549": { "friendly_name": "siren", "retain": false }, "0x0017880104e45550": { "friendly_name": "thermostat", "retain": false }, "0x0017880104e45551": { "friendly_name": "smart vent", "retain": false }, "0x0017880104e45552": { "friendly_name": "j1", "retain": false }, "0x0017880104e45553": { "friendly_name": "bulb_enddevice", "retain": false }, "0x0017880104e45559": { "friendly_name": "cc2530_router", "retain": false }, "0x0017880104e45560": { "friendly_name": "livolo", "retain": false }, "0x0017880104e45724":{"friendly_name":"GLEDOPTO_2ID"}, "0xf4ce368a38be56a1":{"cover_1_enabled":"true","cover_1_tilt_enabled":"true","cover_2_enabled":"true","cover_2_tilt_enabled":"true","dimmer_1_dimming_enabled":"true","dimmer_1_enabled":"true","dimmer_2_dimming_enabled":"true","dimmer_2_enabled":"true","dimmer_3_dimming_enabled":"true","dimmer_3_enabled":"true","dimmer_4_dimming_enabled":"true","dimmer_4_enabled":"true","friendly_name":"zigfred_plus","front_surface_enabled":"true","retain":false}, "0x0017882104a44559": { "friendly_name": "TS0601_thermostat" }, "0x0017882104a44560":{"friendly_name":"TS0601_switch"}, "0x0017882104a44562":{"friendly_name":"TS0601_cover_switch"}, "0x0017882194e45543": { "friendly_name": "QS-Zigbee-D02-TRIAC-2C-LN" }, "0x90fd9ffffe4b64aa": { "friendly_name": "SP600_OLD" }, "0x90fd9ffffe4b64ab": { "friendly_name": "SP600_NEW" }, "0x90fd9ffffe4b64ac": { "friendly_name": "MKS-CM-W5" }, "0x90fd9ffffe4b64ae": { "friendly_name": "tradfri_remote", "retain": false }, "0x90fd9ffffe4b64af": { "friendly_name": "roller_shutter" }, "0x90fd9ffffe4b64ax": { "friendly_name": "ZNLDP12LM" }, "0x0017880104e45561": { "friendly_name": "temperature_sensor" }, "0x0017880104e45562": { "friendly_name": "heating_actuator" } }, "external_converters": [], "groups": { "1": { "friendly_name": "group_1", "retain": false }, "9": { "devices": ['bulb_color_2', 'bulb_2', 'wall_switch_double/right'], "friendly_name": "ha_discovery_group"}, "11": { "devices": ["bulb_2"], "friendly_name": "group_with_tradfri", "retain": false }, "14": { "devices": ["power_plug", "bulb_2"], "friendly_name": "switch_group", "retain": false }, "12": { "devices": ["TS0601_thermostat"], "friendly_name": "thermostat_group", "retain": false }, "15071": { "devices": ["bulb_color_2", "bulb_2"], "friendly_name": "group_tradfri_remote", "retain": false }, "2": { "friendly_name": "group_2", "retain": false }, "21":{"devices":["GLEDOPTO_2ID/cct"],"friendly_name":"gledopto_group"} }, "homeassistant": false, "map_options": { "graphviz": { "colors": { "fill": { "coordinator": "#e04e5d", "enddevice": "#fff8ce", "router": "#4ea3e0" }, "font": { "coordinator": "#ffffff", "enddevice": "#000000", "router": "#ffffff" }, "line": { "active": "#009900", "inactive": "#994444" } } } }, "mqtt": { "base_topic": "zigbee2mqtt", "include_device_information": false, "server": "mqtt://localhost", "force_disable_retain": false, }, "ota":{"disable_automatic_update_check":false,"update_check_interval":1440}, "passlist": [], "permit_join": true, "serial": { "disable_led": false, "port": "/dev/dummy" } }, "coordinator": { "ieee_address":"0x00124b00120144ae","meta": { "revision": 20190425, "version": 1 }, "type": "z-Stack" }, "log_level": "info", "network": { "channel": 15, "extended_pan_id": [0, 11, 22], "pan_id": 5674 }, "permit_join": false, "version": version.version, "zigbee_herdsman_converters": zhcVersion, "zigbee_herdsman": zhVersion, config_schema: settings.schema }), { retain: true, qos: 0 }, expect.any(Function) ); @@ -110,7 +110,7 @@ describe('Bridge', () => { logger.info("this is a test"); // Should not publish dupes expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/logging', - stringify({message: 'this is a test', level: 'info'}), + stringify({message: 'this is a test', level: 'info', namespace: 'z2m'}), { retain: false, qos: 0 }, expect.any(Function) ); @@ -122,6 +122,32 @@ describe('Bridge', () => { expect(MQTT.publish).toHaveBeenCalledTimes(0); }); + it('Should log to MQTT including debug when enabled', async () => { + settings.set(['advanced', 'log_debug_to_mqtt_frontend'], true); + await resetExtension(); + + logger.setTransportsEnabled(true); + MQTT.publish.mockClear(); + logger.info.mockClear(); + logger.info("this is a test"); + logger.info("this is a test"); // Should not publish dupes + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/logging', + stringify({message: 'this is a test', level: 'info', namespace: 'z2m'}), + { retain: false, qos: 0 }, + expect.any(Function) + ); + expect(MQTT.publish).toHaveBeenCalledTimes(1); + + // Should publish debug logging + MQTT.publish.mockClear(); + logger.debug("this is a test"); + expect(MQTT.publish).toHaveBeenCalledTimes(1); + + settings.set(['advanced', 'log_debug_to_mqtt_frontend'], false); + settings.reRead(); + }); + it('Shouldnt log to MQTT when not connected', async () => { logger.setTransportsEnabled(true); MQTT.mock.reconnecting = true; @@ -1267,6 +1293,20 @@ describe('Bridge', () => { ); }); + it('Change options and apply - log_debug_namespace_ignore', async () => { + MQTT.publish.mockClear(); + const nsIgnore = '^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller'; + MQTT.events.message('zigbee2mqtt/bridge/request/options', stringify({options: {advanced: {log_debug_namespace_ignore: nsIgnore}}})); + await flushPromises(); + expect(logger.getDebugNamespaceIgnore()).toStrictEqual(nsIgnore); + expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/response/options', + stringify({"data":{"restart_required":false},"status":"ok"}), + {retain: false, qos: 0}, expect.any(Function) + ); + }); + it('Change options restart required', async () => { zigbeeHerdsman.permitJoin.mockClear(); settings.apply({serial: {port: '123'}}); diff --git a/test/logger.test.js b/test/logger.test.js index eddc64d5fb..cf0b7201f2 100644 --- a/test/logger.test.js +++ b/test/logger.test.js @@ -178,4 +178,66 @@ describe('Logger', () => { expect(error).toHaveBeenCalledWith('error', {namespace: 'z2m'}); expect(error).toHaveBeenCalledTimes(2); }); + + it.each([ + [ + '^zhc:legacy:fz:(tuya|moes)', + new RegExp(/^zhc:legacy:fz:(tuya|moes)/), + [ + { ns: 'zhc:legacy:fz:tuya_device12', match: true }, + { ns: 'zhc:legacy:fz:moes_dimmer', match: true }, + { ns: 'zhc:legacy:fz:not_moes', match: false }, + { ns: 'zhc:legacy:fz', match: false }, + { ns: 'zhc:legacy:fz:', match: false }, + { ns: '1zhc:legacy:fz:tuya_device12', match: false }, + ] + ], + [ + '^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller', + new RegExp(/^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller/), + [ + { ns: 'zh:ember:uart:ash', match: true }, + { ns: 'zh:ember:uart', match: false }, + { ns: 'zh:controller', match: true }, + { ns: 'zh:controller:', match: true }, + { ns: 'azh:controller:', match: false }, + ] + ], + [ + '', + undefined, + [ + { ns: 'zhc:legacy:fz:tuya_device12', match: false }, + { ns: 'zhc:legacy:fz:moes_dimmer', match: false }, + { ns: 'zhc:legacy:fz:not_moes', match: false }, + { ns: 'zhc:legacy:fz', match: false }, + { ns: 'zhc:legacy:fz:', match: false }, + { ns: '1zhc:legacy:fz:tuya_device12', match: false }, + { ns: 'zh:ember:uart:ash', match: false }, + { ns: 'zh:ember:uart', match: false }, + { ns: 'zh:controller', match: false }, + { ns: 'zh:controller:', match: false }, + { ns: 'azh:controller:', match: false }, + ] + ], + ])('Sets namespace ignore for debug level %s', (ignore, expected, tests) => { + const logger = require('../lib/util/logger').default; + logger.setLevel('debug'); + const debugSpy = jest.spyOn(logger.winston, 'debug'); + logger.setDebugNamespaceIgnore(ignore); + expect(logger.debugNamespaceIgnoreRegex).toStrictEqual(expected); + expect(logger.getDebugNamespaceIgnore()).toStrictEqual(ignore); + + for (const test of tests) { + logger.debug('Test message', test.ns); + + if (test.match) { + expect(debugSpy).not.toHaveBeenCalled(); + } else { + expect(debugSpy).toHaveBeenCalled(); + } + + debugSpy.mockClear(); + } + }) }); diff --git a/test/stub/logger.js b/test/stub/logger.js index b06e380874..17d2ac90c0 100644 --- a/test/stub/logger.js +++ b/test/stub/logger.js @@ -1,4 +1,5 @@ let level = 'info'; +let debugNamespaceIgnore = ''; let transports = []; @@ -13,10 +14,10 @@ const callTransports = (level, message, namespace) => { const mock = { init: jest.fn(), - info: jest.fn().mockImplementation((msg) => callTransports('info', msg)), - warning: jest.fn().mockImplementation((msg) => callTransports('warning', msg)), - error: jest.fn().mockImplementation((msg) => callTransports('error', msg)), - debug: jest.fn().mockImplementation((msg) => callTransports('debug', msg)), + info: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('info', msg, namespace)), + warning: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('warning', msg, namespace)), + error: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('error', msg, namespace)), + debug: jest.fn().mockImplementation((msg, namespace = 'z2m') => callTransports('debug', msg, namespace)), cleanup: jest.fn(), logOutput: jest.fn(), add: (transport) => transports.push(transport), @@ -26,6 +27,8 @@ const mock = { }, setLevel: (newLevel) => {level = newLevel}, getLevel: () => level, + setDebugNamespaceIgnore: (newIgnore) => {debugNamespaceIgnore = newIgnore}, + getDebugNamespaceIgnore: () => debugNamespaceIgnore, setTransportsEnabled: (value) => {transportsEnabled = value}, end: jest.fn(), };