@zwave-js/config 15.15.3 → 15.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/JsonTemplate.js +2 -2
- package/build/cjs/JsonTemplate.js.map +2 -2
- package/build/cjs/Logic.d.ts +1 -1
- package/build/cjs/Logic.js +6 -7
- package/build/cjs/Logic.js.map +2 -2
- package/build/cjs/LogicParser.d.ts +66 -84
- package/build/cjs/LogicParser.js +410 -1337
- package/build/cjs/LogicParser.js.map +3 -3
- package/build/cjs/Manufacturers.js +1 -1
- package/build/cjs/Manufacturers.js.map +2 -2
- package/build/cjs/_version.d.ts +1 -1
- package/build/cjs/_version.js +1 -1
- package/build/cjs/_version.js.map +1 -1
- package/build/cjs/devices/DeviceConfig.d.ts +3 -3
- package/build/cjs/devices/DeviceConfig.js +3 -3
- package/build/cjs/devices/DeviceConfig.js.map +2 -2
- package/build/cjs/devices/DeviceConfig.unit._test.js.map +1 -1
- package/build/cjs/devices/ParamInformation.d.ts +2 -0
- package/build/cjs/devices/ParamInformation.js +16 -2
- package/build/cjs/devices/ParamInformation.js.map +2 -2
- package/build/cjs/index.js.map +2 -2
- package/build/esm/JsonTemplate.js +2 -2
- package/build/esm/JsonTemplate.js.map +1 -1
- package/build/esm/Logic.d.ts +1 -1
- package/build/esm/Logic.d.ts.map +1 -1
- package/build/esm/Logic.js +6 -8
- package/build/esm/Logic.js.map +1 -1
- package/build/esm/LogicParser.d.ts +66 -84
- package/build/esm/LogicParser.d.ts.map +1 -1
- package/build/esm/LogicParser.js +432 -2335
- package/build/esm/LogicParser.js.map +1 -1
- package/build/esm/Manufacturers.js +1 -1
- package/build/esm/Manufacturers.js.map +1 -1
- package/build/esm/_version.d.ts +1 -1
- package/build/esm/_version.js +1 -1
- package/build/esm/devices/DeviceConfig.d.ts +3 -3
- package/build/esm/devices/DeviceConfig.d.ts.map +1 -1
- package/build/esm/devices/DeviceConfig.js +4 -3
- package/build/esm/devices/DeviceConfig.js.map +1 -1
- package/build/esm/devices/DeviceConfig.unit._test.js +1 -0
- package/build/esm/devices/DeviceConfig.unit._test.js.map +1 -1
- package/build/esm/devices/ParamInformation.d.ts +2 -0
- package/build/esm/devices/ParamInformation.d.ts.map +1 -1
- package/build/esm/devices/ParamInformation.js +17 -2
- package/build/esm/devices/ParamInformation.js.map +1 -1
- package/build/esm/index.d.ts.map +1 -1
- package/build/esm/index.js +0 -1
- package/build/esm/index.js.map +1 -1
- package/config/devices/0x003b/be468zp.json +1 -1
- package/config/devices/0x003b/be469.json +2 -3
- package/config/devices/0x003b/be469zp.json +2 -3
- package/config/devices/0x0063/55258_zw4002.json +34 -1
- package/config/devices/0x0090/918.json +101 -0
- package/config/devices/0x0090/hc620.json +4 -0
- package/config/devices/0x0148/cometz_700.json +1 -1
- package/config/devices/0x021d/{ddl240x.json → ddl240x_15hzw.json} +1 -2
- package/config/devices/0x021d/ddl240x_1hzw.json +115 -0
- package/config/devices/0x027a/zse11.json +134 -0
- package/config/devices/0x0460/qlsh-001P10.json +119 -0
- package/config/devices/0x0460/qnpl-0A112.json +4 -0
- package/config/devices/0x0460/qnsw-001P16.json +15 -0
- package/config/devices/0x0460/qnsw-001X16.json +10 -0
- package/config/devices/0x0460/templates/wave_template.json +70 -0
- package/config/devices/0x0460/{qnsw-002P16.json → wave_2pm.json} +22 -1
- package/config/devices/templates/master_template.json +1 -1
- package/config/eslint.config.mjs +24 -19
- package/package.json +10 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/devices/DeviceConfig.ts"],
|
|
4
|
-
"sourcesContent": ["import { configDir } from \"#config_dir\";\nimport {\n\tZWaveError,\n\tZWaveErrorCodes,\n\tdeflateSync,\n\tdigest,\n} from \"@zwave-js/core\";\nimport {\n\tBytes,\n\ttype JSONObject,\n\tcloneDeep,\n\tenumFilesRecursive,\n\tformatId,\n\tgetenv,\n\tnum2hex,\n\tpadVersion,\n\tpathExists,\n\tpick,\n\treadTextFile,\n\tstringify,\n\twriteTextFile,\n} from \"@zwave-js/shared\";\nimport type {\n\tReadFile,\n\tReadFileSystemInfo,\n\tWriteFile,\n} from \"@zwave-js/shared/bindings\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport JSON5 from \"json5\";\nimport path from \"pathe\";\nimport semverGt from \"semver/functions/gt.js\";\nimport { clearTemplateCache, readJsonWithTemplate } from \"../JsonTemplate.js\";\nimport type { ConfigLogger } from \"../Logger.js\";\nimport { hexKeyRegex4Digits, throwInvalidConfig } from \"../utils_safe.js\";\nimport {\n\ttype AssociationConfig,\n\tConditionalAssociationConfig,\n} from \"./AssociationConfig.js\";\nimport { type CompatConfig, ConditionalCompatConfig } from \"./CompatConfig.js\";\nimport { evaluateDeep, validateCondition } from \"./ConditionalItem.js\";\nimport {\n\ttype ConditionalPrimitive,\n\tparseConditionalPrimitive,\n} from \"./ConditionalPrimitive.js\";\nimport {\n\tConditionalDeviceMetadata,\n\ttype DeviceMetadata,\n} from \"./DeviceMetadata.js\";\nimport {\n\tConditionalEndpointConfig,\n\ttype EndpointConfig,\n} from \"./EndpointConfig.js\";\nimport {\n\ttype ConditionalParamInfoMap,\n\ttype ParamInfoMap,\n\ttype ParamInformation,\n\tparseConditionalParamInformationMap,\n} from \"./ParamInformation.js\";\nimport { ConditionalSceneConfig, type SceneConfig } from \"./SceneConfig.js\";\nimport type { DeviceID, FirmwareVersionRange } from \"./shared.js\";\n\nexport interface DeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport interface FulltextDeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tmanufacturer: string;\n\tlabel: string;\n\tdescription: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport const embeddedDevicesDir = path.join(configDir, \"devices\");\nconst fulltextIndexPath = path.join(embeddedDevicesDir, \"fulltext_index.json\");\n\nexport function getDevicesPaths(configDir: string): {\n\tdevicesDir: string;\n\tindexPath: string;\n} {\n\tconst devicesDir = path.join(configDir, \"devices\");\n\tconst indexPath = path.join(devicesDir, \"index.json\");\n\treturn { devicesDir, indexPath };\n}\n\nexport type DeviceConfigIndex = DeviceConfigIndexEntry[];\nexport type FulltextDeviceConfigIndex = FulltextDeviceConfigIndexEntry[];\n\nasync function hasChangedDeviceFiles(\n\tfs: ReadFileSystemInfo,\n\tdevicesRoot: string,\n\tdir: string,\n\tlastChange: Date,\n): Promise<boolean> {\n\t// Check if there are any files BUT index.json that were changed\n\t// or directories that were modified\n\tconst filesAndDirs = await fs.readDir(dir);\n\tfor (const f of filesAndDirs) {\n\t\tconst fullPath = path.join(dir, f);\n\n\t\tconst stat = await fs.stat(fullPath);\n\t\tif (\n\t\t\t(dir !== devicesRoot || f !== \"index.json\")\n\t\t\t&& (stat.isFile() || stat.isDirectory())\n\t\t\t&& stat.mtime > lastChange\n\t\t) {\n\t\t\treturn true;\n\t\t} else if (stat.isDirectory()) {\n\t\t\t// we need to go deeper!\n\t\t\tif (\n\t\t\t\tawait hasChangedDeviceFiles(\n\t\t\t\t\tfs,\n\t\t\t\t\tdevicesRoot,\n\t\t\t\t\tfullPath,\n\t\t\t\t\tlastChange,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Read all device config files from a given directory and return them as index entries.\n * Does not update the index itself.\n */\nasync function generateIndex<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdevicesDir: string,\n\tisEmbedded: boolean,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string; rootDir?: string })[]> {\n\tconst index: (T & { filename: string; rootDir?: string })[] = [];\n\n\tclearTemplateCache();\n\tconst configFiles = await enumFilesRecursive(\n\t\tfs,\n\t\tdevicesDir,\n\t\t(file) =>\n\t\t\tfile.endsWith(\".json\")\n\t\t\t&& !file.endsWith(\"index.json\")\n\t\t\t&& !file.includes(\"/templates/\")\n\t\t\t&& !file.includes(\"\\\\templates\\\\\"),\n\t);\n\n\t// Add the embedded devices dir as a fallback if necessary\n\tconst fallbackDirs = devicesDir !== embeddedDevicesDir\n\t\t? [embeddedDevicesDir]\n\t\t: undefined;\n\n\tfor (const file of configFiles) {\n\t\tconst relativePath = path\n\t\t\t.relative(devicesDir, file)\n\t\t\t.replaceAll(\"\\\\\", \"/\");\n\t\t// Try parsing the file\n\t\ttry {\n\t\t\tconst config = await DeviceConfig.from(\n\t\t\t\tfs,\n\t\t\t\tfile,\n\t\t\t\tisEmbedded,\n\t\t\t\t{\n\t\t\t\t\trootDir: devicesDir,\n\t\t\t\t\tfallbackDirs,\n\t\t\t\t\trelative: true,\n\t\t\t\t},\n\t\t\t);\n\t\t\t// Add the file to the index\n\t\t\tindex.push(\n\t\t\t\t...extractIndexEntries(config).map((entry) => {\n\t\t\t\t\tconst ret: T & { filename: string; rootDir?: string } = {\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tfilename: relativePath,\n\t\t\t\t\t};\n\t\t\t\t\t// Only add the root dir to the index if necessary\n\t\t\t\t\tif (devicesDir !== embeddedDevicesDir) {\n\t\t\t\t\t\tret.rootDir = devicesDir;\n\t\t\t\t\t}\n\t\t\t\t\treturn ret;\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tconst message = `Error parsing config file ${relativePath}: ${\n\t\t\t\t(e as Error).message\n\t\t\t}`;\n\t\t\t// Crash hard during tests, just print an error when in production systems.\n\t\t\t// A user could have changed a config file\n\t\t\tif (process.env.NODE_ENV === \"test\" || !!getenv(\"CI\")) {\n\t\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.Config_Invalid);\n\t\t\t} else {\n\t\t\t\tlogger?.print(message, \"error\");\n\t\t\t}\n\t\t}\n\t}\n\n\treturn index;\n}\n\nasync function loadDeviceIndexShared<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tdevicesDir: string,\n\tindexPath: string,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string })[]> {\n\t// The index file needs to be regenerated if it does not exist\n\tlet needsUpdate = !(await pathExists(fs, indexPath));\n\tlet index: (T & { filename: string })[] | undefined;\n\tlet mtimeIndex: Date | undefined;\n\t// ...or if cannot be parsed\n\tif (!needsUpdate) {\n\t\ttry {\n\t\t\tconst fileContents = await readTextFile(fs, indexPath, \"utf8\");\n\t\t\tindex = JSON5.parse(fileContents);\n\t\t\tmtimeIndex = (await fs.stat(indexPath)).mtime;\n\t\t} catch {\n\t\t\tlogger?.print(\n\t\t\t\t\"Error while parsing index file - regenerating...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tneedsUpdate = true;\n\t\t} finally {\n\t\t\tif (!index) {\n\t\t\t\tlogger?.print(\n\t\t\t\t\t\"Index file was malformed - regenerating...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tneedsUpdate = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ...or if there were any changes in the file system\n\tif (!needsUpdate) {\n\t\tneedsUpdate = await hasChangedDeviceFiles(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\tdevicesDir,\n\t\t\tmtimeIndex!,\n\t\t);\n\t\tif (needsUpdate) {\n\t\t\tlogger?.print(\n\t\t\t\t\"Device configuration files on disk changed - regenerating index...\",\n\t\t\t\t\"verbose\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (needsUpdate) {\n\t\t// Read all files from disk and generate an index\n\t\tindex = await generateIndex(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\ttrue,\n\t\t\textractIndexEntries,\n\t\t\tlogger,\n\t\t);\n\t\t// Save the index to disk\n\t\ttry {\n\t\t\tawait writeTextFile(\n\t\t\t\tfs,\n\t\t\t\tpath.join(indexPath),\n\t\t\t\t`// This file is auto-generated. DO NOT edit it by hand if you don't know what you're doing!\"\n${stringify(index, \"\\t\")}\n`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tlogger?.print(\"Device index regenerated\", \"verbose\");\n\t\t} catch (e) {\n\t\t\tlogger?.print(\n\t\t\t\t`Writing the device index to disk failed: ${\n\t\t\t\t\t(e as Error).message\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn index!;\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function generatePriorityDeviceIndex(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdeviceConfigPriorityDir: string,\n\tlogger?: ConfigLogger,\n): Promise<DeviceConfigIndex> {\n\treturn (\n\t\tawait generateIndex(\n\t\t\tfs,\n\t\t\tdeviceConfigPriorityDir,\n\t\t\tfalse,\n\t\t\t(config) =>\n\t\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\t\tmanufacturerId: formatId(\n\t\t\t\t\t\tconfig.manufacturerId.toString(16),\n\t\t\t\t\t),\n\t\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\t\tlabel: config.label,\n\t\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\t\trootDir: deviceConfigPriorityDir,\n\t\t\t\t})),\n\t\t\tlogger,\n\t\t)\n\t).map(({ filename, ...entry }) => ({\n\t\t...entry,\n\t\t// The generated index makes the filenames relative to the given directory\n\t\t// but we need them to be absolute\n\t\tfilename: path.join(deviceConfigPriorityDir, filename),\n\t}));\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n\texternalConfigDir?: string,\n): Promise<DeviceConfigIndex> {\n\tconst { devicesDir, indexPath } = getDevicesPaths(\n\t\texternalConfigDir || configDir,\n\t);\n\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tdevicesDir,\n\t\tindexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\n/**\n * @internal\n * Loads the full text index file to quickly search the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadFulltextDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n): Promise<FulltextDeviceConfigIndex> {\n\t// This method is not meant to operate with the external device index!\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tembeddedDevicesDir,\n\t\tfulltextIndexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tdescription: config.description,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\trootDir: embeddedDevicesDir,\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\nfunction isHexKeyWith4Digits(val: any): val is string {\n\treturn typeof val === \"string\" && hexKeyRegex4Digits.test(val);\n}\n\nconst firmwareVersionRegex = /^\\d{1,3}\\.\\d{1,3}(\\.\\d{1,3})?$/;\nfunction isFirmwareVersion(val: any): val is string {\n\treturn (\n\t\ttypeof val === \"string\"\n\t\t&& firmwareVersionRegex.test(val)\n\t\t&& val\n\t\t\t.split(\".\")\n\t\t\t.map((str) => parseInt(str, 10))\n\t\t\t.every((num) => num >= 0 && num <= 255)\n\t);\n}\n\nconst deflateDict = Bytes.from(\n\t// Substrings appearing in the device config files in descending order of frequency\n\t// except for very short ones like 0, 1, ...\n\t// WARNING: THIS MUST NOT BE CHANGED! Doing so breaks decompressing stored hashes.\n\t[\n\t\t`\"parameterNumber\":`,\n\t\t`255`,\n\t\t`\"value\":`,\n\t\t`\"defaultValue\":`,\n\t\t`\"valueSize\":`,\n\t\t`\"maxValue\":`,\n\t\t`\"minValue\":`,\n\t\t`\"options\":`,\n\t\t`true`,\n\t\t`false`,\n\t\t`\"allowManualEntry\":`,\n\t\t`\"maxNodes\":`,\n\t\t`100`,\n\t\t`\"unsigned\":`,\n\t\t`\"paramInformation\":`,\n\t\t`\"isLifeline\":`,\n\t\t`\"seconds\"`,\n\t\t`99`,\n\t\t`127`,\n\t\t`\"%\"`,\n\t\t`65535`,\n\t\t`32767`,\n\t\t`\"minutes\"`,\n\t\t`\"endpoints\":`,\n\t\t`\"hours\"`,\n\t\t`\"multiChannel\":`,\n\t]\n\t\t.join(\"\"),\n\t\"utf8\",\n);\n\n/** This class represents a device config entry whose conditional settings have not been evaluated yet */\nexport class ConditionalDeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t},\n\t): Promise<ConditionalDeviceConfig> {\n\t\tconst { relative, rootDir } = options;\n\n\t\tconst relativePath = relative\n\t\t\t? path.relative(rootDir, filename).replaceAll(\"\\\\\", \"/\")\n\t\t\t: filename;\n\t\tconst json = await readJsonWithTemplate(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\t[\n\t\t\t\toptions.rootDir,\n\t\t\t\t...(options.fallbackDirs ?? []),\n\t\t\t],\n\t\t);\n\t\treturn new ConditionalDeviceConfig(relativePath, isEmbedded, json);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tdefinition: JSONObject,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\n\t\tif (!isHexKeyWith4Digits(definition.manufacturerId)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nmanufacturer id must be a lowercase hexadecimal number with 4 digits`,\n\t\t\t);\n\t\t}\n\t\tthis.manufacturerId = parseInt(definition.manufacturerId, 16);\n\n\t\tfor (const prop of [\"manufacturer\", \"label\", \"description\"] as const) {\n\t\t\tthis[prop] = parseConditionalPrimitive(\n\t\t\t\tfilename,\n\t\t\t\t\"string\",\n\t\t\t\tprop,\n\t\t\t\tdefinition[prop],\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\t!isArray(definition.devices)\n\t\t\t|| !(definition.devices as any[]).every(\n\t\t\t\t(dev: unknown) =>\n\t\t\t\t\tisObject(dev)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productType)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productId),\n\t\t\t)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\ndevices is malformed (not an object or type/id that is not a lowercase 4-digit hex key)`,\n\t\t\t);\n\t\t}\n\t\tthis.devices = (definition.devices as any[]).map(\n\t\t\t({ productType, productId }) => ({\n\t\t\t\tproductType: parseInt(productType, 16),\n\t\t\t\tproductId: parseInt(productId, 16),\n\t\t\t}),\n\t\t);\n\n\t\tif (\n\t\t\t!isObject(definition.firmwareVersion)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.min)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.max)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion is malformed or invalid. Must be x.y or x.y.z where x, y, and z are integers between 0 and 255`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst { min, max } = definition.firmwareVersion;\n\t\t\tif (semverGt(padVersion(min), padVersion(max))) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion.min ${min} must not be greater than firmwareVersion.max ${max}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.firmwareVersion = { min, max };\n\t\t}\n\n\t\tif (\n\t\t\tdefinition.preferred != undefined\n\t\t\t&& definition.preferred !== true\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\npreferred must be true or omitted`,\n\t\t\t);\n\t\t}\n\t\tthis.preferred = !!definition.preferred;\n\n\t\tif (definition.endpoints != undefined) {\n\t\t\tconst endpoints = new Map<number, ConditionalEndpointConfig>();\n\t\t\tif (!isObject(definition.endpoints)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nendpoints is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const [key, ep] of Object.entries(definition.endpoints)) {\n\t\t\t\tif (!/^\\d+$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric endpoint index \"${key}\" in endpoints`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst epIndex = parseInt(key, 10);\n\t\t\t\tendpoints.set(\n\t\t\t\t\tepIndex,\n\t\t\t\t\tnew ConditionalEndpointConfig(this, epIndex, ep as any),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.endpoints = endpoints;\n\t\t}\n\n\t\tif (definition.associations != undefined) {\n\t\t\tconst associations = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalAssociationConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.associations)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nassociations is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, assocDefinition] of Object.entries(\n\t\t\t\t\tdefinition.associations,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric group id \"${key}\" in associations`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tassociations.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalAssociationConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tassocDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.associations = associations;\n\t\t}\n\n\t\tif (definition.paramInformation != undefined) {\n\t\t\tthis.paramInformation = parseConditionalParamInformationMap(\n\t\t\t\tdefinition,\n\t\t\t\tthis,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.proprietary != undefined) {\n\t\t\tif (!isObject(definition.proprietary)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nproprietary is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.proprietary = definition.proprietary;\n\t\t}\n\n\t\tif (definition.compat != undefined) {\n\t\t\tif (\n\t\t\t\tisArray(definition.compat)\n\t\t\t\t&& definition.compat.every((item: any) => isObject(item))\n\t\t\t) {\n\t\t\t\t// Make sure all conditions are valid\n\t\t\t\tfor (const entry of definition.compat) {\n\t\t\t\t\tvalidateCondition(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\t`At least one entry of compat contains an`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.compat = definition.compat.map(\n\t\t\t\t\t(item: any) => new ConditionalCompatConfig(filename, item),\n\t\t\t\t);\n\t\t\t} else if (isObject(definition.compat)) {\n\t\t\t\tthis.compat = new ConditionalCompatConfig(\n\t\t\t\t\tfilename,\n\t\t\t\t\tdefinition.compat,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\ncompat must be an object or any array of conditional objects`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (definition.metadata != undefined) {\n\t\t\tif (!isObject(definition.metadata)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nmetadata is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.metadata = new ConditionalDeviceMetadata(\n\t\t\t\tfilename,\n\t\t\t\tdefinition.metadata,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.scenes != undefined) {\n\t\t\tconst scenes = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalSceneConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.scenes)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nscenes is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, sceneDefinition] of Object.entries(\n\t\t\t\t\tdefinition.scenes,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\ninvalid scene id \"${key}\" in scenes - must be a positive integer (1-255)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tif (keyNum < 1 || keyNum > 255) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nscene number ${keyNum} must be between 1 and 255`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tscenes.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalSceneConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tsceneDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.scenes = scenes;\n\t\t}\n\t}\n\n\tpublic readonly filename: string;\n\n\tpublic readonly manufacturer!: ConditionalPrimitive<string>;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label!: ConditionalPrimitive<string>;\n\tpublic readonly description!: ConditionalPrimitive<string>;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, ConditionalEndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<\n\t\tnumber,\n\t\tConditionalAssociationConfig\n\t>;\n\tpublic readonly scenes?: ReadonlyMap<number, ConditionalSceneConfig>;\n\tpublic readonly paramInformation?: ConditionalParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?:\n\t\t| ConditionalCompatConfig\n\t\t| ConditionalCompatConfig[];\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: ConditionalDeviceMetadata;\n\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\n\tpublic evaluate(deviceId?: DeviceID): DeviceConfig {\n\t\treturn new DeviceConfig(\n\t\t\tthis.filename,\n\t\t\tthis.isEmbedded,\n\t\t\tevaluateDeep(this.manufacturer, deviceId),\n\t\t\tthis.manufacturerId,\n\t\t\tevaluateDeep(this.label, deviceId),\n\t\t\tevaluateDeep(this.description, deviceId),\n\t\t\tthis.devices,\n\t\t\tthis.firmwareVersion,\n\t\t\tthis.preferred,\n\t\t\tevaluateDeep(this.endpoints, deviceId),\n\t\t\tevaluateDeep(this.associations, deviceId),\n\t\t\tevaluateDeep(this.scenes, deviceId),\n\t\t\tevaluateDeep(this.paramInformation, deviceId),\n\t\t\tthis.proprietary,\n\t\t\tevaluateDeep(this.compat, deviceId),\n\t\t\tevaluateDeep(this.metadata, deviceId),\n\t\t);\n\t}\n}\n\nexport class DeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t\tdeviceId?: DeviceID;\n\t\t},\n\t): Promise<DeviceConfig> {\n\t\tconst ret = await ConditionalDeviceConfig.from(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\tisEmbedded,\n\t\t\toptions,\n\t\t);\n\t\treturn ret.evaluate(options.deviceId);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tmanufacturer: string,\n\t\tmanufacturerId: number,\n\t\tlabel: string,\n\t\tdescription: string,\n\t\tdevices: readonly {\n\t\t\tproductType: number;\n\t\t\tproductId: number;\n\t\t}[],\n\t\tfirmwareVersion: FirmwareVersionRange,\n\t\tpreferred: boolean,\n\t\tendpoints?: ReadonlyMap<number, EndpointConfig>,\n\t\tassociations?: ReadonlyMap<number, AssociationConfig>,\n\t\tscenes?: ReadonlyMap<number, SceneConfig>,\n\t\tparamInformation?: ParamInfoMap,\n\t\tproprietary?: Record<string, unknown>,\n\t\tcompat?: CompatConfig,\n\t\tmetadata?: DeviceMetadata,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\t\tthis.manufacturer = manufacturer;\n\t\tthis.manufacturerId = manufacturerId;\n\t\tthis.label = label;\n\t\tthis.description = description;\n\t\tthis.devices = devices;\n\t\tthis.firmwareVersion = firmwareVersion;\n\t\tthis.preferred = preferred;\n\t\tthis.endpoints = endpoints;\n\t\tthis.associations = associations;\n\t\tthis.scenes = scenes;\n\t\tthis.paramInformation = paramInformation;\n\t\tthis.proprietary = proprietary;\n\t\tthis.compat = compat;\n\t\tthis.metadata = metadata;\n\t}\n\n\tpublic readonly filename: string;\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\tpublic readonly manufacturer: string;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label: string;\n\tpublic readonly description: string;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, EndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<number, AssociationConfig>;\n\tpublic readonly scenes?: ReadonlyMap<number, SceneConfig>;\n\tpublic readonly paramInformation?: ParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?: CompatConfig;\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: DeviceMetadata;\n\n\t/** Returns the association config for a given endpoint */\n\tpublic getAssociationConfigForEndpoint(\n\t\tendpointIndex: number,\n\t\tgroup: number,\n\t): AssociationConfig | undefined {\n\t\tif (endpointIndex === 0) {\n\t\t\t// The root endpoint's associations may be configured separately or as part of \"endpoints\"\n\t\t\treturn (\n\t\t\t\tthis.associations?.get(group)\n\t\t\t\t\t?? this.endpoints?.get(0)?.associations?.get(group)\n\t\t\t);\n\t\t} else {\n\t\t\t// The other endpoints can only have a configuration as part of \"endpoints\"\n\t\t\treturn this.endpoints?.get(endpointIndex)?.associations?.get(group);\n\t\t}\n\t}\n\n\tprivate getHashable(version: 0 | 1 | 2): Record<string, any> {\n\t\t// We only need to compare the information that is persisted elsewhere:\n\t\t// - config parameters\n\t\t// - functional association settings\n\t\t// - CC-related compat flags\n\n\t\tlet hashable: Record<string, any> = {\n\t\t\t// endpoints: {\n\t\t\t// \tassociations: {},\n\t\t\t// \tparamInformation: []\n\t\t\t// },\n\t\t\t// proprietary: {},\n\t\t\t// compat: {},\n\t\t};\n\n\t\tconst sortObject = (obj: Record<string, any>) => {\n\t\t\tconst ret: Record<string, any> = {};\n\t\t\tfor (const key of Object.keys(obj).sort()) {\n\t\t\t\tret[key] = obj[key];\n\t\t\t}\n\t\t\treturn ret;\n\t\t};\n\n\t\tconst cloneAssociationConfig = (a: AssociationConfig) => {\n\t\t\treturn sortObject(\n\t\t\t\tpick(a, [\"maxNodes\", \"multiChannel\", \"isLifeline\"]),\n\t\t\t);\n\t\t};\n\t\tconst cloneAssociationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ReadonlyMap<number, AssociationConfig> | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\ttarget.associations = {};\n\t\t\tfor (const [key, value] of map) {\n\t\t\t\ttarget.associations[key] = cloneAssociationConfig(value);\n\t\t\t}\n\t\t\ttarget.associations = sortObject(target.associations);\n\t\t};\n\n\t\tconst cloneParamInformationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ParamInfoMap | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\tconst getParamKey = (param: ParamInformation) =>\n\t\t\t\t`${param.parameterNumber}${\n\t\t\t\t\tparam.valueBitMask ? `[${num2hex(param.valueBitMask)}]` : \"\"\n\t\t\t\t}`;\n\t\t\ttarget.paramInformation = [...map.values()]\n\t\t\t\t.sort((a, b) => getParamKey(a).localeCompare(getParamKey(b)))\n\t\t\t\t.map((p) => cloneDeep(p));\n\t\t};\n\n\t\t// Clone associations and param information on the root (ep 0) and endpoints\n\t\t{\n\t\t\tlet ep0: Record<string, any> = {};\n\t\t\tcloneAssociationMap(ep0, this.associations);\n\t\t\tcloneParamInformationMap(ep0, this.paramInformation);\n\t\t\tep0 = sortObject(ep0);\n\n\t\t\tif (Object.keys(ep0).length > 0) {\n\t\t\t\thashable.endpoints ??= {};\n\t\t\t\thashable.endpoints[0] = ep0;\n\t\t\t}\n\t\t}\n\n\t\tif (this.endpoints) {\n\t\t\tfor (const [index, endpoint] of this.endpoints) {\n\t\t\t\tlet ep: Record<string, any> = {};\n\n\t\t\t\tcloneAssociationMap(ep, endpoint.associations);\n\t\t\t\tcloneParamInformationMap(ep, endpoint.paramInformation);\n\n\t\t\t\tep = sortObject(ep);\n\n\t\t\t\tif (Object.keys(ep).length > 0) {\n\t\t\t\t\thashable.endpoints ??= {};\n\t\t\t\t\thashable.endpoints[index] = ep;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Clone proprietary config\n\t\tif (this.proprietary && Object.keys(this.proprietary).length > 0) {\n\t\t\thashable.proprietary = sortObject({ ...this.proprietary });\n\t\t}\n\n\t\t// Clone relevant compat flags\n\t\tif (this.compat) {\n\t\t\tlet c: Record<string, any> = {};\n\n\t\t\t// Copy some simple flags over\n\t\t\tfor (\n\t\t\t\tconst prop of [\n\t\t\t\t\t\"forceSceneControllerGroupCount\",\n\t\t\t\t\t\"mapRootReportsToEndpoint\",\n\t\t\t\t\t\"mapBasicSet\",\n\t\t\t\t\t\"preserveRootApplicationCCValueIDs\",\n\t\t\t\t\t\"preserveEndpoints\",\n\t\t\t\t\t\"removeEndpoints\",\n\t\t\t\t\t\"treatMultilevelSwitchSetAsEvent\",\n\t\t\t\t] as const\n\t\t\t) {\n\t\t\t\tif (this.compat[prop] != undefined) {\n\t\t\t\t\tc[prop] = this.compat[prop];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Copy other, more complex flags\n\t\t\tif (this.compat.overrideQueries) {\n\t\t\t\tc.overrideQueries = Object.fromEntries(\n\t\t\t\t\tthis.compat.overrideQueries[\"overrides\"],\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.addCCs) {\n\t\t\t\tc.addCCs = Object.fromEntries(\n\t\t\t\t\t[...this.compat.addCCs].map(([ccId, def]) => [\n\t\t\t\t\t\tccId,\n\t\t\t\t\t\tObject.fromEntries(def.endpoints),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.removeCCs) {\n\t\t\t\tc.removeCCs = Object.fromEntries(this.compat.removeCCs);\n\t\t\t}\n\t\t\tif (this.compat.treatSetAsReport) {\n\t\t\t\tc.treatSetAsReport = [...this.compat.treatSetAsReport].sort();\n\t\t\t}\n\n\t\t\tc = sortObject(c);\n\t\t\tif (Object.keys(c).length > 0) {\n\t\t\t\thashable.compat = c;\n\t\t\t}\n\t\t}\n\n\t\tif (version > 1) {\n\t\t\t// From version 2 and on, we ignore labels and descriptions, and load them dynamically\n\t\t\tfor (\n\t\t\t\tconst ep of Object.values<Record<string, any>>(\n\t\t\t\t\thashable.endpoints ?? {},\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tfor (const param of ep.paramInformation ?? []) {\n\t\t\t\t\tdelete param.label;\n\t\t\t\t\tdelete param.description;\n\t\t\t\t\tfor (const opt of param.options ?? []) {\n\t\t\t\t\t\tdelete opt.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\thashable = sortObject(hashable);\n\t\treturn hashable;\n\t}\n\n\t/**\n\t * Returns a hash code that can be used to check whether a device config has changed enough to require a re-interview.\n\t */\n\tpublic async getHash(\n\t\tversion: 0 | 1 | 2 = DeviceConfig.maxHashVersion,\n\t): Promise<Uint8Array> {\n\t\t// Figure out what to hash\n\t\tconst hashable = this.getHashable(version);\n\n\t\t// And create a \"hash\" from it. Older versions used a non-cryptographic hash,\n\t\t// newer versions compress a subset of the config file.\n\t\tlet hash: Uint8Array;\n\t\tif (version === 0) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"md5\", buffer);\n\t\t} else if (version === 1) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"sha-256\", buffer);\n\t\t} else {\n\t\t\thash = deflateSync(\n\t\t\t\tBytes.from(JSON.stringify(hashable), \"utf8\"),\n\t\t\t\t// Try to make the hash as small as possible\n\t\t\t\t{ level: 9, dictionary: deflateDict },\n\t\t\t);\n\t\t}\n\n\t\t// Version the hash from v2 onwards, so we can change the format in the future\n\t\tconst prefixBytes = Bytes.from(`$v${version}$`, \"utf8\");\n\t\treturn Bytes.concat([prefixBytes, hash]);\n\t}\n\n\tpublic static get maxHashVersion(): 2 {\n\t\treturn 2;\n\t}\n\n\tpublic static areHashesEqual(hash: Uint8Array, other: Uint8Array): boolean {\n\t\tconst parsedHash = parseHash(hash);\n\t\tconst parsedOther = parseHash(other);\n\t\t// If one of the hashes could not be parsed, they are not equal\n\t\tif (!parsedHash || !parsedOther) return false;\n\n\t\t// For legacy hashes, we only compare the hash data. We already make sure during\n\t\t// parsing of the cache files that we only need to compare hashes of the same version,\n\t\t// so simply comparing the contents is sufficient.\n\t\tif (parsedHash.version < 2 && parsedOther.version < 2) {\n\t\t\treturn Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);\n\t\t}\n\t\t// We take care during loading to downlevel the current config hash to legacy versions if needed.\n\t\t// If we end up with just one legacy hash here, something went wrong. Just bail in that case.\n\t\tif (parsedHash.version < 2 || parsedOther.version < 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// This is a versioned hash. If both versions are equal, it's simple - just compare the hash data\n\t\tif (parsedHash.version === parsedOther.version) {\n\t\t\treturn Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);\n\t\t}\n\n\t\t// For different versions, we have to do some case by case checks. For example, a newer hash version\n\t\t// might remove or add data into the hashable, so we cannot simply convert between versions easily.\n\t\t// Implement when that is actually needed.\n\t\treturn false;\n\t}\n}\n\nfunction parseHash(hash: Uint8Array): {\n\tversion: number;\n\thashData: Uint8Array;\n} | undefined {\n\tconst hashString = Bytes.view(hash).toString(\"utf8\");\n\tconst versionMatch = hashString.match(/^\\$v(\\d+)\\$/);\n\tif (versionMatch) {\n\t\t// This is a versioned hash\n\t\tconst version = parseInt(versionMatch[1], 10);\n\t\tconst hashData = hash.subarray(\n\t\t\t// The prefix is ASCII, so this is safe to do even in the context of UTF-8\n\t\t\tversionMatch[0].length,\n\t\t);\n\t\treturn {\n\t\t\tversion,\n\t\t\thashData,\n\t\t};\n\t}\n\n\t// This is probably an unversioned legacy hash\n\tswitch (hash.length) {\n\t\tcase 16: // MD5\n\t\t\treturn {\n\t\t\t\tversion: 0,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tcase 32: // SHA-256\n\t\t\treturn {\n\t\t\t\tversion: 1,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tdefault:\n\t\t\t// This is not a valid hash\n\t\t\treturn undefined;\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { configDir } from \"#config_dir\";\nimport {\n\tZWaveError,\n\tZWaveErrorCodes,\n\tdeflateSync,\n\tdigest,\n} from \"@zwave-js/core\";\nimport {\n\tBytes,\n\ttype BytesView,\n\ttype JSONObject,\n\tcloneDeep,\n\tenumFilesRecursive,\n\tformatId,\n\tgetenv,\n\tnum2hex,\n\tpadVersion,\n\tpathExists,\n\tpick,\n\treadTextFile,\n\tstringify,\n\twriteTextFile,\n} from \"@zwave-js/shared\";\nimport type {\n\tReadFile,\n\tReadFileSystemInfo,\n\tWriteFile,\n} from \"@zwave-js/shared/bindings\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport JSON5 from \"json5\";\nimport path from \"pathe\";\nimport semverGt from \"semver/functions/gt.js\";\nimport { clearTemplateCache, readJsonWithTemplate } from \"../JsonTemplate.js\";\nimport type { ConfigLogger } from \"../Logger.js\";\nimport { hexKeyRegex4Digits, throwInvalidConfig } from \"../utils_safe.js\";\nimport {\n\ttype AssociationConfig,\n\tConditionalAssociationConfig,\n} from \"./AssociationConfig.js\";\nimport { type CompatConfig, ConditionalCompatConfig } from \"./CompatConfig.js\";\nimport { evaluateDeep, validateCondition } from \"./ConditionalItem.js\";\nimport {\n\ttype ConditionalPrimitive,\n\tparseConditionalPrimitive,\n} from \"./ConditionalPrimitive.js\";\nimport {\n\tConditionalDeviceMetadata,\n\ttype DeviceMetadata,\n} from \"./DeviceMetadata.js\";\nimport {\n\tConditionalEndpointConfig,\n\ttype EndpointConfig,\n} from \"./EndpointConfig.js\";\nimport {\n\ttype ConditionalParamInfoMap,\n\ttype ParamInfoMap,\n\ttype ParamInformation,\n\tparseConditionalParamInformationMap,\n} from \"./ParamInformation.js\";\nimport { ConditionalSceneConfig, type SceneConfig } from \"./SceneConfig.js\";\nimport type { DeviceID, FirmwareVersionRange } from \"./shared.js\";\n\nexport interface DeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport interface FulltextDeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tmanufacturer: string;\n\tlabel: string;\n\tdescription: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport const embeddedDevicesDir = path.join(configDir, \"devices\");\nconst fulltextIndexPath = path.join(embeddedDevicesDir, \"fulltext_index.json\");\n\nexport function getDevicesPaths(configDir: string): {\n\tdevicesDir: string;\n\tindexPath: string;\n} {\n\tconst devicesDir = path.join(configDir, \"devices\");\n\tconst indexPath = path.join(devicesDir, \"index.json\");\n\treturn { devicesDir, indexPath };\n}\n\nexport type DeviceConfigIndex = DeviceConfigIndexEntry[];\nexport type FulltextDeviceConfigIndex = FulltextDeviceConfigIndexEntry[];\n\nasync function hasChangedDeviceFiles(\n\tfs: ReadFileSystemInfo,\n\tdevicesRoot: string,\n\tdir: string,\n\tlastChange: Date,\n): Promise<boolean> {\n\t// Check if there are any files BUT index.json that were changed\n\t// or directories that were modified\n\tconst filesAndDirs = await fs.readDir(dir);\n\tfor (const f of filesAndDirs) {\n\t\tconst fullPath = path.join(dir, f);\n\n\t\tconst stat = await fs.stat(fullPath);\n\t\tif (\n\t\t\t(dir !== devicesRoot || f !== \"index.json\")\n\t\t\t&& (stat.isFile() || stat.isDirectory())\n\t\t\t&& stat.mtime > lastChange\n\t\t) {\n\t\t\treturn true;\n\t\t} else if (stat.isDirectory()) {\n\t\t\t// we need to go deeper!\n\t\t\tif (\n\t\t\t\tawait hasChangedDeviceFiles(\n\t\t\t\t\tfs,\n\t\t\t\t\tdevicesRoot,\n\t\t\t\t\tfullPath,\n\t\t\t\t\tlastChange,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Read all device config files from a given directory and return them as index entries.\n * Does not update the index itself.\n */\nasync function generateIndex<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdevicesDir: string,\n\tisEmbedded: boolean,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string; rootDir?: string })[]> {\n\tconst index: (T & { filename: string; rootDir?: string })[] = [];\n\n\tclearTemplateCache();\n\tconst configFiles = await enumFilesRecursive(\n\t\tfs,\n\t\tdevicesDir,\n\t\t(file) =>\n\t\t\tfile.endsWith(\".json\")\n\t\t\t&& !file.endsWith(\"index.json\")\n\t\t\t&& !file.includes(\"/templates/\")\n\t\t\t&& !file.includes(\"\\\\templates\\\\\"),\n\t);\n\n\t// Add the embedded devices dir as a fallback if necessary\n\tconst fallbackDirs = devicesDir !== embeddedDevicesDir\n\t\t? [embeddedDevicesDir]\n\t\t: undefined;\n\n\tfor (const file of configFiles) {\n\t\tconst relativePath = path\n\t\t\t.relative(devicesDir, file)\n\t\t\t.replaceAll(\"\\\\\", \"/\");\n\t\t// Try parsing the file\n\t\ttry {\n\t\t\tconst config = await DeviceConfig.from(\n\t\t\t\tfs,\n\t\t\t\tfile,\n\t\t\t\tisEmbedded,\n\t\t\t\t{\n\t\t\t\t\trootDir: devicesDir,\n\t\t\t\t\tfallbackDirs,\n\t\t\t\t\trelative: true,\n\t\t\t\t},\n\t\t\t);\n\t\t\t// Add the file to the index\n\t\t\tindex.push(\n\t\t\t\t...extractIndexEntries(config).map((entry) => {\n\t\t\t\t\tconst ret: T & { filename: string; rootDir?: string } = {\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tfilename: relativePath,\n\t\t\t\t\t};\n\t\t\t\t\t// Only add the root dir to the index if necessary\n\t\t\t\t\tif (devicesDir !== embeddedDevicesDir) {\n\t\t\t\t\t\tret.rootDir = devicesDir;\n\t\t\t\t\t}\n\t\t\t\t\treturn ret;\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tconst message = `Error parsing config file ${relativePath}: ${\n\t\t\t\t(e as Error).message\n\t\t\t}`;\n\t\t\t// Crash hard during tests, just print an error when in production systems.\n\t\t\t// A user could have changed a config file\n\t\t\tif (process.env.NODE_ENV === \"test\" || !!getenv(\"CI\")) {\n\t\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.Config_Invalid);\n\t\t\t} else {\n\t\t\t\tlogger?.print(message, \"error\");\n\t\t\t}\n\t\t}\n\t}\n\n\treturn index;\n}\n\nasync function loadDeviceIndexShared<T extends Record<string, unknown>>(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tdevicesDir: string,\n\tindexPath: string,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string })[]> {\n\t// The index file needs to be regenerated if it does not exist\n\tlet needsUpdate = !(await pathExists(fs, indexPath));\n\tlet index: (T & { filename: string })[] | undefined;\n\tlet mtimeIndex: Date | undefined;\n\t// ...or if cannot be parsed\n\tif (!needsUpdate) {\n\t\ttry {\n\t\t\tconst fileContents = await readTextFile(fs, indexPath, \"utf8\");\n\t\t\tindex = JSON5.parse(fileContents);\n\t\t\tmtimeIndex = (await fs.stat(indexPath)).mtime;\n\t\t} catch {\n\t\t\tlogger?.print(\n\t\t\t\t\"Error while parsing index file - regenerating...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tneedsUpdate = true;\n\t\t} finally {\n\t\t\tif (!index) {\n\t\t\t\tlogger?.print(\n\t\t\t\t\t\"Index file was malformed - regenerating...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tneedsUpdate = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ...or if there were any changes in the file system\n\tif (!needsUpdate) {\n\t\tneedsUpdate = await hasChangedDeviceFiles(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\tdevicesDir,\n\t\t\tmtimeIndex!,\n\t\t);\n\t\tif (needsUpdate) {\n\t\t\tlogger?.print(\n\t\t\t\t\"Device configuration files on disk changed - regenerating index...\",\n\t\t\t\t\"verbose\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (needsUpdate) {\n\t\t// Read all files from disk and generate an index\n\t\tindex = await generateIndex(\n\t\t\tfs,\n\t\t\tdevicesDir,\n\t\t\ttrue,\n\t\t\textractIndexEntries,\n\t\t\tlogger,\n\t\t);\n\t\t// Save the index to disk\n\t\ttry {\n\t\t\tawait writeTextFile(\n\t\t\t\tfs,\n\t\t\t\tpath.join(indexPath),\n\t\t\t\t`// This file is auto-generated. DO NOT edit it by hand if you don't know what you're doing!\"\n${stringify(index, \"\\t\")}\n`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tlogger?.print(\"Device index regenerated\", \"verbose\");\n\t\t} catch (e) {\n\t\t\tlogger?.print(\n\t\t\t\t`Writing the device index to disk failed: ${\n\t\t\t\t\t(e as Error).message\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn index!;\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function generatePriorityDeviceIndex(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tdeviceConfigPriorityDir: string,\n\tlogger?: ConfigLogger,\n): Promise<DeviceConfigIndex> {\n\treturn (\n\t\tawait generateIndex(\n\t\t\tfs,\n\t\t\tdeviceConfigPriorityDir,\n\t\t\tfalse,\n\t\t\t(config) =>\n\t\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\t\tmanufacturerId: formatId(\n\t\t\t\t\t\tconfig.manufacturerId.toString(16),\n\t\t\t\t\t),\n\t\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\t\tlabel: config.label,\n\t\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\t\trootDir: deviceConfigPriorityDir,\n\t\t\t\t})),\n\t\t\tlogger,\n\t\t)\n\t).map(({ filename, ...entry }) => ({\n\t\t...entry,\n\t\t// The generated index makes the filenames relative to the given directory\n\t\t// but we need them to be absolute\n\t\tfilename: path.join(deviceConfigPriorityDir, filename),\n\t}));\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n\texternalConfigDir?: string,\n): Promise<DeviceConfigIndex> {\n\tconst { devicesDir, indexPath } = getDevicesPaths(\n\t\texternalConfigDir || configDir,\n\t);\n\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tdevicesDir,\n\t\tindexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\n/**\n * @internal\n * Loads the full text index file to quickly search the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadFulltextDeviceIndexInternal(\n\tfs: ReadFileSystemInfo & ReadFile & WriteFile,\n\tlogger?: ConfigLogger,\n): Promise<FulltextDeviceConfigIndex> {\n\t// This method is not meant to operate with the external device index!\n\treturn loadDeviceIndexShared(\n\t\tfs,\n\t\tembeddedDevicesDir,\n\t\tfulltextIndexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tdescription: config.description,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\trootDir: embeddedDevicesDir,\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\nfunction isHexKeyWith4Digits(val: any): val is string {\n\treturn typeof val === \"string\" && hexKeyRegex4Digits.test(val);\n}\n\nconst firmwareVersionRegex = /^\\d{1,3}\\.\\d{1,3}(\\.\\d{1,3})?$/;\nfunction isFirmwareVersion(val: any): val is string {\n\treturn (\n\t\ttypeof val === \"string\"\n\t\t&& firmwareVersionRegex.test(val)\n\t\t&& val\n\t\t\t.split(\".\")\n\t\t\t.map((str) => parseInt(str, 10))\n\t\t\t.every((num) => num >= 0 && num <= 255)\n\t);\n}\n\nconst deflateDict = Bytes.from(\n\t// Substrings appearing in the device config files in descending order of frequency\n\t// except for very short ones like 0, 1, ...\n\t// WARNING: THIS MUST NOT BE CHANGED! Doing so breaks decompressing stored hashes.\n\t[\n\t\t`\"parameterNumber\":`,\n\t\t`255`,\n\t\t`\"value\":`,\n\t\t`\"defaultValue\":`,\n\t\t`\"valueSize\":`,\n\t\t`\"maxValue\":`,\n\t\t`\"minValue\":`,\n\t\t`\"options\":`,\n\t\t`true`,\n\t\t`false`,\n\t\t`\"allowManualEntry\":`,\n\t\t`\"maxNodes\":`,\n\t\t`100`,\n\t\t`\"unsigned\":`,\n\t\t`\"paramInformation\":`,\n\t\t`\"isLifeline\":`,\n\t\t`\"seconds\"`,\n\t\t`99`,\n\t\t`127`,\n\t\t`\"%\"`,\n\t\t`65535`,\n\t\t`32767`,\n\t\t`\"minutes\"`,\n\t\t`\"endpoints\":`,\n\t\t`\"hours\"`,\n\t\t`\"multiChannel\":`,\n\t]\n\t\t.join(\"\"),\n\t\"utf8\",\n);\n\n/** This class represents a device config entry whose conditional settings have not been evaluated yet */\nexport class ConditionalDeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t},\n\t): Promise<ConditionalDeviceConfig> {\n\t\tconst { relative, rootDir } = options;\n\n\t\tconst relativePath = relative\n\t\t\t? path.relative(rootDir, filename).replaceAll(\"\\\\\", \"/\")\n\t\t\t: filename;\n\t\tconst json = await readJsonWithTemplate(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\t[\n\t\t\t\toptions.rootDir,\n\t\t\t\t...(options.fallbackDirs ?? []),\n\t\t\t],\n\t\t);\n\t\treturn new ConditionalDeviceConfig(relativePath, isEmbedded, json);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tdefinition: JSONObject,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\n\t\tif (!isHexKeyWith4Digits(definition.manufacturerId)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nmanufacturer id must be a lowercase hexadecimal number with 4 digits`,\n\t\t\t);\n\t\t}\n\t\tthis.manufacturerId = parseInt(definition.manufacturerId, 16);\n\n\t\tfor (const prop of [\"manufacturer\", \"label\", \"description\"] as const) {\n\t\t\tthis[prop] = parseConditionalPrimitive(\n\t\t\t\tfilename,\n\t\t\t\t\"string\",\n\t\t\t\tprop,\n\t\t\t\tdefinition[prop],\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\t!isArray(definition.devices)\n\t\t\t|| !(definition.devices as any[]).every(\n\t\t\t\t(dev: unknown) =>\n\t\t\t\t\tisObject(dev)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productType)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productId),\n\t\t\t)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\ndevices is malformed (not an object or type/id that is not a lowercase 4-digit hex key)`,\n\t\t\t);\n\t\t}\n\t\tthis.devices = (definition.devices as any[]).map(\n\t\t\t({ productType, productId }) => ({\n\t\t\t\tproductType: parseInt(productType, 16),\n\t\t\t\tproductId: parseInt(productId, 16),\n\t\t\t}),\n\t\t);\n\n\t\tif (\n\t\t\t!isObject(definition.firmwareVersion)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.min)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.max)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion is malformed or invalid. Must be x.y or x.y.z where x, y, and z are integers between 0 and 255`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst { min, max } = definition.firmwareVersion;\n\t\t\tif (semverGt(padVersion(min), padVersion(max))) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion.min ${min} must not be greater than firmwareVersion.max ${max}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.firmwareVersion = { min, max };\n\t\t}\n\n\t\tif (\n\t\t\tdefinition.preferred != undefined\n\t\t\t&& definition.preferred !== true\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\npreferred must be true or omitted`,\n\t\t\t);\n\t\t}\n\t\tthis.preferred = !!definition.preferred;\n\n\t\tif (definition.endpoints != undefined) {\n\t\t\tconst endpoints = new Map<number, ConditionalEndpointConfig>();\n\t\t\tif (!isObject(definition.endpoints)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nendpoints is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const [key, ep] of Object.entries(definition.endpoints)) {\n\t\t\t\tif (!/^\\d+$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric endpoint index \"${key}\" in endpoints`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst epIndex = parseInt(key, 10);\n\t\t\t\tendpoints.set(\n\t\t\t\t\tepIndex,\n\t\t\t\t\tnew ConditionalEndpointConfig(this, epIndex, ep as any),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.endpoints = endpoints;\n\t\t}\n\n\t\tif (definition.associations != undefined) {\n\t\t\tconst associations = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalAssociationConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.associations)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nassociations is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, assocDefinition] of Object.entries(\n\t\t\t\t\tdefinition.associations,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric group id \"${key}\" in associations`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tassociations.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalAssociationConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tassocDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.associations = associations;\n\t\t}\n\n\t\tif (definition.paramInformation != undefined) {\n\t\t\tthis.paramInformation = parseConditionalParamInformationMap(\n\t\t\t\tdefinition,\n\t\t\t\tthis,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.proprietary != undefined) {\n\t\t\tif (!isObject(definition.proprietary)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nproprietary is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.proprietary = definition.proprietary;\n\t\t}\n\n\t\tif (definition.compat != undefined) {\n\t\t\tif (\n\t\t\t\tisArray(definition.compat)\n\t\t\t\t&& definition.compat.every((item: any) => isObject(item))\n\t\t\t) {\n\t\t\t\t// Make sure all conditions are valid\n\t\t\t\tfor (const entry of definition.compat) {\n\t\t\t\t\tvalidateCondition(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\t`At least one entry of compat contains an`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.compat = definition.compat.map(\n\t\t\t\t\t(item: any) => new ConditionalCompatConfig(filename, item),\n\t\t\t\t);\n\t\t\t} else if (isObject(definition.compat)) {\n\t\t\t\tthis.compat = new ConditionalCompatConfig(\n\t\t\t\t\tfilename,\n\t\t\t\t\tdefinition.compat,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\ncompat must be an object or any array of conditional objects`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (definition.metadata != undefined) {\n\t\t\tif (!isObject(definition.metadata)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nmetadata is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.metadata = new ConditionalDeviceMetadata(\n\t\t\t\tfilename,\n\t\t\t\tdefinition.metadata,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.scenes != undefined) {\n\t\t\tconst scenes = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalSceneConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.scenes)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nscenes is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, sceneDefinition] of Object.entries(\n\t\t\t\t\tdefinition.scenes,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\ninvalid scene id \"${key}\" in scenes - must be a positive integer (1-255)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tif (keyNum < 1 || keyNum > 255) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nscene number ${keyNum} must be between 1 and 255`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tscenes.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalSceneConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tsceneDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.scenes = scenes;\n\t\t}\n\t}\n\n\tpublic readonly filename: string;\n\n\tpublic readonly manufacturer!: ConditionalPrimitive<string>;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label!: ConditionalPrimitive<string>;\n\tpublic readonly description!: ConditionalPrimitive<string>;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, ConditionalEndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<\n\t\tnumber,\n\t\tConditionalAssociationConfig\n\t>;\n\tpublic readonly scenes?: ReadonlyMap<number, ConditionalSceneConfig>;\n\tpublic readonly paramInformation?: ConditionalParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?:\n\t\t| ConditionalCompatConfig\n\t\t| ConditionalCompatConfig[];\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: ConditionalDeviceMetadata;\n\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\n\tpublic evaluate(deviceId?: DeviceID): DeviceConfig {\n\t\treturn new DeviceConfig(\n\t\t\tthis.filename,\n\t\t\tthis.isEmbedded,\n\t\t\tevaluateDeep(this.manufacturer, deviceId),\n\t\t\tthis.manufacturerId,\n\t\t\tevaluateDeep(this.label, deviceId),\n\t\t\tevaluateDeep(this.description, deviceId),\n\t\t\tthis.devices,\n\t\t\tthis.firmwareVersion,\n\t\t\tthis.preferred,\n\t\t\tevaluateDeep(this.endpoints, deviceId),\n\t\t\tevaluateDeep(this.associations, deviceId),\n\t\t\tevaluateDeep(this.scenes, deviceId),\n\t\t\tevaluateDeep(this.paramInformation, deviceId),\n\t\t\tthis.proprietary,\n\t\t\tevaluateDeep(this.compat, deviceId),\n\t\t\tevaluateDeep(this.metadata, deviceId),\n\t\t);\n\t}\n}\n\nexport class DeviceConfig {\n\tpublic static async from(\n\t\tfs: ReadFileSystemInfo & ReadFile,\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t\tdeviceId?: DeviceID;\n\t\t},\n\t): Promise<DeviceConfig> {\n\t\tconst ret = await ConditionalDeviceConfig.from(\n\t\t\tfs,\n\t\t\tfilename,\n\t\t\tisEmbedded,\n\t\t\toptions,\n\t\t);\n\t\treturn ret.evaluate(options.deviceId);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tmanufacturer: string,\n\t\tmanufacturerId: number,\n\t\tlabel: string,\n\t\tdescription: string,\n\t\tdevices: readonly {\n\t\t\tproductType: number;\n\t\t\tproductId: number;\n\t\t}[],\n\t\tfirmwareVersion: FirmwareVersionRange,\n\t\tpreferred: boolean,\n\t\tendpoints?: ReadonlyMap<number, EndpointConfig>,\n\t\tassociations?: ReadonlyMap<number, AssociationConfig>,\n\t\tscenes?: ReadonlyMap<number, SceneConfig>,\n\t\tparamInformation?: ParamInfoMap,\n\t\tproprietary?: Record<string, unknown>,\n\t\tcompat?: CompatConfig,\n\t\tmetadata?: DeviceMetadata,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\t\tthis.manufacturer = manufacturer;\n\t\tthis.manufacturerId = manufacturerId;\n\t\tthis.label = label;\n\t\tthis.description = description;\n\t\tthis.devices = devices;\n\t\tthis.firmwareVersion = firmwareVersion;\n\t\tthis.preferred = preferred;\n\t\tthis.endpoints = endpoints;\n\t\tthis.associations = associations;\n\t\tthis.scenes = scenes;\n\t\tthis.paramInformation = paramInformation;\n\t\tthis.proprietary = proprietary;\n\t\tthis.compat = compat;\n\t\tthis.metadata = metadata;\n\t}\n\n\tpublic readonly filename: string;\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\tpublic readonly manufacturer: string;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label: string;\n\tpublic readonly description: string;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, EndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<number, AssociationConfig>;\n\tpublic readonly scenes?: ReadonlyMap<number, SceneConfig>;\n\tpublic readonly paramInformation?: ParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?: CompatConfig;\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: DeviceMetadata;\n\n\t/** Returns the association config for a given endpoint */\n\tpublic getAssociationConfigForEndpoint(\n\t\tendpointIndex: number,\n\t\tgroup: number,\n\t): AssociationConfig | undefined {\n\t\tif (endpointIndex === 0) {\n\t\t\t// The root endpoint's associations may be configured separately or as part of \"endpoints\"\n\t\t\treturn (\n\t\t\t\tthis.associations?.get(group)\n\t\t\t\t\t?? this.endpoints?.get(0)?.associations?.get(group)\n\t\t\t);\n\t\t} else {\n\t\t\t// The other endpoints can only have a configuration as part of \"endpoints\"\n\t\t\treturn this.endpoints?.get(endpointIndex)?.associations?.get(group);\n\t\t}\n\t}\n\n\tprivate getHashable(version: 0 | 1 | 2): Record<string, any> {\n\t\t// We only need to compare the information that is persisted elsewhere:\n\t\t// - config parameters\n\t\t// - functional association settings\n\t\t// - CC-related compat flags\n\n\t\tlet hashable: Record<string, any> = {\n\t\t\t// endpoints: {\n\t\t\t// \tassociations: {},\n\t\t\t// \tparamInformation: []\n\t\t\t// },\n\t\t\t// proprietary: {},\n\t\t\t// compat: {},\n\t\t};\n\n\t\tconst sortObject = (obj: Record<string, any>) => {\n\t\t\tconst ret: Record<string, any> = {};\n\t\t\tfor (const key of Object.keys(obj).toSorted()) {\n\t\t\t\tret[key] = obj[key];\n\t\t\t}\n\t\t\treturn ret;\n\t\t};\n\n\t\tconst cloneAssociationConfig = (a: AssociationConfig) => {\n\t\t\treturn sortObject(\n\t\t\t\tpick(a, [\"maxNodes\", \"multiChannel\", \"isLifeline\"]),\n\t\t\t);\n\t\t};\n\t\tconst cloneAssociationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ReadonlyMap<number, AssociationConfig> | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\ttarget.associations = {};\n\t\t\tfor (const [key, value] of map) {\n\t\t\t\ttarget.associations[key] = cloneAssociationConfig(value);\n\t\t\t}\n\t\t\ttarget.associations = sortObject(target.associations);\n\t\t};\n\n\t\tconst cloneParamInformationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ParamInfoMap | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\tconst getParamKey = (param: ParamInformation) =>\n\t\t\t\t`${param.parameterNumber}${\n\t\t\t\t\tparam.valueBitMask ? `[${num2hex(param.valueBitMask)}]` : \"\"\n\t\t\t\t}`;\n\t\t\ttarget.paramInformation = [...map.values()]\n\t\t\t\t.toSorted((a, b) =>\n\t\t\t\t\tgetParamKey(a).localeCompare(getParamKey(b))\n\t\t\t\t)\n\t\t\t\t.map((p) => cloneDeep(p));\n\t\t};\n\n\t\t// Clone associations and param information on the root (ep 0) and endpoints\n\t\t{\n\t\t\tlet ep0: Record<string, any> = {};\n\t\t\tcloneAssociationMap(ep0, this.associations);\n\t\t\tcloneParamInformationMap(ep0, this.paramInformation);\n\t\t\tep0 = sortObject(ep0);\n\n\t\t\tif (Object.keys(ep0).length > 0) {\n\t\t\t\thashable.endpoints ??= {};\n\t\t\t\thashable.endpoints[0] = ep0;\n\t\t\t}\n\t\t}\n\n\t\tif (this.endpoints) {\n\t\t\tfor (const [index, endpoint] of this.endpoints) {\n\t\t\t\tlet ep: Record<string, any> = {};\n\n\t\t\t\tcloneAssociationMap(ep, endpoint.associations);\n\t\t\t\tcloneParamInformationMap(ep, endpoint.paramInformation);\n\n\t\t\t\tep = sortObject(ep);\n\n\t\t\t\tif (Object.keys(ep).length > 0) {\n\t\t\t\t\thashable.endpoints ??= {};\n\t\t\t\t\thashable.endpoints[index] = ep;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Clone proprietary config\n\t\tif (this.proprietary && Object.keys(this.proprietary).length > 0) {\n\t\t\thashable.proprietary = sortObject({ ...this.proprietary });\n\t\t}\n\n\t\t// Clone relevant compat flags\n\t\tif (this.compat) {\n\t\t\tlet c: Record<string, any> = {};\n\n\t\t\t// Copy some simple flags over\n\t\t\tfor (\n\t\t\t\tconst prop of [\n\t\t\t\t\t\"forceSceneControllerGroupCount\",\n\t\t\t\t\t\"mapRootReportsToEndpoint\",\n\t\t\t\t\t\"mapBasicSet\",\n\t\t\t\t\t\"preserveRootApplicationCCValueIDs\",\n\t\t\t\t\t\"preserveEndpoints\",\n\t\t\t\t\t\"removeEndpoints\",\n\t\t\t\t\t\"treatMultilevelSwitchSetAsEvent\",\n\t\t\t\t] as const\n\t\t\t) {\n\t\t\t\tif (this.compat[prop] != undefined) {\n\t\t\t\t\tc[prop] = this.compat[prop];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Copy other, more complex flags\n\t\t\tif (this.compat.overrideQueries) {\n\t\t\t\tc.overrideQueries = Object.fromEntries(\n\t\t\t\t\tthis.compat.overrideQueries[\"overrides\"],\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.addCCs) {\n\t\t\t\tc.addCCs = Object.fromEntries(\n\t\t\t\t\t[...this.compat.addCCs].map(([ccId, def]) => [\n\t\t\t\t\t\tccId,\n\t\t\t\t\t\tObject.fromEntries(def.endpoints),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.removeCCs) {\n\t\t\t\tc.removeCCs = Object.fromEntries(this.compat.removeCCs);\n\t\t\t}\n\t\t\tif (this.compat.treatSetAsReport) {\n\t\t\t\tc.treatSetAsReport = [...this.compat.treatSetAsReport]\n\t\t\t\t\t.toSorted();\n\t\t\t}\n\n\t\t\tc = sortObject(c);\n\t\t\tif (Object.keys(c).length > 0) {\n\t\t\t\thashable.compat = c;\n\t\t\t}\n\t\t}\n\n\t\tif (version > 1) {\n\t\t\t// From version 2 and on, we ignore labels and descriptions, and load them dynamically\n\t\t\tfor (\n\t\t\t\tconst ep of Object.values<Record<string, any>>(\n\t\t\t\t\thashable.endpoints ?? {},\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tfor (const param of ep.paramInformation ?? []) {\n\t\t\t\t\tdelete param.label;\n\t\t\t\t\tdelete param.description;\n\t\t\t\t\tfor (const opt of param.options ?? []) {\n\t\t\t\t\t\tdelete opt.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\thashable = sortObject(hashable);\n\t\treturn hashable;\n\t}\n\n\t/**\n\t * Returns a hash code that can be used to check whether a device config has changed enough to require a re-interview.\n\t */\n\tpublic async getHash(\n\t\tversion: 0 | 1 | 2 = DeviceConfig.maxHashVersion,\n\t): Promise<BytesView> {\n\t\t// Figure out what to hash\n\t\tconst hashable = this.getHashable(version);\n\n\t\t// And create a \"hash\" from it. Older versions used a non-cryptographic hash,\n\t\t// newer versions compress a subset of the config file.\n\t\tlet hash: BytesView;\n\t\tif (version === 0) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"md5\", buffer);\n\t\t} else if (version === 1) {\n\t\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\t\treturn await digest(\"sha-256\", buffer);\n\t\t} else {\n\t\t\thash = deflateSync(\n\t\t\t\tBytes.from(JSON.stringify(hashable), \"utf8\"),\n\t\t\t\t// Try to make the hash as small as possible\n\t\t\t\t{ level: 9, dictionary: deflateDict },\n\t\t\t);\n\t\t}\n\n\t\t// Version the hash from v2 onwards, so we can change the format in the future\n\t\tconst prefixBytes = Bytes.from(`$v${version}$`, \"utf8\");\n\t\treturn Bytes.concat([prefixBytes, hash]);\n\t}\n\n\tpublic static get maxHashVersion(): 2 {\n\t\treturn 2;\n\t}\n\n\tpublic static areHashesEqual(hash: BytesView, other: BytesView): boolean {\n\t\tconst parsedHash = parseHash(hash);\n\t\tconst parsedOther = parseHash(other);\n\t\t// If one of the hashes could not be parsed, they are not equal\n\t\tif (!parsedHash || !parsedOther) return false;\n\n\t\t// For legacy hashes, we only compare the hash data. We already make sure during\n\t\t// parsing of the cache files that we only need to compare hashes of the same version,\n\t\t// so simply comparing the contents is sufficient.\n\t\tif (parsedHash.version < 2 && parsedOther.version < 2) {\n\t\t\treturn Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);\n\t\t}\n\t\t// We take care during loading to downlevel the current config hash to legacy versions if needed.\n\t\t// If we end up with just one legacy hash here, something went wrong. Just bail in that case.\n\t\tif (parsedHash.version < 2 || parsedOther.version < 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// This is a versioned hash. If both versions are equal, it's simple - just compare the hash data\n\t\tif (parsedHash.version === parsedOther.version) {\n\t\t\treturn Bytes.view(parsedHash.hashData).equals(parsedOther.hashData);\n\t\t}\n\n\t\t// For different versions, we have to do some case by case checks. For example, a newer hash version\n\t\t// might remove or add data into the hashable, so we cannot simply convert between versions easily.\n\t\t// Implement when that is actually needed.\n\t\treturn false;\n\t}\n}\n\nfunction parseHash(hash: BytesView): {\n\tversion: number;\n\thashData: BytesView;\n} | undefined {\n\tconst hashString = Bytes.view(hash).toString(\"utf8\");\n\tconst versionMatch = hashString.match(/^\\$v(\\d+)\\$/);\n\tif (versionMatch) {\n\t\t// This is a versioned hash\n\t\tconst version = parseInt(versionMatch[1], 10);\n\t\tconst hashData = hash.subarray(\n\t\t\t// The prefix is ASCII, so this is safe to do even in the context of UTF-8\n\t\t\tversionMatch[0].length,\n\t\t);\n\t\treturn {\n\t\t\tversion,\n\t\t\thashData,\n\t\t};\n\t}\n\n\t// This is probably an unversioned legacy hash\n\tswitch (hash.length) {\n\t\tcase 16: // MD5\n\t\t\treturn {\n\t\t\t\tversion: 0,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tcase 32: // SHA-256\n\t\t\treturn {\n\t\t\t\tversion: 1,\n\t\t\t\thashData: hash,\n\t\t\t};\n\t\tdefault:\n\t\t\t// This is not a valid hash\n\t\t\treturn undefined;\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path", "configDir", "JSON5", "semverGt"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/devices/DeviceConfig.unit._test.ts"],
|
|
4
|
-
"sourcesContent": ["// FIXME: These tests are incompatible with auto-generating the index file\n\n// import fsExtra from \"fs-extra\";\n// import path from \"path\";\n// import { ConfigManager } from \"./ConfigManager\";\n// import { configDir } from \"./utils\";\n\n// jest.mock(\"fs-extra\");\n// const readFileMock = fsExtra.readFile as jest.Mock;\n// const pathExistsMock = fsExtra.pathExists as jest.Mock;\n\n// describe(\"lib/config/Devices\", () => {\n// \tdescribe(\"lookupDevice (with missing index)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValue(false);\n// \t\t\treadFileMock.mockRejectedValue(new Error(\"File does not exist\"));\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(1, 2, 3),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(1, 2, 5),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupDevice (with missing file)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValueOnce(true).mockResolvedValue(false);\n// \t\t\treadFileMock\n// \t\t\t\t.mockResolvedValueOnce(\n// \t\t\t\t\t// Index\n// \t\t\t\t\tJSON.stringify([\n// \t\t\t\t\t\t{\n// \t\t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t\t},\n// \t\t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t]),\n// \t\t\t\t)\n// \t\t\t\t.mockRejectedValue(new Error(\"File does not exist\"));\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0023),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupManufacturer (with invalid file)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\treadFileMock\n// \t\t\t\t.mockResolvedValueOnce(\n// \t\t\t\t\t// Index\n// \t\t\t\t\tJSON.stringify([\n// \t\t\t\t\t\t{\n// \t\t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t\t},\n// \t\t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t]),\n// \t\t\t\t)\n// \t\t\t\t.mockResolvedValueOnce(`{`);\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0023),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupDevice()\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\treadFileMock.mockReset();\n// \t\t\treadFileMock.mockResolvedValueOnce(\n// \t\t\t\t// Index\n// \t\t\t\tJSON.stringify([\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t},\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0034\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\tmax: \"1.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/123456.json\",\n// \t\t\t\t\t},\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0034\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"2.0\",\n// \t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/123456-8.json\",\n// \t\t\t\t\t},\n// \t\t\t\t]),\n// \t\t\t);\n// \t\t\tpathExistsMock.mockReset();\n// \t\t\tpathExistsMock.mockResolvedValueOnce(true);\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tbeforeEach(() => {\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockClear();\n// \t\t});\n\n// \t\tit(\"tests if the corresponding file exists\", async () => {\n// \t\t\tpathExistsMock.mockResolvedValue(false);\n// \t\t\tawait configManager.lookupDevice(0x0abc, 0x0001, 0x0023);\n// \t\t\texpect(pathExistsMock).toBeCalledTimes(1);\n// \t\t\tconst expectedPath = path.join(\n// \t\t\t\tconfigDir,\n// \t\t\t\t\"devices/0x0abc/abcdef.json\",\n// \t\t\t);\n// \t\t\texpect(pathExistsMock.mock.calls[0][0]).toBe(expectedPath);\n// \t\t});\n\n// \t\tit(\"looks up the file with the correct firmware version\", async () => {\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\tawait configManager.lookupDevice(0x0abc, 0x0001, 0x0034, \"2.1\");\n// \t\t\texpect(pathExistsMock).toBeCalledTimes(1);\n// \t\t\tconst expectedPath = path.join(\n// \t\t\t\tconfigDir,\n// \t\t\t\t\"devices/0x0abc/123456-8.json\",\n// \t\t\t);\n// \t\t\texpect(pathExistsMock.mock.calls[0][0]).toBe(expectedPath);\n// \t\t});\n\n// \t\tit(\"returns the contents of a found file, parsed as JSON5\", async () => {\n// \t\t\t// The first attempt at reading the file should succeed\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\t// Return a dummy file that must be parsed as JSON5\n// \t\t\treadFileMock.mockResolvedValue(\n// \t\t\t\t`// This is a minimal valid device config\n// {\n// \t\"manufacturer\": \"Test manufacturer\",\n// \t\"manufacturerId\": \"0x0abc\",\n// \t\"label\": \"LABEL\",\n// \t\"description\": \"desc rip tion\",\n// \t\"devices\": [\n// \t\t{\n// \t\t\t\"productType\": \"0x0001\",\n// \t\t\t\"productId\": \"0x0001\"\n// \t\t}\n// \t],\n// \t\"firmwareVersion\": {\n// \t\t\"min\": \"0.0\",\n// \t\t\"max\": \"255.255\"\n// \t}\n// }`,\n// \t\t\t);\n\n// \t\t\tconst result = await configManager.lookupDevice(\n// \t\t\t\t0x0abc,\n// \t\t\t\t0x0001,\n// \t\t\t\t0x0034,\n// \t\t\t\t\"2.1\",\n// \t\t\t);\n// \t\t\texpect(result).toBeDefined();\n// \t\t\texpect(result!.manufacturer).toBe(\"Test manufacturer\");\n// \t\t});\n\n// \t\tit(\"does not throw if the JSON file is invalid\", async () => {\n// \t\t\t// The first attempt at reading the file should succeed\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\t// Return an invalid JSON file\n// \t\t\treadFileMock.mockResolvedValue(`{\"name\": }`);\n\n// \t\t\t// return undefined instead of throwing\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0034, \"2.1\"),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n// });\n"],
|
|
4
|
+
"sourcesContent": ["// oxlint-disable no-empty-file\n// FIXME: These tests are incompatible with auto-generating the index file\n\n// import fsExtra from \"fs-extra\";\n// import path from \"path\";\n// import { ConfigManager } from \"./ConfigManager\";\n// import { configDir } from \"./utils\";\n\n// jest.mock(\"fs-extra\");\n// const readFileMock = fsExtra.readFile as jest.Mock;\n// const pathExistsMock = fsExtra.pathExists as jest.Mock;\n\n// describe(\"lib/config/Devices\", () => {\n// \tdescribe(\"lookupDevice (with missing index)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValue(false);\n// \t\t\treadFileMock.mockRejectedValue(new Error(\"File does not exist\"));\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(1, 2, 3),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(1, 2, 5),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupDevice (with missing file)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValueOnce(true).mockResolvedValue(false);\n// \t\t\treadFileMock\n// \t\t\t\t.mockResolvedValueOnce(\n// \t\t\t\t\t// Index\n// \t\t\t\t\tJSON.stringify([\n// \t\t\t\t\t\t{\n// \t\t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t\t},\n// \t\t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t]),\n// \t\t\t\t)\n// \t\t\t\t.mockRejectedValue(new Error(\"File does not exist\"));\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0023),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupManufacturer (with invalid file)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\treadFileMock\n// \t\t\t\t.mockResolvedValueOnce(\n// \t\t\t\t\t// Index\n// \t\t\t\t\tJSON.stringify([\n// \t\t\t\t\t\t{\n// \t\t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t\t},\n// \t\t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t]),\n// \t\t\t\t)\n// \t\t\t\t.mockResolvedValueOnce(`{`);\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0023),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupDevice()\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\treadFileMock.mockReset();\n// \t\t\treadFileMock.mockResolvedValueOnce(\n// \t\t\t\t// Index\n// \t\t\t\tJSON.stringify([\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t},\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0034\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\tmax: \"1.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/123456.json\",\n// \t\t\t\t\t},\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0034\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"2.0\",\n// \t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/123456-8.json\",\n// \t\t\t\t\t},\n// \t\t\t\t]),\n// \t\t\t);\n// \t\t\tpathExistsMock.mockReset();\n// \t\t\tpathExistsMock.mockResolvedValueOnce(true);\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tbeforeEach(() => {\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockClear();\n// \t\t});\n\n// \t\tit(\"tests if the corresponding file exists\", async () => {\n// \t\t\tpathExistsMock.mockResolvedValue(false);\n// \t\t\tawait configManager.lookupDevice(0x0abc, 0x0001, 0x0023);\n// \t\t\texpect(pathExistsMock).toBeCalledTimes(1);\n// \t\t\tconst expectedPath = path.join(\n// \t\t\t\tconfigDir,\n// \t\t\t\t\"devices/0x0abc/abcdef.json\",\n// \t\t\t);\n// \t\t\texpect(pathExistsMock.mock.calls[0][0]).toBe(expectedPath);\n// \t\t});\n\n// \t\tit(\"looks up the file with the correct firmware version\", async () => {\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\tawait configManager.lookupDevice(0x0abc, 0x0001, 0x0034, \"2.1\");\n// \t\t\texpect(pathExistsMock).toBeCalledTimes(1);\n// \t\t\tconst expectedPath = path.join(\n// \t\t\t\tconfigDir,\n// \t\t\t\t\"devices/0x0abc/123456-8.json\",\n// \t\t\t);\n// \t\t\texpect(pathExistsMock.mock.calls[0][0]).toBe(expectedPath);\n// \t\t});\n\n// \t\tit(\"returns the contents of a found file, parsed as JSON5\", async () => {\n// \t\t\t// The first attempt at reading the file should succeed\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\t// Return a dummy file that must be parsed as JSON5\n// \t\t\treadFileMock.mockResolvedValue(\n// \t\t\t\t`// This is a minimal valid device config\n// {\n// \t\"manufacturer\": \"Test manufacturer\",\n// \t\"manufacturerId\": \"0x0abc\",\n// \t\"label\": \"LABEL\",\n// \t\"description\": \"desc rip tion\",\n// \t\"devices\": [\n// \t\t{\n// \t\t\t\"productType\": \"0x0001\",\n// \t\t\t\"productId\": \"0x0001\"\n// \t\t}\n// \t],\n// \t\"firmwareVersion\": {\n// \t\t\"min\": \"0.0\",\n// \t\t\"max\": \"255.255\"\n// \t}\n// }`,\n// \t\t\t);\n\n// \t\t\tconst result = await configManager.lookupDevice(\n// \t\t\t\t0x0abc,\n// \t\t\t\t0x0001,\n// \t\t\t\t0x0034,\n// \t\t\t\t\"2.1\",\n// \t\t\t);\n// \t\t\texpect(result).toBeDefined();\n// \t\t\texpect(result!.manufacturer).toBe(\"Test manufacturer\");\n// \t\t});\n\n// \t\tit(\"does not throw if the JSON file is invalid\", async () => {\n// \t\t\t// The first attempt at reading the file should succeed\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\t// Return an invalid JSON file\n// \t\t\treadFileMock.mockResolvedValue(`{\"name\": }`);\n\n// \t\t\t// return undefined instead of throwing\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0034, \"2.1\"),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n// });\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;AAAA;;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -14,10 +14,12 @@ export declare class ConditionalParamInformation implements ConditionalItem<Para
|
|
|
14
14
|
readonly maxValue?: number;
|
|
15
15
|
readonly unsigned?: boolean;
|
|
16
16
|
readonly defaultValue: number;
|
|
17
|
+
readonly recommendedValue?: number;
|
|
17
18
|
readonly unit?: string;
|
|
18
19
|
readonly readOnly?: true;
|
|
19
20
|
readonly writeOnly?: true;
|
|
20
21
|
readonly allowManualEntry: boolean;
|
|
22
|
+
readonly destructive?: boolean;
|
|
21
23
|
readonly options: readonly ConditionalConfigOption[];
|
|
22
24
|
readonly condition?: string;
|
|
23
25
|
evaluateCondition(deviceId?: DeviceID): ParamInformation | undefined;
|
|
@@ -93,11 +93,21 @@ Parameter #${parameterNumber} is missing defaultValue, which is required unless
|
|
|
93
93
|
Parameter #${parameterNumber} has a non-numeric property defaultValue`);
|
|
94
94
|
}
|
|
95
95
|
this.defaultValue = definition.defaultValue;
|
|
96
|
+
if (definition.recommendedValue != void 0 && typeof definition.recommendedValue !== "number") {
|
|
97
|
+
(0, import_utils_safe.throwInvalidConfig)("devices", `packages/config/config/devices/${parent.filename}:
|
|
98
|
+
Parameter #${parameterNumber} has a non-numeric property recommendedValue`);
|
|
99
|
+
}
|
|
100
|
+
this.recommendedValue = definition.recommendedValue;
|
|
96
101
|
if (definition.allowManualEntry != void 0 && definition.allowManualEntry !== false) {
|
|
97
102
|
(0, import_utils_safe.throwInvalidConfig)("devices", `packages/config/config/devices/${parent.filename}:
|
|
98
103
|
Parameter #${parameterNumber}: allowManualEntry must be false or omitted!`);
|
|
99
104
|
}
|
|
100
|
-
this.allowManualEntry = definition.allowManualEntry ??
|
|
105
|
+
this.allowManualEntry = definition.allowManualEntry ?? !this.readOnly;
|
|
106
|
+
if (definition.destructive != void 0 && typeof definition.destructive !== "boolean") {
|
|
107
|
+
(0, import_utils_safe.throwInvalidConfig)("devices", `packages/config/config/devices/${parent.filename}:
|
|
108
|
+
Parameter #${parameterNumber} has a non-boolean property destructive`);
|
|
109
|
+
}
|
|
110
|
+
this.destructive = definition.destructive;
|
|
101
111
|
if ((0, import_typeguards.isArray)(definition.options) && !definition.options.every((opt) => (0, import_typeguards.isObject)(opt) && typeof opt.label === "string" && typeof opt.value === "number")) {
|
|
102
112
|
(0, import_utils_safe.throwInvalidConfig)("devices", `packages/config/config/devices/${parent.filename}:
|
|
103
113
|
Parameter #${parameterNumber}: options is malformed!`);
|
|
@@ -114,10 +124,12 @@ Parameter #${parameterNumber}: options is malformed!`);
|
|
|
114
124
|
maxValue;
|
|
115
125
|
unsigned;
|
|
116
126
|
defaultValue;
|
|
127
|
+
recommendedValue;
|
|
117
128
|
unit;
|
|
118
129
|
readOnly;
|
|
119
130
|
writeOnly;
|
|
120
131
|
allowManualEntry;
|
|
132
|
+
destructive;
|
|
121
133
|
options;
|
|
122
134
|
condition;
|
|
123
135
|
evaluateCondition(deviceId) {
|
|
@@ -134,10 +146,12 @@ Parameter #${parameterNumber}: options is malformed!`);
|
|
|
134
146
|
"maxValue",
|
|
135
147
|
"unsigned",
|
|
136
148
|
"defaultValue",
|
|
149
|
+
"recommendedValue",
|
|
137
150
|
"unit",
|
|
138
151
|
"readOnly",
|
|
139
152
|
"writeOnly",
|
|
140
|
-
"allowManualEntry"
|
|
153
|
+
"allowManualEntry",
|
|
154
|
+
"destructive"
|
|
141
155
|
]),
|
|
142
156
|
options: (0, import_ConditionalItem.evaluateDeep)(this.options, deviceId, true)
|
|
143
157
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/devices/ParamInformation.ts"],
|
|
4
|
-
"sourcesContent": ["import { tryParseParamNumber } from \"@zwave-js/core\";\nimport {\n\ttype JSONObject,\n\tObjectKeyMap,\n\ttype ReadonlyObjectKeyMap,\n\tpick,\n} from \"@zwave-js/shared\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport { throwInvalidConfig } from \"../utils_safe.js\";\nimport {\n\ttype ConditionalItem,\n\tconditionApplies,\n\tevaluateDeep,\n\tvalidateCondition,\n} from \"./ConditionalItem.js\";\nimport type { ConditionalDeviceConfig } from \"./DeviceConfig.js\";\nimport type { DeviceID } from \"./shared.js\";\n\nexport class ConditionalParamInformation\n\timplements ConditionalItem<ParamInformation>\n{\n\tpublic constructor(\n\t\tparent: ConditionalDeviceConfig,\n\t\tparameterNumber: number,\n\t\tvalueBitMask: number | undefined,\n\t\tdefinition: JSONObject,\n\t) {\n\t\tthis.parent = parent;\n\t\tthis.parameterNumber = parameterNumber;\n\t\tthis.valueBitMask = valueBitMask;\n\t\t// No need to validate here, this should be done one level higher\n\t\tthis.condition = definition.$if;\n\n\t\tif (typeof definition.label !== \"string\") {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-string label`,\n\t\t\t);\n\t\t}\n\t\tthis.label = definition.label;\n\n\t\tif (\n\t\t\tdefinition.description != undefined\n\t\t\t&& typeof definition.description !== \"string\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-string description`,\n\t\t\t);\n\t\t}\n\t\tthis.description = definition.description;\n\n\t\tif (\n\t\t\ttypeof definition.valueSize !== \"number\"\n\t\t\t|| definition.valueSize <= 0\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has an invalid value size`,\n\t\t\t);\n\t\t}\n\t\tthis.valueSize = definition.valueSize;\n\n\t\tif (\n\t\t\tdefinition.minValue != undefined\n\t\t\t&& typeof definition.minValue !== \"number\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-numeric property minValue`,\n\t\t\t);\n\t\t}\n\t\tthis.minValue = definition.minValue;\n\n\t\tif (\n\t\t\tdefinition.maxValue != undefined\n\t\t\t&& typeof definition.maxValue !== \"number\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-numeric property maxValue`,\n\t\t\t);\n\t\t}\n\t\tthis.maxValue = definition.maxValue;\n\n\t\tif (\n\t\t\tdefinition.unsigned != undefined\n\t\t\t&& typeof definition.unsigned !== \"boolean\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-boolean property unsigned`,\n\t\t\t);\n\t\t}\n\t\tthis.unsigned = definition.unsigned === true;\n\n\t\tif (\n\t\t\tdefinition.unit != undefined\n\t\t\t&& typeof definition.unit !== \"string\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-string unit`,\n\t\t\t);\n\t\t}\n\t\tthis.unit = definition.unit;\n\n\t\tif (definition.readOnly != undefined && definition.readOnly !== true) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\n\t\tParameter #${parameterNumber}: readOnly must true or omitted!`,\n\t\t\t);\n\t\t}\n\t\tthis.readOnly = definition.readOnly;\n\n\t\tif (\n\t\t\tdefinition.writeOnly != undefined\n\t\t\t&& definition.writeOnly !== true\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\n\t\tParameter #${parameterNumber}: writeOnly must be true or omitted!`,\n\t\t\t);\n\t\t}\n\t\tthis.writeOnly = definition.writeOnly;\n\n\t\tif (definition.defaultValue == undefined) {\n\t\t\tif (!this.readOnly) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"devices\",\n\t\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} is missing defaultValue, which is required unless the parameter is readOnly`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (typeof definition.defaultValue !== \"number\") {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-numeric property defaultValue`,\n\t\t\t);\n\t\t}\n\t\tthis.defaultValue = definition.defaultValue;\n\n\t\tif (\n\t\t\tdefinition.allowManualEntry != undefined\n\t\t\t&& definition.allowManualEntry !== false\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber}: allowManualEntry must be false or omitted!`,\n\t\t\t);\n\t\t}\n\t\t// Default to allowing manual entry, except if the param is readonly\n\t\tthis.allowManualEntry = definition.allowManualEntry\n\t\t\t?? (this.readOnly ? false : true);\n\n\t\tif (\n\t\t\tisArray(definition.options)\n\t\t\t&& !definition.options.every(\n\t\t\t\t(opt: unknown) =>\n\t\t\t\t\tisObject(opt)\n\t\t\t\t\t&& typeof opt.label === \"string\"\n\t\t\t\t\t&& typeof opt.value === \"number\",\n\t\t\t)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber}: options is malformed!`,\n\t\t\t);\n\t\t}\n\n\t\tthis.options = definition.options?.map(\n\t\t\t(opt: any) =>\n\t\t\t\tnew ConditionalConfigOption(opt.value, opt.label, opt.$if),\n\t\t) ?? [];\n\t}\n\n\tprivate parent: ConditionalDeviceConfig;\n\tpublic readonly parameterNumber: number;\n\tpublic readonly valueBitMask?: number;\n\tpublic readonly label: string;\n\tpublic readonly description?: string;\n\tpublic readonly valueSize: number;\n\tpublic readonly minValue?: number;\n\tpublic readonly maxValue?: number;\n\tpublic readonly unsigned?: boolean;\n\tpublic readonly defaultValue: number;\n\tpublic readonly unit?: string;\n\tpublic readonly readOnly?: true;\n\tpublic readonly writeOnly?: true;\n\tpublic readonly allowManualEntry: boolean;\n\tpublic readonly options: readonly ConditionalConfigOption[];\n\n\tpublic readonly condition?: string;\n\n\tpublic evaluateCondition(\n\t\tdeviceId?: DeviceID,\n\t): ParamInformation | undefined {\n\t\tif (!conditionApplies(this, deviceId)) return;\n\n\t\tconst ret = {\n\t\t\t...pick(this, [\n\t\t\t\t\"parameterNumber\",\n\t\t\t\t\"valueBitMask\",\n\t\t\t\t\"label\",\n\t\t\t\t\"description\",\n\t\t\t\t\"valueSize\",\n\t\t\t\t\"minValue\",\n\t\t\t\t\"maxValue\",\n\t\t\t\t\"unsigned\",\n\t\t\t\t\"defaultValue\",\n\t\t\t\t\"unit\",\n\t\t\t\t\"readOnly\",\n\t\t\t\t\"writeOnly\",\n\t\t\t\t\"allowManualEntry\",\n\t\t\t]),\n\t\t\toptions: evaluateDeep(this.options, deviceId, true),\n\t\t};\n\t\t// Infer minValue from options if possible\n\t\tif (ret.minValue == undefined) {\n\t\t\tif (ret.allowManualEntry === false && ret.options.length > 0) {\n\t\t\t\tret.minValue = Math.min(...ret.options.map((o) => o.value));\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"devices\",\n\t\t\t\t\t`packages/config/config/devices/${this.parent.filename}:\nParameter #${this.parameterNumber} is missing required property \"minValue\"!`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (ret.maxValue == undefined) {\n\t\t\tif (ret.allowManualEntry === false && ret.options.length > 0) {\n\t\t\t\tret.maxValue = Math.max(...ret.options.map((o) => o.value));\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"devices\",\n\t\t\t\t\t`packages/config/config/devices/${this.parent.filename}:\nParameter #${this.parameterNumber} is missing required property \"maxValue\"!`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// @ts-expect-error TS doesn't seem to understand that we do set min/maxValue\n\t\treturn ret;\n\t}\n}\n\nexport type ParamInformation =\n\t& Omit<\n\t\tConditionalParamInformation,\n\t\t\"condition\" | \"evaluateCondition\" | \"options\" | \"minValue\" | \"maxValue\"\n\t>\n\t& {\n\t\toptions: readonly ConfigOption[];\n\t\tminValue: NonNullable<ConditionalParamInformation[\"minValue\"]>;\n\t\tmaxValue: NonNullable<ConditionalParamInformation[\"maxValue\"]>;\n\t};\n\nexport class ConditionalConfigOption implements ConditionalItem<ConfigOption> {\n\tpublic constructor(\n\t\tpublic readonly value: number,\n\t\tpublic readonly label: string,\n\t\tpublic readonly condition?: string,\n\t) {}\n\n\tpublic evaluateCondition(deviceId?: DeviceID): ConfigOption | undefined {\n\t\tif (!conditionApplies(this, deviceId)) return;\n\n\t\treturn pick(this, [\"value\", \"label\"]);\n\t}\n}\n\nexport interface ConfigOption {\n\tvalue: number;\n\tlabel: string;\n}\n\nexport type ConditionalParamInfoMap = ReadonlyObjectKeyMap<\n\t{ parameter: number; valueBitMask?: number },\n\tConditionalParamInformation[]\n>;\n\nexport type ParamInfoMap = ReadonlyObjectKeyMap<\n\t{ parameter: number; valueBitMask?: number },\n\tParamInformation\n>;\n\nexport function parseConditionalParamInformationMap(\n\tdefinition: JSONObject,\n\tparent: ConditionalDeviceConfig,\n\terrorPrefix: string = \"\",\n): ConditionalParamInfoMap {\n\tconst paramInformation = new ObjectKeyMap<\n\t\t{ parameter: number; valueBitMask?: number },\n\t\tConditionalParamInformation[]\n\t>();\n\n\tconst filename = parent.filename;\n\n\tif (isArray(definition.paramInformation)) {\n\t\t// Check that every param has a param number\n\t\tif (!definition.paramInformation.every((entry: any) => \"#\" in entry)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}: \n${errorPrefix}required property \"#\" missing in at least one entry of paramInformation`,\n\t\t\t);\n\t\t}\n\n\t\t// And a valid $if condition\n\t\tfor (const entry of definition.paramInformation) {\n\t\t\tvalidateCondition(\n\t\t\t\tfilename,\n\t\t\t\tentry,\n\t\t\t\t`${errorPrefix}At least one entry of paramInformation contains an`,\n\t\t\t);\n\t\t}\n\n\t\tfor (const paramDefinition of definition.paramInformation) {\n\t\t\tconst { [\"#\"]: paramNo, ...defn } = paramDefinition;\n\t\t\tconst key = tryParseParamNumber(paramNo);\n\t\t\tif (!key) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}: \n${errorPrefix}found invalid param number \"${paramNo}\" in paramInformation`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!paramInformation.has(key)) paramInformation.set(key, []);\n\t\t\tparamInformation\n\t\t\t\t.get(key)!\n\t\t\t\t.push(\n\t\t\t\t\tnew ConditionalParamInformation(\n\t\t\t\t\t\tparent,\n\t\t\t\t\t\tkey.parameter,\n\t\t\t\t\t\tkey.valueBitMask,\n\t\t\t\t\t\tdefn,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t}\n\t} else if (isObject(definition.paramInformation)) {\n\t\t// Silently ignore this old format\n\t} else {\n\t\tthrowInvalidConfig(\n\t\t\t`device`,\n\t\t\t`packages/config/config/devices/${filename}:\n${errorPrefix}paramInformation must be an array!`,\n\t\t);\n\t}\n\n\treturn paramInformation;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;;;;;;;AAAA,kBAAoC;AACpC,oBAKO;AACP,wBAAkC;AAClC,wBAAmC;AACnC,6BAKO;AAID,MAAO,4BAA2B;EAlBxC,OAkBwC;;;EAGvC,YACC,QACA,iBACA,cACA,YAAsB;AAEtB,SAAK,SAAS;AACd,SAAK,kBAAkB;AACvB,SAAK,eAAe;AAEpB,SAAK,YAAY,WAAW;AAE5B,QAAI,OAAO,WAAW,UAAU,UAAU;AACzC,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,yBAAyB;IAEnD;AACA,SAAK,QAAQ,WAAW;AAExB,QACC,WAAW,eAAe,UACvB,OAAO,WAAW,gBAAgB,UACpC;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,+BAA+B;IAEzD;AACA,SAAK,cAAc,WAAW;AAE9B,QACC,OAAO,WAAW,cAAc,YAC7B,WAAW,aAAa,GAC1B;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,4BAA4B;IAEtD;AACA,SAAK,YAAY,WAAW;AAE5B,QACC,WAAW,YAAY,UACpB,OAAO,WAAW,aAAa,UACjC;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,sCAAsC;IAEhE;AACA,SAAK,WAAW,WAAW;AAE3B,QACC,WAAW,YAAY,UACpB,OAAO,WAAW,aAAa,UACjC;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,sCAAsC;IAEhE;AACA,SAAK,WAAW,WAAW;AAE3B,QACC,WAAW,YAAY,UACpB,OAAO,WAAW,aAAa,WACjC;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,sCAAsC;IAEhE;AACA,SAAK,WAAW,WAAW,aAAa;AAExC,QACC,WAAW,QAAQ,UAChB,OAAO,WAAW,SAAS,UAC7B;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,wBAAwB;IAElD;AACA,SAAK,OAAO,WAAW;AAEvB,QAAI,WAAW,YAAY,UAAa,WAAW,aAAa,MAAM;AACrE,gDACC,WACA,kCAAkC,OAAO,QAAQ;eACtC,eAAe,kCAAkC;IAE9D;AACA,SAAK,WAAW,WAAW;AAE3B,QACC,WAAW,aAAa,UACrB,WAAW,cAAc,MAC3B;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;eACtC,eAAe,sCAAsC;IAElE;AACA,SAAK,YAAY,WAAW;AAE5B,QAAI,WAAW,gBAAgB,QAAW;AACzC,UAAI,CAAC,KAAK,UAAU;AACnB,kDACC,WACA,kCAAkC,OAAO,QAAQ;aACzC,eAAe,8EAA8E;MAEvG;IACD,WAAW,OAAO,WAAW,iBAAiB,UAAU;AACvD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,0CAA0C;IAEpE;AACA,SAAK,eAAe,WAAW;AAE/B,QACC,WAAW,oBAAoB,UAC5B,WAAW,qBAAqB,OAClC;AACD,gDACC,WACA,kCAAkC,OAAO,QAAQ;aACxC,eAAe,8CAA8C;IAExE;AAEA,SAAK,mBAAmB,WAAW,
|
|
4
|
+
"sourcesContent": ["import { tryParseParamNumber } from \"@zwave-js/core\";\nimport {\n\ttype JSONObject,\n\tObjectKeyMap,\n\ttype ReadonlyObjectKeyMap,\n\tpick,\n} from \"@zwave-js/shared\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport { throwInvalidConfig } from \"../utils_safe.js\";\nimport {\n\ttype ConditionalItem,\n\tconditionApplies,\n\tevaluateDeep,\n\tvalidateCondition,\n} from \"./ConditionalItem.js\";\nimport type { ConditionalDeviceConfig } from \"./DeviceConfig.js\";\nimport type { DeviceID } from \"./shared.js\";\n\nexport class ConditionalParamInformation\n\timplements ConditionalItem<ParamInformation>\n{\n\tpublic constructor(\n\t\tparent: ConditionalDeviceConfig,\n\t\tparameterNumber: number,\n\t\tvalueBitMask: number | undefined,\n\t\tdefinition: JSONObject,\n\t) {\n\t\tthis.parent = parent;\n\t\tthis.parameterNumber = parameterNumber;\n\t\tthis.valueBitMask = valueBitMask;\n\t\t// No need to validate here, this should be done one level higher\n\t\tthis.condition = definition.$if;\n\n\t\tif (typeof definition.label !== \"string\") {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-string label`,\n\t\t\t);\n\t\t}\n\t\tthis.label = definition.label;\n\n\t\tif (\n\t\t\tdefinition.description != undefined\n\t\t\t&& typeof definition.description !== \"string\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-string description`,\n\t\t\t);\n\t\t}\n\t\tthis.description = definition.description;\n\n\t\tif (\n\t\t\ttypeof definition.valueSize !== \"number\"\n\t\t\t|| definition.valueSize <= 0\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has an invalid value size`,\n\t\t\t);\n\t\t}\n\t\tthis.valueSize = definition.valueSize;\n\n\t\tif (\n\t\t\tdefinition.minValue != undefined\n\t\t\t&& typeof definition.minValue !== \"number\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-numeric property minValue`,\n\t\t\t);\n\t\t}\n\t\tthis.minValue = definition.minValue;\n\n\t\tif (\n\t\t\tdefinition.maxValue != undefined\n\t\t\t&& typeof definition.maxValue !== \"number\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-numeric property maxValue`,\n\t\t\t);\n\t\t}\n\t\tthis.maxValue = definition.maxValue;\n\n\t\tif (\n\t\t\tdefinition.unsigned != undefined\n\t\t\t&& typeof definition.unsigned !== \"boolean\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-boolean property unsigned`,\n\t\t\t);\n\t\t}\n\t\tthis.unsigned = definition.unsigned === true;\n\n\t\tif (\n\t\t\tdefinition.unit != undefined\n\t\t\t&& typeof definition.unit !== \"string\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-string unit`,\n\t\t\t);\n\t\t}\n\t\tthis.unit = definition.unit;\n\n\t\tif (definition.readOnly != undefined && definition.readOnly !== true) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\n\t\tParameter #${parameterNumber}: readOnly must true or omitted!`,\n\t\t\t);\n\t\t}\n\t\tthis.readOnly = definition.readOnly;\n\n\t\tif (\n\t\t\tdefinition.writeOnly != undefined\n\t\t\t&& definition.writeOnly !== true\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\n\t\tParameter #${parameterNumber}: writeOnly must be true or omitted!`,\n\t\t\t);\n\t\t}\n\t\tthis.writeOnly = definition.writeOnly;\n\n\t\tif (definition.defaultValue == undefined) {\n\t\t\tif (!this.readOnly) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"devices\",\n\t\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} is missing defaultValue, which is required unless the parameter is readOnly`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (typeof definition.defaultValue !== \"number\") {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-numeric property defaultValue`,\n\t\t\t);\n\t\t}\n\t\tthis.defaultValue = definition.defaultValue;\n\n\t\tif (\n\t\t\tdefinition.recommendedValue != undefined\n\t\t\t&& typeof definition.recommendedValue !== \"number\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-numeric property recommendedValue`,\n\t\t\t);\n\t\t}\n\t\tthis.recommendedValue = definition.recommendedValue;\n\n\t\tif (\n\t\t\tdefinition.allowManualEntry != undefined\n\t\t\t&& definition.allowManualEntry !== false\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber}: allowManualEntry must be false or omitted!`,\n\t\t\t);\n\t\t}\n\t\t// Default to allowing manual entry, except if the param is readonly\n\t\tthis.allowManualEntry = definition.allowManualEntry ?? !this.readOnly;\n\n\t\tif (\n\t\t\tdefinition.destructive != undefined\n\t\t\t&& typeof definition.destructive !== \"boolean\"\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber} has a non-boolean property destructive`,\n\t\t\t);\n\t\t}\n\t\tthis.destructive = definition.destructive;\n\n\t\tif (\n\t\t\tisArray(definition.options)\n\t\t\t&& !definition.options.every(\n\t\t\t\t(opt: unknown) =>\n\t\t\t\t\tisObject(opt)\n\t\t\t\t\t&& typeof opt.label === \"string\"\n\t\t\t\t\t&& typeof opt.value === \"number\",\n\t\t\t)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"devices\",\n\t\t\t\t`packages/config/config/devices/${parent.filename}:\nParameter #${parameterNumber}: options is malformed!`,\n\t\t\t);\n\t\t}\n\n\t\tthis.options = definition.options?.map(\n\t\t\t(opt: any) =>\n\t\t\t\tnew ConditionalConfigOption(opt.value, opt.label, opt.$if),\n\t\t) ?? [];\n\t}\n\n\tprivate parent: ConditionalDeviceConfig;\n\tpublic readonly parameterNumber: number;\n\tpublic readonly valueBitMask?: number;\n\tpublic readonly label: string;\n\tpublic readonly description?: string;\n\tpublic readonly valueSize: number;\n\tpublic readonly minValue?: number;\n\tpublic readonly maxValue?: number;\n\tpublic readonly unsigned?: boolean;\n\tpublic readonly defaultValue: number;\n\tpublic readonly recommendedValue?: number;\n\tpublic readonly unit?: string;\n\tpublic readonly readOnly?: true;\n\tpublic readonly writeOnly?: true;\n\tpublic readonly allowManualEntry: boolean;\n\tpublic readonly destructive?: boolean;\n\tpublic readonly options: readonly ConditionalConfigOption[];\n\n\tpublic readonly condition?: string;\n\n\tpublic evaluateCondition(\n\t\tdeviceId?: DeviceID,\n\t): ParamInformation | undefined {\n\t\tif (!conditionApplies(this, deviceId)) return;\n\n\t\tconst ret = {\n\t\t\t...pick(this, [\n\t\t\t\t\"parameterNumber\",\n\t\t\t\t\"valueBitMask\",\n\t\t\t\t\"label\",\n\t\t\t\t\"description\",\n\t\t\t\t\"valueSize\",\n\t\t\t\t\"minValue\",\n\t\t\t\t\"maxValue\",\n\t\t\t\t\"unsigned\",\n\t\t\t\t\"defaultValue\",\n\t\t\t\t\"recommendedValue\",\n\t\t\t\t\"unit\",\n\t\t\t\t\"readOnly\",\n\t\t\t\t\"writeOnly\",\n\t\t\t\t\"allowManualEntry\",\n\t\t\t\t\"destructive\",\n\t\t\t]),\n\t\t\toptions: evaluateDeep(this.options, deviceId, true),\n\t\t};\n\t\t// Infer minValue from options if possible\n\t\tif (ret.minValue == undefined) {\n\t\t\tif (ret.allowManualEntry === false && ret.options.length > 0) {\n\t\t\t\tret.minValue = Math.min(...ret.options.map((o) => o.value));\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"devices\",\n\t\t\t\t\t`packages/config/config/devices/${this.parent.filename}:\nParameter #${this.parameterNumber} is missing required property \"minValue\"!`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (ret.maxValue == undefined) {\n\t\t\tif (ret.allowManualEntry === false && ret.options.length > 0) {\n\t\t\t\tret.maxValue = Math.max(...ret.options.map((o) => o.value));\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"devices\",\n\t\t\t\t\t`packages/config/config/devices/${this.parent.filename}:\nParameter #${this.parameterNumber} is missing required property \"maxValue\"!`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// @ts-expect-error TS doesn't seem to understand that we do set min/maxValue\n\t\treturn ret;\n\t}\n}\n\nexport type ParamInformation =\n\t& Omit<\n\t\tConditionalParamInformation,\n\t\t\"condition\" | \"evaluateCondition\" | \"options\" | \"minValue\" | \"maxValue\"\n\t>\n\t& {\n\t\toptions: readonly ConfigOption[];\n\t\tminValue: NonNullable<ConditionalParamInformation[\"minValue\"]>;\n\t\tmaxValue: NonNullable<ConditionalParamInformation[\"maxValue\"]>;\n\t};\n\nexport class ConditionalConfigOption implements ConditionalItem<ConfigOption> {\n\tpublic constructor(\n\t\tpublic readonly value: number,\n\t\tpublic readonly label: string,\n\t\tpublic readonly condition?: string,\n\t) {}\n\n\tpublic evaluateCondition(deviceId?: DeviceID): ConfigOption | undefined {\n\t\tif (!conditionApplies(this, deviceId)) return;\n\n\t\treturn pick(this, [\"value\", \"label\"]);\n\t}\n}\n\nexport interface ConfigOption {\n\tvalue: number;\n\tlabel: string;\n}\n\nexport type ConditionalParamInfoMap = ReadonlyObjectKeyMap<\n\t{ parameter: number; valueBitMask?: number },\n\tConditionalParamInformation[]\n>;\n\nexport type ParamInfoMap = ReadonlyObjectKeyMap<\n\t{ parameter: number; valueBitMask?: number },\n\tParamInformation\n>;\n\nexport function parseConditionalParamInformationMap(\n\tdefinition: JSONObject,\n\tparent: ConditionalDeviceConfig,\n\terrorPrefix: string = \"\",\n): ConditionalParamInfoMap {\n\tconst paramInformation = new ObjectKeyMap<\n\t\t{ parameter: number; valueBitMask?: number },\n\t\tConditionalParamInformation[]\n\t>();\n\n\tconst filename = parent.filename;\n\n\tif (isArray(definition.paramInformation)) {\n\t\t// Check that every param has a param number\n\t\tif (!definition.paramInformation.every((entry: any) => \"#\" in entry)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}: \n${errorPrefix}required property \"#\" missing in at least one entry of paramInformation`,\n\t\t\t);\n\t\t}\n\n\t\t// And a valid $if condition\n\t\tfor (const entry of definition.paramInformation) {\n\t\t\tvalidateCondition(\n\t\t\t\tfilename,\n\t\t\t\tentry,\n\t\t\t\t`${errorPrefix}At least one entry of paramInformation contains an`,\n\t\t\t);\n\t\t}\n\n\t\tfor (const paramDefinition of definition.paramInformation) {\n\t\t\tconst { [\"#\"]: paramNo, ...defn } = paramDefinition;\n\t\t\tconst key = tryParseParamNumber(paramNo);\n\t\t\tif (!key) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}: \n${errorPrefix}found invalid param number \"${paramNo}\" in paramInformation`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!paramInformation.has(key)) paramInformation.set(key, []);\n\t\t\tparamInformation\n\t\t\t\t.get(key)!\n\t\t\t\t.push(\n\t\t\t\t\tnew ConditionalParamInformation(\n\t\t\t\t\t\tparent,\n\t\t\t\t\t\tkey.parameter,\n\t\t\t\t\t\tkey.valueBitMask,\n\t\t\t\t\t\tdefn,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t}\n\t} else if (isObject(definition.paramInformation)) {\n\t\t// Silently ignore this old format\n\t} else {\n\t\tthrowInvalidConfig(\n\t\t\t`device`,\n\t\t\t`packages/config/config/devices/${filename}:\n${errorPrefix}paramInformation must be an array!`,\n\t\t);\n\t}\n\n\treturn paramInformation;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/cjs/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;;;;;
|
|
4
|
+
"sourcesContent": ["export * from \"./ConfigManager.js\";\nexport * from \"./Logger_safe.js\";\nexport * from \"./Manufacturers.js\";\nexport { PACKAGE_VERSION } from \"./_version.js\";\nexport * from \"./devices/AssociationConfig.js\";\nexport * from \"./devices/CompatConfig.js\";\nexport * from \"./devices/DeviceConfig.js\";\nexport * from \"./devices/DeviceMetadata.js\";\nexport * from \"./devices/EndpointConfig.js\";\nexport * from \"./devices/ParamInformation.js\";\nexport * from \"./devices/SceneConfig.js\";\nexport * from \"./devices/shared.js\";\nexport type * from \"./traits.js\";\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;;;;;AAAA,0BAAc,+BAAd;AACA,0BAAc,6BADd;AAEA,0BAAc,+BAFd;AAGA,qBAAgC;AAChC,0BAAc,2CAJd;AAKA,0BAAc,sCALd;AAMA,0BAAc,sCANd;AAOA,0BAAc,wCAPd;AAQA,0BAAc,wCARd;AASA,0BAAc,0CATd;AAUA,0BAAc,qCAVd;AAWA,0BAAc,gCAXd;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,7 +4,7 @@ import { isArray, isObject } from "alcalzone-shared/typeguards";
|
|
|
4
4
|
import JSON5 from "json5";
|
|
5
5
|
import path from "pathe";
|
|
6
6
|
const IMPORT_KEY = "$import";
|
|
7
|
-
const importSpecifierRegex = /^(?<filename>(?:~\/)?[\w\d
|
|
7
|
+
const importSpecifierRegex = /^(?<filename>(?:~\/)?[\w\d/\\._-]+\.json)?(?:#(?<selector>[\w\d/._-]+(?:\[0x[0-9a-fA-F]+\])?))?$/i;
|
|
8
8
|
// The template cache is used to speed up cases where the same files get parsed multiple times,
|
|
9
9
|
// e.g. during config file linting. It should be cleared whenever the files need to be loaded fresh
|
|
10
10
|
// from disk, like when creating an index
|
|
@@ -68,7 +68,7 @@ function select(obj, selector) {
|
|
|
68
68
|
}
|
|
69
69
|
function getImportStack(visited, selector) {
|
|
70
70
|
const source = [...visited, selector ? `#${selector}` : undefined]
|
|
71
|
-
.
|
|
71
|
+
.toReversed()
|
|
72
72
|
.filter((s) => !!s);
|
|
73
73
|
if (source.length > 0) {
|
|
74
74
|
return `\nImport stack: ${source.map((s) => `\n in ${s}`).join("")}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JsonTemplate.js","sourceRoot":"","sources":["../../src/JsonTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,OAAO,CAAC;AAEzB,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,oBAAoB,GACzB,
|
|
1
|
+
{"version":3,"file":"JsonTemplate.js","sourceRoot":"","sources":["../../src/JsonTemplate.ts"],"names":[],"mappings":"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"}
|
package/build/esm/Logic.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type RulesLogic } from "json-logic-js";
|
|
2
|
-
export declare function parseLogic(
|
|
2
|
+
export declare function parseLogic(input: string): RulesLogic;
|
|
3
3
|
export declare function evaluate(logic: string, context: unknown): string | number | boolean;
|
|
4
4
|
//# sourceMappingURL=Logic.d.ts.map
|
package/build/esm/Logic.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Logic.d.ts","sourceRoot":"","sources":["../../src/Logic.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Logic.d.ts","sourceRoot":"","sources":["../../src/Logic.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,UAAU,EAAwB,MAAM,eAAe,CAAC;AAsCtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAMpD;AAED,wBAAgB,QAAQ,CACvB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACd,MAAM,GAAG,MAAM,GAAG,OAAO,CAG3B"}
|
package/build/esm/Logic.js
CHANGED
|
@@ -4,9 +4,9 @@ import semverGt from "semver/functions/gt.js";
|
|
|
4
4
|
import semverGte from "semver/functions/gte.js";
|
|
5
5
|
import semverLt from "semver/functions/lt.js";
|
|
6
6
|
import semverLte from "semver/functions/lte.js";
|
|
7
|
-
import { parse } from "./LogicParser.js";
|
|
8
7
|
// The types are not correct:
|
|
9
8
|
import { default as JsonLogic } from "json-logic-js";
|
|
9
|
+
import { parse, toRulesLogic } from "./LogicParser.js";
|
|
10
10
|
const { add_operation, apply } = JsonLogic;
|
|
11
11
|
function tryOr(operation, onError) {
|
|
12
12
|
return ((...args) => {
|
|
@@ -23,14 +23,12 @@ add_operation("ver >", tryOr((a, b) => semverGt(padVersion(a), padVersion(b)), f
|
|
|
23
23
|
add_operation("ver <=", tryOr((a, b) => semverLte(padVersion(a), padVersion(b)), false));
|
|
24
24
|
add_operation("ver <", tryOr((a, b) => semverLt(padVersion(a), padVersion(b)), false));
|
|
25
25
|
add_operation("ver ===", tryOr((a, b) => semverEq(padVersion(a), padVersion(b)), false));
|
|
26
|
-
export function parseLogic(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
catch (e) {
|
|
32
|
-
throw new Error(`Invalid logic: ${logic}\n${e.message}`);
|
|
26
|
+
export function parseLogic(input) {
|
|
27
|
+
const expr = parse(input);
|
|
28
|
+
if (!expr) {
|
|
29
|
+
throw new Error(`Failed to parse expression: ${input}`);
|
|
33
30
|
}
|
|
31
|
+
return toRulesLogic(expr);
|
|
34
32
|
}
|
|
35
33
|
export function evaluate(logic, context) {
|
|
36
34
|
const rules = parseLogic(logic);
|
package/build/esm/Logic.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Logic.js","sourceRoot":"","sources":["../../src/Logic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,SAAS,MAAM,yBAAyB,CAAC;AAChD,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,SAAS,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"Logic.js","sourceRoot":"","sources":["../../src/Logic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,SAAS,MAAM,yBAAyB,CAAC;AAChD,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,SAAS,MAAM,yBAAyB,CAAC;AAEhD,6BAA6B;AAC7B,OAAO,EAAmB,OAAO,IAAI,SAAS,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACvD,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;AAE3C,SAAS,KAAK,CACb,SAAY,EACZ,OAAsB;IAEtB,OAAO,CAAC,CAAC,GAAG,IAAW,EAAE,EAAE;QAC1B,IAAI,CAAC;YACJ,OAAO,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,OAAO,CAAC;QAChB,CAAC;IACF,CAAC,CAAa,CAAC;AAChB,CAAC;AAED,aAAa,CACZ,QAAQ,EACR,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAC/D,CAAC;AACF,aAAa,CACZ,OAAO,EACP,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAC9D,CAAC;AACF,aAAa,CACZ,QAAQ,EACR,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAC/D,CAAC;AACF,aAAa,CACZ,OAAO,EACP,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAC9D,CAAC;AACF,aAAa,CACZ,SAAS,EACT,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAC9D,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,QAAQ,CACvB,KAAa,EACb,OAAgB;IAEhB,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC"}
|