fix(ignore): Migrate to eslint 9 (#23800)

* Eslint 9

* Automatic changes

* Manual changes

* Process feedback

* u
This commit is contained in:
Koen Kanters
2024-09-08 14:26:18 +02:00
committed by GitHub
parent d989061122
commit afd80449b3
36 changed files with 582 additions and 627 deletions
-2
View File
@@ -1,2 +0,0 @@
node_modules/*
test/
-63
View File
@@ -1,63 +0,0 @@
module.exports = {
env: {
'jest/globals': true,
es6: true,
node: true,
},
extends: ['eslint:recommended', 'plugin:jest/recommended', 'plugin:jest/style', 'prettier'],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
rules: {
'require-jsdoc': 'off',
'no-prototype-builtins': 'off',
'@typescript-eslint/no-floating-promises': 'error',
},
plugins: ['jest', 'perfectionist'],
overrides: [
{
files: ['*.ts'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: ['plugin:@typescript-eslint/recommended'],
parserOptions: {
project: './tsconfig.json',
},
rules: {
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'no-return-await': 'error',
'perfectionist/sort-imports': [
'error',
{
groups: [
'type',
['builtin', 'external'],
'internal-type',
'internal',
['parent-type', 'sibling-type', 'index-type'],
['parent', 'sibling', 'index'],
'object',
'unknown',
],
customGroups: {
value: {},
type: {},
},
newlinesBetween: 'always',
internalPattern: ['~/**'],
type: 'natural',
order: 'asc',
ignoreCase: false,
},
],
},
},
],
};
+1 -2
View File
@@ -23,8 +23,7 @@ jobs:
node-version: 20
cache: npm
# connect-gzip-static@4.0.0 requires Node 20 >=
# eslint: https://github.com/typescript-eslint/typescript-eslint/issues/8211
- run: npx npm-check-updates -u -x connect-gzip-static -x eslint
- run: npx npm-check-updates -u -x connect-gzip-static
- run: rm -f package-lock.json
- run: npm install
- uses: peter-evans/create-pull-request@v7
+19 -1
View File
@@ -5,5 +5,23 @@
"printWidth": 150,
"bracketSpacing": false,
"endOfLine": "lf",
"tabWidth": 4
"tabWidth": 4,
"importOrder": [
"",
"<TYPES>^(node:)",
"",
"<TYPES>",
"",
"<TYPES>^[.]",
"",
"<BUILTIN_MODULES>",
"",
"<THIRD_PARTY_MODULES>",
"",
"^zigbee",
"",
"^[.]"
],
"importOrderParserPlugins": ["typescript", "decorators"],
"plugins": ["@ianvs/prettier-plugin-sort-imports"]
}
+32
View File
@@ -0,0 +1,32 @@
// @ts-check
import eslint from '@eslint/js';
import eslintConfigPrettier from 'eslint-config-prettier';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
languageOptions: {
parserOptions: {
project: true,
},
},
rules: {
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/ban-ts-comment': 'error',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'array-bracket-spacing': ['error', 'never'],
'no-return-await': 'error',
'object-curly-spacing': ['error', 'never'],
'@typescript-eslint/no-floating-promises': 'error',
},
},
{
ignores: ['dist/', '**/*.js', '**/*.mjs'],
},
eslintConfigPrettier,
);
+20 -14
View File
@@ -1,6 +1,10 @@
import type * as SdNotify from 'sd-notify';
import assert from 'assert';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import {setLogger as zhSetLogger} from 'zigbee-herdsman';
import {setLogger as zhcSetLogger} from 'zigbee-herdsman-converters';
@@ -31,6 +35,8 @@ import * as settings from './util/settings';
import utils from './util/utils';
import Zigbee from './zigbee';
type SdNotifyType = typeof SdNotify;
const AllExtensions = [
ExtensionPublish,
ExtensionReceive,
@@ -63,15 +69,6 @@ type ExtensionArgs = [
addExtension: (extension: Extension) => Promise<void>,
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let sdNotify: any = null;
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
sdNotify = process.env.NOTIFY_SOCKET ? require('sd-notify') : null;
} catch {
// sd-notify is optional
}
export class Controller {
private eventBus: EventBus;
private zigbee: Zigbee;
@@ -81,6 +78,7 @@ export class Controller {
private exitCallback: (code: number, restart: boolean) => Promise<void>;
private extensions: Extension[];
private extensionArgs: ExtensionArgs;
private sdNotify: SdNotifyType | undefined;
constructor(restartCallback: () => Promise<void>, exitCallback: (code: number, restart: boolean) => Promise<void>) {
logger.init();
@@ -149,6 +147,14 @@ export class Controller {
const info = await utils.getZigbee2MQTTVersion();
logger.info(`Starting Zigbee2MQTT version ${info.version} (commit #${info.commitHash})`);
try {
this.sdNotify = process.env.NOTIFY_SOCKET ? await import('sd-notify') : undefined;
logger.debug('sd-notify loaded');
} catch {
// istanbul ignore next
logger.debug('sd-notify is not installed');
}
// Start zigbee
let startResult;
try {
@@ -224,11 +230,11 @@ export class Controller {
logger.info(`Zigbee2MQTT started!`);
const watchdogInterval = sdNotify?.watchdogInterval() || 0;
const watchdogInterval = this.sdNotify?.watchdogInterval() || 0;
if (watchdogInterval > 0) {
sdNotify.startWatchdogMode(Math.floor(watchdogInterval / 2));
this.sdNotify?.startWatchdogMode(Math.floor(watchdogInterval / 2));
}
sdNotify?.ready();
this.sdNotify?.ready();
}
@bind async enableDisableExtension(enable: boolean, name: string): Promise<void> {
@@ -253,7 +259,7 @@ export class Controller {
}
async stop(restart = false): Promise<void> {
sdNotify?.stopping();
this.sdNotify?.stopping(process.pid);
// Call extensions
await this.callExtensions('stop', this.extensions);
@@ -272,7 +278,7 @@ export class Controller {
code = 1;
}
sdNotify?.stopWatchdogMode();
this.sdNotify?.stopWatchdogMode();
return this.exit(code, restart);
}
-1
View File
@@ -2,7 +2,6 @@ import events from 'events';
import logger from './util/logger';
// eslint-disable-next-line
type ListenerKey = object;
interface EventBusMap {
+2
View File
@@ -1,6 +1,8 @@
import assert from 'assert';
import bind from 'bind-decorator';
import debounce from 'debounce';
import * as zhc from 'zigbee-herdsman-converters';
import logger from '../util/logger';
+2
View File
@@ -1,7 +1,9 @@
import assert from 'assert';
import bind from 'bind-decorator';
import debounce from 'debounce';
import stringify from 'json-stable-stringify-without-jsonify';
import {Zcl} from 'zigbee-herdsman';
import {ClusterName} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
+24 -23
View File
@@ -1,14 +1,15 @@
/* eslint-disable camelcase */
import bind from 'bind-decorator';
import fs from 'fs';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import JSZip from 'jszip';
import objectAssignDeep from 'object-assign-deep';
import winston from 'winston';
import Transport from 'winston-transport';
import {Clusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/cluster';
import {CustomClusters, ClusterDefinition, ClusterName} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
import * as zhc from 'zigbee-herdsman-converters';
import {Clusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/cluster';
import {ClusterDefinition, ClusterName, CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
import Device from '../model/device';
import Group from '../model/group';
@@ -275,12 +276,12 @@ export default class Bridge extends Extension {
}
@bind async groupAdd(message: string | KeyValue): Promise<MQTTResponse> {
if (typeof message === 'object' && !message.hasOwnProperty('friendly_name')) {
if (typeof message === 'object' && message.friendly_name === undefined) {
throw new Error(`Invalid payload`);
}
const friendlyName = typeof message === 'object' ? message.friendly_name : message;
const ID = typeof message === 'object' && message.hasOwnProperty('id') ? message.id : null;
const ID = typeof message === 'object' && message.id !== undefined ? message.id : null;
const group = settings.addGroup(friendlyName, ID);
this.zigbee.createGroup(group.ID);
await this.publishGroups();
@@ -316,7 +317,7 @@ export default class Bridge extends Extension {
}
@bind async installCodeAdd(message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message === 'object' && !message.hasOwnProperty('value')) {
if (typeof message === 'object' && message.value === undefined) {
throw new Error('Invalid payload');
}
@@ -327,7 +328,7 @@ export default class Bridge extends Extension {
}
@bind async permitJoin(message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message === 'object' && !message.hasOwnProperty('value')) {
if (typeof message === 'object' && message.value === undefined) {
throw new Error('Invalid payload');
}
@@ -426,7 +427,7 @@ export default class Bridge extends Extension {
}
@bind async touchlinkIdentify(message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message !== 'object' || !message.hasOwnProperty('ieee_address') || !message.hasOwnProperty('channel')) {
if (typeof message !== 'object' || message.ieee_address === undefined || message.channel === undefined) {
throw new Error('Invalid payload');
}
@@ -438,7 +439,7 @@ export default class Bridge extends Extension {
@bind async touchlinkFactoryReset(message: KeyValue | string): Promise<MQTTResponse> {
let result = false;
const payload: {ieee_address?: string; channel?: number} = {};
if (typeof message === 'object' && message.hasOwnProperty('ieee_address') && message.hasOwnProperty('channel')) {
if (typeof message === 'object' && message.ieee_address !== undefined && message.channel !== undefined) {
logger.info(`Start Touchlink factory reset of '${message.ieee_address}' on channel ${message.channel}`);
result = await this.zigbee.touchlinkFactoryReset(message.ieee_address, message.channel);
payload.ieee_address = message.ieee_address;
@@ -473,7 +474,7 @@ export default class Bridge extends Extension {
getValue(message: KeyValue | string): string | boolean | number {
if (typeof message === 'object') {
if (!message.hasOwnProperty('value')) {
if (message.value === undefined) {
throw new Error('No value given');
}
@@ -484,7 +485,7 @@ export default class Bridge extends Extension {
}
async changeEntityOptions(entityType: 'device' | 'group', message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message !== 'object' || !message.hasOwnProperty('id') || !message.hasOwnProperty('options')) {
if (typeof message !== 'object' || message.id === undefined || message.options === undefined) {
throw new Error(`Invalid payload`);
}
@@ -514,12 +515,12 @@ export default class Bridge extends Extension {
@bind async deviceConfigureReporting(message: string | KeyValue): Promise<MQTTResponse> {
if (
typeof message !== 'object' ||
!message.hasOwnProperty('id') ||
!message.hasOwnProperty('cluster') ||
!message.hasOwnProperty('maximum_report_interval') ||
!message.hasOwnProperty('minimum_report_interval') ||
!message.hasOwnProperty('reportable_change') ||
!message.hasOwnProperty('attribute')
message.id === undefined ||
message.cluster === undefined ||
message.maximum_report_interval === undefined ||
message.minimum_report_interval === undefined ||
message.reportable_change === undefined ||
message.attribute === undefined
) {
throw new Error(`Invalid payload`);
}
@@ -565,7 +566,7 @@ export default class Bridge extends Extension {
}
@bind async deviceInterview(message: string | KeyValue): Promise<MQTTResponse> {
if (typeof message !== 'object' || !message.hasOwnProperty('id')) {
if (typeof message !== 'object' || message.id === undefined) {
throw new Error(`Invalid payload`);
}
@@ -588,7 +589,7 @@ export default class Bridge extends Extension {
}
@bind async deviceGenerateExternalDefinition(message: string | KeyValue): Promise<MQTTResponse> {
if (typeof message !== 'object' || !message.hasOwnProperty('id')) {
if (typeof message !== 'object' || message.id === undefined) {
throw new Error(`Invalid payload`);
}
@@ -606,7 +607,7 @@ export default class Bridge extends Extension {
async renameEntity(entityType: 'group' | 'device', message: string | KeyValue): Promise<MQTTResponse> {
const deviceAndHasLast = entityType === 'device' && typeof message === 'object' && message.last === true;
if (typeof message !== 'object' || (!message.hasOwnProperty('from') && !deviceAndHasLast) || !message.hasOwnProperty('to')) {
if (typeof message !== 'object' || (message.from === undefined && !deviceAndHasLast) || message.to === undefined) {
throw new Error(`Invalid payload`);
}
@@ -616,7 +617,7 @@ export default class Bridge extends Extension {
const from = deviceAndHasLast ? this.lastJoinedDeviceIeeeAddr : message.from;
const to = message.to;
const homeAssisantRename = message.hasOwnProperty('homeassistant_rename') ? message.homeassistant_rename : false;
const homeAssisantRename = message.homeassistant_rename !== undefined ? message.homeassistant_rename : false;
const entity = this.getEntity(entityType, from);
const oldFriendlyName = entity.options.friendly_name;
@@ -745,7 +746,7 @@ export default class Bridge extends Extension {
ieee_address: this.zigbee.firstCoordinatorEndpoint().getDevice().ieeeAddr,
...this.coordinatorVersion,
},
network: utils.toSnakeCase(await this.zigbee.getNetworkParameters()),
network: utils.toSnakeCaseObject(await this.zigbee.getNetworkParameters()),
log_level: logger.getLevel(),
permit_join: this.zigbee.getPermitJoin(),
permit_join_timeout: this.zigbee.getPermitJoinTimeout(),
+6 -5
View File
@@ -1,5 +1,6 @@
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import * as zhc from 'zigbee-herdsman-converters';
import Device from '../model/device';
@@ -19,7 +20,7 @@ export default class Configure extends Extension {
@bind private async onReconfigure(data: eventdata.Reconfigure): Promise<void> {
// Disabling reporting unbinds some cluster which could be bound by configure, re-setup.
if (data.device.zh.meta?.hasOwnProperty('configured')) {
if (data.device.zh.meta?.configured !== undefined) {
delete data.device.zh.meta.configured;
data.device.zh.save();
}
@@ -43,7 +44,7 @@ export default class Configure extends Extension {
await this.configure(device, 'mqtt_message', true);
} else if (data.topic === this.topic) {
const message = utils.parseJSON(data.message, data.message);
const ID = typeof message === 'object' && message.hasOwnProperty('id') ? message.id : message;
const ID = typeof message === 'object' && message.id !== undefined ? message.id : message;
let error: string | undefined;
const device = this.zigbee.resolveEntity(ID);
@@ -76,7 +77,7 @@ export default class Configure extends Extension {
});
this.eventBus.onDeviceJoined(this, async (data) => {
if (data.device.zh.meta.hasOwnProperty('configured')) {
if (data.device.zh.meta.configured !== undefined) {
delete data.device.zh.meta.configured;
data.device.zh.save();
}
@@ -104,7 +105,7 @@ export default class Configure extends Extension {
return;
}
if (device.zh.meta?.hasOwnProperty('configured')) {
if (device.zh.meta?.configured !== undefined) {
return;
}
@@ -120,7 +121,7 @@ export default class Configure extends Extension {
this.configuring.add(device.ieeeAddr);
if (!this.attempts.hasOwnProperty(device.ieeeAddr)) {
if (this.attempts[device.ieeeAddr] === undefined) {
this.attempts[device.ieeeAddr] = 0;
}
+4 -3
View File
@@ -1,8 +1,9 @@
import bind from 'bind-decorator';
import fs from 'fs';
import stringify from 'json-stable-stringify-without-jsonify';
import path from 'path';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import * as settings from '../util/settings';
import utils from '../util/utils';
import data from './../util/data';
@@ -93,7 +94,7 @@ export default class ExternalExtension extends Extension {
@bind private async loadExtension(ConstructorClass: typeof Extension): Promise<void> {
await this.enableDisableExtension(false, ConstructorClass.name);
// @ts-ignore
// @ts-expect-error `ConstructorClass` is the interface, not the actual passed class
await this.addExtension(new ConstructorClass(this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus, settings, logger));
}
+8 -8
View File
@@ -1,14 +1,16 @@
import assert from 'assert';
import bind from 'bind-decorator';
import gzipStatic, {RequestHandler} from 'connect-gzip-static';
import finalhandler from 'finalhandler';
import fs from 'fs';
import http from 'http';
import https from 'https';
import stringify from 'json-stable-stringify-without-jsonify';
import net from 'net';
import url from 'url';
import bind from 'bind-decorator';
import gzipStatic, {RequestHandler} from 'connect-gzip-static';
import finalhandler from 'finalhandler';
import stringify from 'json-stable-stringify-without-jsonify';
import WebSocket from 'ws';
import frontend from 'zigbee2mqtt-frontend';
import logger from '../util/logger';
@@ -78,8 +80,7 @@ export default class Frontend extends Extension {
/* istanbul ignore next */
const options = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setHeaders: (res: any, path: string): void => {
setHeaders: (res: {setHeader(key: string, value: string): void}, path: string): void => {
if (path.endsWith('index.html')) {
res.setHeader('Cache-Control', 'no-store');
}
@@ -117,8 +118,7 @@ export default class Frontend extends Extension {
}
@bind private onRequest(request: http.IncomingMessage, response: http.ServerResponse): void {
// @ts-ignore
this.fileServer(request, response, finalhandler(request, response));
this.fileServer?.(request, response, finalhandler(request, response));
}
private authenticate(request: http.IncomingMessage, cb: (authenticate: boolean) => void): void {
+2
View File
@@ -1,7 +1,9 @@
import assert from 'assert';
import bind from 'bind-decorator';
import equals from 'fast-deep-equal/es6';
import stringify from 'json-stable-stringify-without-jsonify';
import * as zhc from 'zigbee-herdsman-converters';
import Device from '../model/device';
+15 -16
View File
@@ -1,11 +1,13 @@
import assert from 'assert';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import * as zhc from 'zigbee-herdsman-converters';
import logger from '../util/logger';
import * as settings from '../util/settings';
import utils, {isNumericExpose, isBinaryExpose, isEnumExpose, assertBinaryExpose, assertNumericExpose, assertEnumExpose} from '../util/utils';
import utils, {assertBinaryExpose, assertEnumExpose, assertNumericExpose, isBinaryExpose, isEnumExpose, isNumericExpose} from '../util/utils';
import Extension from './extension';
interface MockProperty {
@@ -13,7 +15,6 @@ interface MockProperty {
value: KeyValue | string | null;
}
// eslint-disable-next-line camelcase
interface DiscoveryEntry {
mockProperties: MockProperty[];
type: string;
@@ -379,7 +380,6 @@ class Bridge {
homeassistant?: KeyValue;
};
/* eslint-disable brace-style */
get ID(): string {
return this.coordinatorIeeeAddress;
}
@@ -417,7 +417,6 @@ class Bridge {
isGroup(): this is Group {
return false;
}
/* eslint-enable brace-style */
}
/**
@@ -1528,7 +1527,7 @@ export default class HomeAssistant extends Extension {
});
});
if (isDevice && entity.options.hasOwnProperty('legacy') && !entity.options.legacy) {
if (isDevice && entity.options.legacy !== undefined && !entity.options.legacy) {
configs = configs.filter((c) => c !== SENSOR_CLICK);
}
@@ -1541,7 +1540,7 @@ export default class HomeAssistant extends Extension {
if (entity.options.homeassistant) {
const s = entity.options.homeassistant;
configs = configs.filter((config) => !s.hasOwnProperty(config.object_id) || s[config.object_id] != null);
configs = configs.filter((config) => s[config.object_id] === undefined || s[config.object_id] != null);
configs.forEach((config) => {
const configOverride = s[config.object_id];
if (configOverride) {
@@ -1563,7 +1562,7 @@ export default class HomeAssistant extends Extension {
return;
} else if (
isDevice &&
(!entity.definition || entity.zh.interviewing || (entity.options.hasOwnProperty('homeassistant') && !entity.options.homeassistant))
(!entity.definition || entity.zh.interviewing || (entity.options.homeassistant !== undefined && !entity.options.homeassistant))
) {
return;
}
@@ -1582,11 +1581,11 @@ export default class HomeAssistant extends Extension {
delete payload.state_topic_postfix;
}
if (!payload.hasOwnProperty('state_topic') || payload.state_topic) {
if (payload.state_topic === undefined || payload.state_topic) {
payload.state_topic = stateTopic;
} else {
/* istanbul ignore else */
if (payload.hasOwnProperty('state_topic')) {
if (payload.state_topic !== undefined) {
delete payload.state_topic;
}
}
@@ -1626,7 +1625,7 @@ export default class HomeAssistant extends Extension {
payload.origin = this.discoveryOrigin;
// Availability payload (can be disabled by setting `payload.availability = false`).
if (!payload.hasOwnProperty('availability') || payload.availability) {
if (payload.availability === undefined || payload.availability) {
payload.availability = [{topic: `${settings.get().mqtt.base_topic}/bridge/state`}];
if (isDevice || isGroup) {
@@ -1941,17 +1940,17 @@ export default class HomeAssistant extends Extension {
override adjustMessageBeforePublish(entity: Device | Group | Bridge, message: KeyValue): void {
this.getDiscovered(entity).mockProperties.forEach((mockProperty) => {
if (!message.hasOwnProperty(mockProperty.property)) {
if (message[mockProperty.property] === undefined) {
message[mockProperty.property] = mockProperty.value;
}
});
// Copy hue -> h, saturation -> s to make homeassistant happy
if (message.hasOwnProperty('color')) {
if (message.color.hasOwnProperty('hue')) {
if (message.color !== undefined) {
if (message.color.hue !== undefined) {
message.color.h = message.color.hue;
}
if (message.color.hasOwnProperty('saturation')) {
if (message.color.saturation !== undefined) {
message.color.s = message.color.saturation;
}
}
@@ -1977,8 +1976,8 @@ export default class HomeAssistant extends Extension {
private async publishDeviceTriggerDiscover(device: Device, key: string, value: string, force = false): Promise<void> {
const haConfig = device.options.homeassistant;
if (
device.options.hasOwnProperty('homeassistant') &&
(haConfig == null || (haConfig.hasOwnProperty('device_automation') && typeof haConfig === 'object' && haConfig.device_automation == null))
device.options.homeassistant !== undefined &&
(haConfig == null || (haConfig.device_automation !== undefined && typeof haConfig === 'object' && haConfig.device_automation == null))
) {
return;
}
+6 -5
View File
@@ -1,4 +1,5 @@
import assert from 'assert';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
@@ -67,7 +68,7 @@ export default class BridgeLegacy extends Extension {
return;
}
if (!json.hasOwnProperty('friendly_name') || !json.hasOwnProperty('options')) {
if (json.friendly_name === undefined || json.options === undefined) {
logger.error('Invalid JSON message, should contain "friendly_name" and "options"');
return;
}
@@ -227,11 +228,11 @@ export default class BridgeLegacy extends Extension {
try {
// json payload with id and friendly_name
const json = JSON.parse(message);
if (json.hasOwnProperty('id')) {
if (json.id !== undefined) {
id = json.id;
name = `group_${id}`;
}
if (json.hasOwnProperty('friendly_name')) {
if (json.friendly_name !== undefined) {
name = json.friendly_name;
}
} catch {
@@ -321,7 +322,7 @@ export default class BridgeLegacy extends Extension {
await cleanup();
} catch (error) {
logger.error(`Failed to ${lookup[action][2]} ${entity.name} (${error})`);
// eslint-disable-next-line
logger.error(`See https://www.zigbee2mqtt.io/guide/usage/mqtt_topics_and_messages.html#zigbee2mqtt-bridge-request for more info`);
await this.mqtt.publish('bridge/log', stringify({type: `device_${lookup[action][0]}_failed`, message}));
@@ -342,7 +343,7 @@ export default class BridgeLegacy extends Extension {
const option = match[1];
if (!this.supportedOptions.hasOwnProperty(option)) {
if (this.supportedOptions[option] === undefined) {
return;
}
@@ -1,5 +1,7 @@
/* istanbul ignore file */
import assert from 'assert';
import bind from 'bind-decorator';
import Device from '../../model/device';
+2 -2
View File
@@ -172,11 +172,11 @@ export default class Report extends Extension {
// Gledopto devices don't support reporting.
if (devicesNotSupportingReporting.includes(device.definition) || device.definition.vendor === 'Gledopto') return false;
if (this.enabled && device.zh.meta.hasOwnProperty('reporting') && device.zh.meta.reporting === reportKey) {
if (this.enabled && device.zh.meta.reporting !== undefined && device.zh.meta.reporting === reportKey) {
return false;
}
if (!this.enabled && !device.zh.meta.hasOwnProperty('reporting')) {
if (!this.enabled && device.zh.meta.reporting === undefined) {
return false;
}
+1
View File
@@ -1,4 +1,5 @@
/* istanbul ignore file */
import logger from '../../util/logger';
// DEPRECATED
import * as settings from '../../util/settings';
+2 -2
View File
@@ -55,7 +55,7 @@ export default class NetworkMap extends Extension {
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
/* istanbul ignore else */
if (this.legacyApi) {
if ((data.topic === this.legacyTopic || data.topic === this.legacyTopicRoutes) && this.supportedFormats.hasOwnProperty(data.message)) {
if ((data.topic === this.legacyTopic || data.topic === this.legacyTopicRoutes) && this.supportedFormats[data.message] !== undefined) {
const includeRoutes = data.topic === this.legacyTopicRoutes;
const topology = await this.networkScan(includeRoutes);
let converted = this.supportedFormats[data.message](topology);
@@ -68,7 +68,7 @@ export default class NetworkMap extends Extension {
const message = utils.parseJSON(data.message, data.message);
try {
const type = typeof message === 'object' ? message.type : message;
if (!this.supportedFormats.hasOwnProperty(type)) {
if (this.supportedFormats[type] === undefined) {
throw new Error(`Type '${type}' not supported, allowed are: ${Object.keys(this.supportedFormats)}`);
}
+12 -9
View File
@@ -1,8 +1,10 @@
import assert from 'assert';
import path from 'path';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import path from 'path';
import * as URI from 'uri-js';
import {Zcl} from 'zigbee-herdsman';
import * as zhc from 'zigbee-herdsman-converters';
@@ -27,7 +29,7 @@ function isValidUrl(url: string): boolean {
type UpdateState = 'updating' | 'idle' | 'available';
interface UpdatePayload {
update_available?: boolean;
// eslint-disable-next-line camelcase
update: {
progress?: number;
remaining?: number;
@@ -93,9 +95,10 @@ export default class OTAUpdate extends Extension {
// with only 10 - 60 seconds inbetween. It doesn't make sense to check for a new update
// each time, so this interval can be set by the user. The default is 1,440 minutes (one day).
const updateCheckInterval = settings.get().ota.update_check_interval * 1000 * 60;
const check = this.lastChecked.hasOwnProperty(data.device.ieeeAddr)
? Date.now() - this.lastChecked[data.device.ieeeAddr] > updateCheckInterval
: true;
const check =
this.lastChecked[data.device.ieeeAddr] !== undefined
? Date.now() - this.lastChecked[data.device.ieeeAddr] > updateCheckInterval
: true;
if (!check) return;
this.lastChecked[data.device.ieeeAddr] = Date.now();
@@ -185,10 +188,10 @@ export default class OTAUpdate extends Extension {
}
const message = utils.parseJSON(data.message, data.message);
const ID = (typeof message === 'object' && message.hasOwnProperty('id') ? message.id : message) as string;
const ID = (typeof message === 'object' && message['id'] !== undefined ? message.id : message) as string;
const device = this.zigbee.resolveEntity(ID);
const type = data.topic.substring(data.topic.lastIndexOf('/') + 1);
const responseData: {id: string; updateAvailable?: boolean; from?: string; to?: string} = {id: ID};
const responseData: {id: string; updateAvailable?: boolean; from?: KeyValue | null; to?: KeyValue | null} = {id: ID};
let error: string | undefined;
let errorStack: string | undefined;
@@ -288,8 +291,8 @@ export default class OTAUpdate extends Extension {
const to = await this.readSoftwareBuildIDAndDateCode(device);
const [fromS, toS] = [stringify(from_), stringify(to)];
logger.info(`Device '${device.name}' was updated from '${fromS}' to '${toS}'`);
responseData.from = from_ ? utils.toSnakeCase(from_) : null;
responseData.to = to ? utils.toSnakeCase(to) : null;
responseData.from = from_ ? utils.toSnakeCaseObject(from_) : null;
responseData.to = to ? utils.toSnakeCaseObject(to) : null;
/**
* Re-configure after reading software build ID and date code, some devices use a
* custom attribute for this (e.g. Develco SMSZB-120)
+8 -6
View File
@@ -1,6 +1,8 @@
import assert from 'assert';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import * as zhc from 'zigbee-herdsman-converters';
import * as philips from 'zigbee-herdsman-converters/lib/philips';
@@ -117,7 +119,7 @@ export default class Publish 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.
// retrieve_state == deprecated
if (re instanceof Device && result && result.hasOwnProperty('readAfterWriteTime') && re.options.retrieve_state) {
if (re instanceof Device && result && result.readAfterWriteTime !== undefined && re.options.retrieve_state) {
const convertGet = converter.convertGet;
assert(convertGet !== undefined, 'Converter has `readAfterWriteTime` but no `convertGet`');
setTimeout(() => convertGet(target, key, meta), result.readAfterWriteTime);
@@ -131,9 +133,9 @@ export default class Publish extends Extension {
* (state) is probably unnecessary.
*/
if (settings.get().homeassistant) {
const hasColorTemp = message.hasOwnProperty('color_temp');
const hasColor = message.hasOwnProperty('color');
const hasBrightness = message.hasOwnProperty('brightness');
const hasColorTemp = message.color_temp !== undefined;
const hasColor = message.color !== undefined;
const hasBrightness = message.brightness !== undefined;
const isOn = entityState.state === 'ON' ? true : false;
if (isOn && (hasColorTemp || hasColor) && !hasBrightness) {
delete message.state;
@@ -254,7 +256,7 @@ export default class Publish extends Extension {
endpointOrGroupID = localTarget.ID;
}
if (!usedConverters.hasOwnProperty(endpointOrGroupID)) usedConverters[endpointOrGroupID] = [];
if (usedConverters[endpointOrGroupID] === undefined) usedConverters[endpointOrGroupID] = [];
/* istanbul ignore next */
// Match any key if the toZigbee converter defines no key.
const converter = converters.find((c) => (!c.key || c.key.includes(key)) && (!c.endpoint || c.endpoint == endpointName));
@@ -302,7 +304,7 @@ export default class Publish extends Extension {
if (parsedTopic.type === 'set' && converter.convertSet) {
logger.debug(`Publishing '${parsedTopic.type}' '${key}' to '${re.name}'`);
const result = await converter.convertSet(localTarget, key, value, meta);
const optimistic = !entitySettings.hasOwnProperty('optimistic') || entitySettings.optimistic;
const optimistic = entitySettings.optimistic === undefined || entitySettings.optimistic;
if (result && result.state && optimistic) {
const msg = result.state;
+2
View File
@@ -1,7 +1,9 @@
import assert from 'assert';
import bind from 'bind-decorator';
import debounce from 'debounce';
import stringify from 'json-stable-stringify-without-jsonify';
import * as zhc from 'zigbee-herdsman-converters';
import logger from '../util/logger';
+2 -2
View File
@@ -1,7 +1,7 @@
/* eslint-disable brace-style */
import assert from 'assert';
import {CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
import * as zhc from 'zigbee-herdsman-converters';
import {CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
import * as settings from '../util/settings';
+4 -3
View File
@@ -1,7 +1,8 @@
import type {QoS} from 'mqtt-packet';
import bind from 'bind-decorator';
import fs from 'fs';
import bind from 'bind-decorator';
import * as mqtt from 'mqtt';
import logger from './util/logger';
@@ -74,14 +75,14 @@ export default class MQTT {
options.clientId = mqttSettings.client_id;
}
if (mqttSettings.hasOwnProperty('reject_unauthorized') && !mqttSettings.reject_unauthorized) {
if (mqttSettings.reject_unauthorized !== undefined && !mqttSettings.reject_unauthorized) {
logger.debug(`MQTT reject_unauthorized set false, ignoring certificate warnings.`);
options.rejectUnauthorized = false;
}
return new Promise((resolve, reject) => {
this.client = mqtt.connect(mqttSettings.server, options);
// @ts-ignore https://github.com/Koenkk/zigbee2mqtt/issues/9822
// https://github.com/Koenkk/zigbee2mqtt/issues/9822
this.client.stream.setMaxListeners(0);
this.eventBus.onPublishAvailability(this, this.publishStateOnline);
+2 -1
View File
@@ -1,4 +1,5 @@
import fs from 'fs';
import objectAssignDeep from 'object-assign-deep';
import data from './util/data';
@@ -88,7 +89,7 @@ class State {
}
exists(entity: Device | Group): boolean {
return this.state.hasOwnProperty(entity.ID);
return this.state[entity.ID] !== undefined;
}
get(entity: Group | Device): KeyValue {
+4 -7
View File
@@ -1,4 +1,3 @@
/* eslint-disable camelcase */
import type TypeEventBus from 'lib/eventBus';
import type TypeExtension from 'lib/extension/extension';
import type TypeDevice from 'lib/model/device';
@@ -7,17 +6,17 @@ import type TypeMQTT from 'lib/mqtt';
import type TypeState from 'lib/state';
import type TypeZigbee from 'lib/zigbee';
import type {QoS} from 'mqtt-packet';
import type * as zhc from 'zigbee-herdsman-converters';
import type {
NetworkParameters as ZHNetworkParameters,
CoordinatorVersion as ZHCoordinatorVersion,
LQI as ZHLQI,
NetworkParameters as ZHNetworkParameters,
RoutingTable as ZHRoutingTable,
RoutingTableEntry as ZHRoutingTableEntry,
} from 'zigbee-herdsman/dist/adapter/tstype';
import type * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
import type {Device as ZHDevice, Group as ZHGroup, Endpoint as ZHEndpoint} from 'zigbee-herdsman/dist/controller/model';
import type {Device as ZHDevice, Endpoint as ZHEndpoint, Group as ZHGroup} from 'zigbee-herdsman/dist/controller/model';
import type {Cluster as ZHCluster, FrameControl as ZHFrameControl} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype';
import type * as zhc from 'zigbee-herdsman-converters';
import {LogLevel} from 'lib/util/settings';
@@ -34,8 +33,7 @@ declare global {
type Extension = TypeExtension;
// Types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ExternalDefinition = zhc.Definition & {homeassistant: any};
type ExternalDefinition = zhc.Definition & {homeassistant: unknown};
interface MQTTResponse {
data: KeyValue;
status: 'error' | 'ok';
@@ -114,7 +112,6 @@ declare global {
}
// Settings
// eslint-disable camelcase
interface Settings {
homeassistant?: {
discovery_topic: string;
+2 -2
View File
@@ -3,7 +3,7 @@ declare module 'zigbee2mqtt-frontend' {
}
declare module 'connect-gzip-static' {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type RequestHandler = (req: any, res: any) => void;
import {IncomingMessage, ServerResponse} from 'http';
export type RequestHandler = (req: IncomingMessage, res: ServerResponse, finalhandler: (err: unknown) => void) => void;
export default function gzipStatic(root: string, options?: Record<string, unknown>): RequestHandler;
}
+4 -3
View File
@@ -1,8 +1,9 @@
import assert from 'assert';
import fs from 'fs';
import path from 'path';
import fx from 'mkdir-recursive';
import moment from 'moment';
import path from 'path';
import {rimrafSync} from 'rimraf';
import winston from 'winston';
@@ -108,7 +109,7 @@ class Logger {
/* istanbul ignore next */
if (this.output.includes('syslog')) {
logging += `, syslog`;
// eslint-disable-next-line
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unused-expressions
require('winston-syslog').Syslog;
const options: KeyValue = {
@@ -117,7 +118,7 @@ class Logger {
...settings.get().advanced.log_syslog,
};
if (options.hasOwnProperty('type')) {
if (options['type'] !== undefined) {
options.type = options.type.toString();
}
+47 -43
View File
@@ -1,11 +1,13 @@
import path from 'path';
import Ajv, {ValidateFunction} from 'ajv';
import objectAssignDeep from 'object-assign-deep';
import path from 'path';
import data from './data';
import schemaJson from './settings.schema.json';
import utils from './utils';
import yaml, {YAMLFileException} from './yaml';
export let schema: KeyValue = schemaJson;
schema = {};
@@ -143,63 +145,66 @@ function loadSettingsWithDefaults(): void {
'homeassistant_legacy_entity_attributes',
'homeassistant_status_topic',
]) {
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settingsWithDefaults.advanced[key] !== undefined) {
// @ts-expect-error
// @ts-expect-error ignore typing
sLegacy[key.replace('homeassistant_', '')] = _settingsWithDefaults.advanced[key];
}
}
}
const s = typeof _settingsWithDefaults.homeassistant === 'object' ? _settingsWithDefaults.homeassistant : {};
// @ts-expect-error
// @ts-expect-error ignore typing
_settingsWithDefaults.homeassistant = {};
// @ts-expect-error
// @ts-expect-error ignore typing
objectAssignDeep(_settingsWithDefaults.homeassistant, defaults, sLegacy, s);
}
if (_settingsWithDefaults.availability || _settingsWithDefaults.advanced?.availability_timeout) {
const defaults = {};
const s = typeof _settingsWithDefaults.availability === 'object' ? _settingsWithDefaults.availability : {};
// @ts-expect-error
// @ts-expect-error ignore typing
_settingsWithDefaults.availability = {};
// @ts-expect-error
// @ts-expect-error ignore typing
objectAssignDeep(_settingsWithDefaults.availability, defaults, s);
}
if (_settingsWithDefaults.frontend) {
const defaults = {port: 8080, auth_token: false};
const s = typeof _settingsWithDefaults.frontend === 'object' ? _settingsWithDefaults.frontend : {};
// @ts-expect-error
// @ts-expect-error ignore typing
_settingsWithDefaults.frontend = {};
// @ts-expect-error
// @ts-expect-error ignore typing
objectAssignDeep(_settingsWithDefaults.frontend, defaults, s);
}
if (_settings.advanced?.hasOwnProperty('baudrate') && _settings.serial?.baudrate == null) {
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settings.advanced?.baudrate !== undefined && _settings.serial?.baudrate == null) {
// @ts-expect-error ignore typing
_settingsWithDefaults.serial.baudrate = _settings.advanced.baudrate;
}
if (_settings.advanced?.hasOwnProperty('rtscts') && _settings.serial?.rtscts == null) {
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settings.advanced?.rtscts !== undefined && _settings.serial?.rtscts == null) {
// @ts-expect-error ignore typing
_settingsWithDefaults.serial.rtscts = _settings.advanced.rtscts;
}
if (_settings.advanced?.hasOwnProperty('ikea_ota_use_test_url') && _settings.ota?.ikea_ota_use_test_url == null) {
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settings.advanced?.ikea_ota_use_test_url !== undefined && _settings.ota?.ikea_ota_use_test_url == null) {
// @ts-expect-error ignore typing
_settingsWithDefaults.ota.ikea_ota_use_test_url = _settings.advanced.ikea_ota_use_test_url;
}
// @ts-expect-error
if (_settings.experimental?.hasOwnProperty('transmit_power') && _settings.advanced?.transmit_power == null) {
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settings.experimental?.transmit_power !== undefined && _settings.advanced?.transmit_power == null) {
// @ts-expect-error ignore typing
_settingsWithDefaults.advanced.transmit_power = _settings.experimental.transmit_power;
}
// @ts-expect-error
if (_settings.experimental?.hasOwnProperty('output') && _settings.advanced?.output == null) {
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settings.experimental?.output !== undefined && _settings.advanced?.output == null) {
// @ts-expect-error ignore typing
_settingsWithDefaults.advanced.output = _settings.experimental.output;
}
@@ -207,15 +212,15 @@ function loadSettingsWithDefaults(): void {
_settingsWithDefaults.advanced.log_level = 'warning';
}
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settingsWithDefaults.ban) {
// @ts-expect-error
// @ts-expect-error ignore typing
_settingsWithDefaults.blocklist.push(..._settingsWithDefaults.ban);
}
// @ts-expect-error
// @ts-expect-error ignore typing
if (_settingsWithDefaults.whitelist) {
// @ts-expect-error
// @ts-expect-error ignore typing
_settingsWithDefaults.passlist.push(..._settingsWithDefaults.whitelist);
}
}
@@ -377,14 +382,14 @@ function read(): Settings {
applyEnvironmentVariables(s);
// Read !secret MQTT username and password if set
// eslint-disable-next-line
const interpretValue = (value: any): any => {
const ref = parseValueRef(value);
if (ref) {
return yaml.read(data.joinPath(ref.filename))[ref.key];
} else {
return value;
const interpretValue = <T>(value: T): T => {
if (typeof value === 'string') {
const ref = parseValueRef(value);
if (ref) {
return yaml.read(data.joinPath(ref.filename))[ref.key];
}
}
return value;
};
if (s.mqtt?.user) {
@@ -410,7 +415,6 @@ function read(): Settings {
// Read devices/groups configuration from separate file if specified.
const readDevicesOrGroups = (type: 'devices' | 'groups'): void => {
if (typeof s[type] === 'string' || (Array.isArray(s[type]) && Array(s[type]).length > 0)) {
/* eslint-disable-line */
const files: string[] = Array.isArray(s[type]) ? s[type] : [s[type]];
s[type] = {};
for (const file of files) {
@@ -439,30 +443,30 @@ function applyEnvironmentVariables(settings: Partial<Settings>): void {
if (envVariable) {
const setting = path.reduce((acc, val) => {
// @ts-expect-error
// @ts-expect-error ignore typing
acc[val] = acc[val] || {};
// @ts-expect-error
// @ts-expect-error ignore typing
return acc[val];
}, settings);
if (type.indexOf('object') >= 0 || type.indexOf('array') >= 0) {
try {
// @ts-expect-error
// @ts-expect-error ignore typing
setting[key] = JSON.parse(envVariable);
} catch {
// @ts-expect-error
// @ts-expect-error ignore typing
setting[key] = envVariable;
}
} else if (type.indexOf('number') >= 0) {
// @ts-expect-error
// @ts-expect-error ignore typing
setting[key] = (envVariable as unknown as number) * 1;
} else if (type.indexOf('boolean') >= 0) {
// @ts-expect-error
// @ts-expect-error ignore typing
setting[key] = envVariable.toLowerCase() === 'true';
} else {
/* istanbul ignore else */
if (type.indexOf('string') >= 0) {
// @ts-expect-error
// @ts-expect-error ignore typing
setting[key] = envVariable;
}
}
@@ -523,7 +527,7 @@ export function set(path: string[], value: string | number | boolean | KeyValue)
export function apply(settings: Record<string, unknown>): boolean {
getInternalSettings(); // Ensure _settings is initialized.
// @ts-expect-error
// @ts-expect-error ignore typing
const newSettings = objectAssignDeep.noMutate(_settings, settings);
utils.removeNullPropertiesFromObject(newSettings, NULLABLE_SETTINGS);
ajvSetting(newSettings);
@@ -678,14 +682,14 @@ export function addGroup(name: string, ID?: string): GroupOptions {
// look for free ID
ID = '1';
while (settings.groups.hasOwnProperty(ID)) {
while (settings.groups[ID]) {
ID = (Number.parseInt(ID) + 1).toString();
}
} else {
// ensure provided ID is not in use
ID = ID.toString();
if (settings.groups.hasOwnProperty(ID)) {
if (settings.groups[ID]) {
throw new Error(`Group ID '${ID}' is already in use`);
}
}
+24 -22
View File
@@ -1,12 +1,13 @@
import type * as zhc from 'zigbee-herdsman-converters';
import assert from 'assert';
import equals from 'fast-deep-equal/es6';
import fs from 'fs';
import humanizeDuration from 'humanize-duration';
import path from 'path';
import vm from 'vm';
import equals from 'fast-deep-equal/es6';
import humanizeDuration from 'humanize-duration';
import data from './data';
// construct a local ISO8601 string (instead of UTC-based)
@@ -101,7 +102,7 @@ function objectIsEmpty(object: object): boolean {
function objectHasProperties(object: {[s: string]: unknown}, properties: string[]): boolean {
for (const property of properties) {
if (!object.hasOwnProperty(property)) {
if (object[property] === undefined) {
return false;
}
}
@@ -120,7 +121,7 @@ function equalsPartial(object: KeyValue, expected: KeyValue): boolean {
}
function getObjectProperty(object: KeyValue, key: string, defaultValue: unknown): unknown {
return object && object.hasOwnProperty(key) ? object[key] : defaultValue;
return object && object[key] !== undefined ? object[key] : defaultValue;
}
function getResponse(request: KeyValue | string, data: KeyValue, error?: string): MQTTResponse {
@@ -130,7 +131,7 @@ function getResponse(request: KeyValue | string, data: KeyValue, error?: string)
response.error = error;
}
if (typeof request === 'object' && request.hasOwnProperty('transaction')) {
if (typeof request === 'object' && request['transaction'] !== undefined) {
response.transaction = request.transaction;
}
@@ -210,24 +211,24 @@ function toNetworkAddressHex(value: number): string {
return `0x${'0'.repeat(4 - hex.length)}${hex}`;
}
// eslint-disable-next-line
function toSnakeCase(value: string | KeyValue): any {
if (typeof value === 'object') {
value = {...value};
for (const key of Object.keys(value)) {
const keySnakeCase = toSnakeCase(key);
if (key !== keySnakeCase) {
value[keySnakeCase] = value[key];
delete value[key];
}
function toSnakeCaseObject(value: KeyValue): KeyValue {
value = {...value};
for (const key of Object.keys(value)) {
const keySnakeCase = toSnakeCaseString(key);
assert(typeof keySnakeCase === 'string');
if (key !== keySnakeCase) {
value[keySnakeCase] = value[key];
delete value[key];
}
return value;
} else {
return value
.replace(/\.?([A-Z])/g, (x, y) => '_' + y.toLowerCase())
.replace(/^_/, '')
.replace('_i_d', '_id');
}
return value;
}
function toSnakeCaseString(value: string): string {
return value
.replace(/\.?([A-Z])/g, (x, y) => '_' + y.toLowerCase())
.replace(/^_/, '')
.replace('_i_d', '_id');
}
function charRange(start: string, stop: string): number[] {
@@ -447,7 +448,8 @@ export default {
loadModuleFromFile,
removeNullPropertiesFromObject,
toNetworkAddressHex,
toSnakeCase,
toSnakeCaseString,
toSnakeCaseObject,
isZHEndpoint,
isZHGroup,
hours,
+2 -1
View File
@@ -1,5 +1,6 @@
import equals from 'fast-deep-equal/es6';
import fs from 'fs';
import equals from 'fast-deep-equal/es6';
import yaml, {YAMLException} from 'js-yaml';
export class YAMLFileException extends YAMLException {
+4 -2
View File
@@ -1,6 +1,8 @@
import bind from 'bind-decorator';
import {randomInt} from 'crypto';
import bind from 'bind-decorator';
import stringify from 'json-stable-stringify-without-jsonify';
import {Controller} from 'zigbee-herdsman';
import * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
@@ -115,7 +117,7 @@ export default class Zigbee {
logger.debug(
`Received Zigbee message from '${device.name}', type '${data.type}', ` +
`cluster '${data.cluster}', data '${stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``) +
(data['groupID'] !== undefined ? ` with groupID ${data.groupID}` : ``) +
(device.zh.type === 'Coordinator' ? `, ignoring since it is from coordinator` : ``),
);
if (device.zh.type === 'Coordinator') return;
+299 -361
View File
File diff suppressed because it is too large Load Diff
+8 -7
View File
@@ -22,7 +22,7 @@
"scripts": {
"build": "tsc && node index.js writehash",
"build-watch": "tsc --watch",
"eslint": "eslint lib/ --max-warnings=0",
"eslint": "eslint --max-warnings=0",
"pretty:write": "prettier --write .",
"pretty:check": "prettier --check .",
"start": "node index.js",
@@ -69,6 +69,9 @@
"@babel/plugin-proposal-decorators": "^7.24.7",
"@babel/preset-env": "^7.25.4",
"@babel/preset-typescript": "^7.24.7",
"@eslint/js": "^9.9.1",
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
"@types/eslint__js": "^8.42.3",
"@types/finalhandler": "^1.2.3",
"@types/humanize-duration": "^3.27.4",
"@types/jest": "^29.5.12",
@@ -76,18 +79,16 @@
"@types/node": "^22.5.4",
"@types/object-assign-deep": "^0.4.3",
"@types/readable-stream": "4.0.15",
"@types/sd-notify": "^2.8.2",
"@types/ws": "8.5.12",
"@typescript-eslint/eslint-plugin": "^8.4.0",
"@typescript-eslint/parser": "^8.4.0",
"babel-jest": "^29.7.0",
"eslint": "^8.57.0",
"eslint": "^9.9.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^28.8.3",
"eslint-plugin-perfectionist": "^3.5.0",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"tmp": "^0.2.3",
"typescript": "^5.5.4"
"typescript": "^5.5.4",
"typescript-eslint": "^8.3.0"
},
"overrides": {
"zigbee-herdsman-converters": {
+10 -11
View File
@@ -1,14 +1,13 @@
import data from './stub/data';
import logger from './stub/logger';
import MQTT from './stub/mqtt';
import zigbeeHerdsman from './stub/zigbeeHerdsman';
import utils from '../lib/util/utils';
import * as settings from '../lib/util/settings';
import Controller from '../lib/controller';
import Availability from '../lib/extension/availability';
import flushPromises from './lib/flushPromises';
import stringify from 'json-stable-stringify-without-jsonify';
const data = require('./stub/data');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = require('./lib/flushPromises');
const Availability = require('../lib/extension/availability').default;
const stringify = require('json-stable-stringify-without-jsonify');
const utils = require('../lib/util/utils').default;
const mocks = [MQTT.publish, logger.warning, logger.info];
const devices = zigbeeHerdsman.devices;