mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-07 03:09:55 +00:00
fix(ignore): Migrate to eslint 9 (#23800)
* Eslint 9 * Automatic changes * Manual changes * Process feedback * u
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
node_modules/*
|
||||
test/
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -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
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import events from 'events';
|
||||
|
||||
import logger from './util/logger';
|
||||
|
||||
// eslint-disable-next-line
|
||||
type ListenerKey = object;
|
||||
|
||||
interface EventBusMap {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import logger from '../../util/logger';
|
||||
// DEPRECATED
|
||||
import * as settings from '../../util/settings';
|
||||
|
||||
@@ -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)}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Vendored
+4
-7
@@ -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;
|
||||
|
||||
Vendored
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Generated
+299
-361
File diff suppressed because it is too large
Load Diff
+8
-7
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user