inventoresed
Version:
Z-Wave driver written entirely in JavaScript/TypeScript
337 lines (320 loc) • 9.01 kB
text/typescript
/* eslint-disable @typescript-eslint/no-floating-promises */
require("reflect-metadata");
import { CommandClasses } from "@zwave-js/core";
import { num2hex } from "@zwave-js/shared";
import * as c from "ansi-colors";
import * as clipboard from "clipboardy";
import * as fs from "fs-extra";
import * as path from "path";
import * as yargs from "yargs";
const ccRegex = /^@commandClass\(CommandClasses(?:\.|\[")(.+?)(?:"\])?\)/m;
const versionRegex = /^@implementedVersion\((\d+)\)/m;
const apiRegex = /^@API\(CommandClasses(?:\.|\[)(.+?)(?:\])?\)/m;
const noApiRegex = /^\/\/ @noAPI/m; // This comment marks a CC that needs no API
const setValueApiRegex = /^\tprotected \[SET_VALUE\]/m;
const noSetValueApiRegex = /^\/\/ @noSetValueAPI/m; // This comment marks a CC that needs no setValue API
const interviewRegex = /^\tpublic async interview/m;
const noInterviewRegex = /^\/\/ @noInterview/m; // This comment marks a CC that needs no interview procedure
const pollValueApiRegex = /^\tprotected \[POLL_VALUE\]/m;
const noPollValueApiRegex = /^\/\/ @noPollValueAPI/m; // This comment marks a CC that needs no pollValue API
const argv = yargs.parseSync();
const onlyIncomplete = !!argv.onlyIncomplete;
function getSafeLength(str: string): number {
return c.stripColor(str).length;
}
function padEnd(str: string, len: number): string {
return str + " ".repeat(len - getSafeLength(str));
}
interface CCInfo {
version: number;
API: boolean;
setValue: boolean;
pollValue: boolean;
interview: boolean;
}
(async () => {
const ccDir = path.join(__dirname, "../..", "cc/src/cc");
const ccFiles = (await fs.readdir(ccDir))
.filter((file) => file.endsWith(".ts") && !file.endsWith("test.ts"))
.map((file) => path.join(ccDir, file));
const allCCs = new Map<string, CCInfo>(
Object.keys(CommandClasses)
.filter((cc) => Number.isNaN(+cc))
.map((name) => [
name,
{
version: 0,
API: false,
setValue: false,
pollValue: false,
interview: false,
},
]),
);
for (const ccFile of ccFiles) {
const fileContent = await fs.readFile(ccFile, "utf8");
try {
const ccName = ccRegex.exec(fileContent)![1];
const ccVersion = +versionRegex.exec(fileContent)![1];
const hasAPI =
apiRegex.test(fileContent) || noApiRegex.test(fileContent);
const setValue =
(hasAPI && setValueApiRegex.test(fileContent)) ||
noApiRegex.test(fileContent) ||
noSetValueApiRegex.test(fileContent);
const pollValue =
(hasAPI && pollValueApiRegex.test(fileContent)) ||
noApiRegex.test(fileContent) ||
noPollValueApiRegex.test(fileContent);
const interview =
interviewRegex.test(fileContent) ||
noInterviewRegex.test(fileContent);
allCCs.set(ccName, {
version: ccVersion,
API: hasAPI,
setValue,
pollValue,
interview,
});
} catch {
/* ok */
}
}
const headers = [
"",
"CC name",
"Version",
"Interview?",
"API?",
"setValue?",
"pollValue?",
];
const rows: string[][] = [];
for (const [
name,
{ version, interview, API, setValue, pollValue },
] of allCCs.entries()) {
const {
version: latest,
deprecated,
obsolete,
} = getLatestVersion(name);
if (obsolete) continue;
const implementationStatus =
version === latest && interview && API && setValue
? "done"
: version > 0
? "in progress"
: "none";
const overallColor =
implementationStatus === "done"
? c.green
: implementationStatus === "in progress"
? c.yellow
: deprecated
? c.reset
: c.red;
const versionColor =
version === latest
? c.green
: version > 0
? c.yellow
: deprecated
? c.reset
: c.red;
const implementedVersion = versionColor(
version > 0 ? version.toString() : "-",
);
const hasInterview = interview ? c.green(" ✔ ") : c.red(" ❌ ");
const hasAPI = API ? c.green(" ✔ ") : c.red(" ❌ ");
const hasSetValue = setValue ? c.green(" ✔ ") : c.red(" ❌ ");
const hasPollValue = pollValue ? c.green(" ✔ ") : c.red(" ❌ ");
const prefix =
implementationStatus === "done"
? "✔"
: implementationStatus === "in progress"
? "✍🏻"
: "❌";
const postfix = deprecated ? " " + c.reset("(deprecated)") : "";
if (implementationStatus !== "done" || !onlyIncomplete) {
rows.push([
overallColor(prefix),
overallColor(name + postfix),
`${implementedVersion} (${latest})`,
hasInterview,
hasAPI,
hasSetValue,
hasPollValue,
]);
}
}
writeTable(
[headers, ...rows],
argv.flavor === "github" ? "github" : "console",
);
})();
function writeTable(rows: string[][], flavor: "console" | "github"): void {
const numColumns = rows[0].length;
if (flavor === "console") {
const columnLenghts: number[] = [];
for (let col = 0; col < numColumns; col++) {
columnLenghts.push(
Math.max(...rows.map((row) => getSafeLength(row[col]))),
);
}
const HR =
"|-" +
columnLenghts.map((len) => "-".repeat(len)).join("-|-") +
"-|";
console.log(HR);
for (let i = 0; i < rows.length; i++) {
const row =
"| " +
rows[i]
.map((r, ri) => padEnd(r, columnLenghts[ri]))
.join(" | ") +
" |";
console.log(row);
if (i === 0) console.log(HR);
}
console.log(HR);
} /*if (flavor === "github")*/ else {
let output = "";
let HR = "|";
for (let i = 0; i < numColumns; i++) HR += " --- |";
for (let i = 0; i < rows.length; i++) {
const row = "| " + rows[i].join(" | ") + " |";
output += row + "\n";
if (i === 0) output += HR + "\n";
}
console.log(output);
if (!process.env.CI) {
clipboard.write(c.stripColor(output));
console.log(c.green("The table was copied to the clipboard!"));
}
}
}
// Taken from SDS13548
const ccVersions: Record<
string,
| { version: string | number; deprecated?: boolean; obsolete?: boolean }
| undefined
> = {
"0x9C": { version: 1, deprecated: true },
"0x9D": { version: 1 },
"0x5D": { version: 3 },
"0x7e": { version: 1 },
"0x22": { version: 1 },
"0x85": { version: 3 },
"0x9B": { version: 1 },
"0x59": { version: 3 },
"0xA1": { version: 1 },
"0xA2": { version: 1 },
"0x66": { version: 1 },
"0x20": { version: 2 },
"0x36": { version: 1 },
"0x80": { version: 3 },
"0x30": { version: 2, deprecated: true },
"0x25": { version: 2 },
"0x46": { version: 1, deprecated: true },
"0x5B": { version: 3 },
"0x81": { version: 1 },
"0x33": { version: 3 },
"0x70": { version: 4 },
"0x21": { version: 1 },
"0x56": { version: 1, deprecated: true },
"0x3A": { version: 1 },
"0x3B": { version: 1 },
"0x5A": { version: 1 },
"0x62": { version: 4 },
"0x4C": { version: 1 },
"0x90": { version: 1 },
"0x6F": { version: 1 },
"0x7A": { version: 7 },
"0xA3": { version: 1 },
"0x8C": { version: 1 },
"0x7B": { version: 1, deprecated: true },
"0x37": { version: 1 },
"0x39": { version: 1 },
"0x6D": { version: 2 },
"0x6E": { version: 1 },
"0x64": { version: 2 },
"0x74": { version: 1 },
"0x87": { version: 4 },
"0x5C": { version: 1 },
"0xA0": { version: 1 },
"0x6B": { version: 1 },
"0x89": { version: 1 },
"0x76": { version: 1, deprecated: true },
"0x69": { version: 2 },
"0x91": { version: 1 },
"0x72": { version: 2 },
"0x32": { version: 6 },
"0x3C": { version: 1 },
"0x3D": { version: 3 },
"0x3E": { version: 1 },
"0x60": { version: 4 },
"0x8E": { version: 4 },
"0x8F": { version: 1 },
"0x31": { version: 11 },
"0x26": { version: 4 },
"0x29": { version: 1, deprecated: true },
"0x4D": { version: 2 },
"0x34": { version: 3 },
"0x67": { version: 2 },
"0x52": { version: 3 },
"0x00": { version: 1 },
"0x77": { version: 1 },
"0x78": { version: 1 },
"0x71": { version: 8 },
"0x73": { version: 1 },
"0x3F": { version: 1 },
"0x41": { version: 1 },
"0x88": { version: 1, deprecated: true },
"0x75": { version: 2 },
"0x35": { version: 1, deprecated: true },
"0x48": { version: 1 },
"0x49": { version: 1 },
"0x2B": { version: 1 },
"0x2C": { version: 1 },
"0x2D": { version: 1 },
"0x53": { version: 4 },
"0x4E": { version: 3, deprecated: true },
"0x93": { version: 2 },
"0x92": { version: 2 },
"0x98": { version: 1 },
"0x9F": { version: 1 },
"0x94": { version: 4 },
"0x79": { version: 2 },
"0x6C": { version: 2 },
"0x4A": { version: 1 },
"0x4B": { version: 1 },
"0x44": { version: 5 },
"0x45": { version: 2 },
"0x40": { version: 3 },
"0x42": { version: 2 },
"0x47": { version: 1 },
"0x43": { version: 3 },
"0x8A": { version: 2 },
"0x8B": { version: 1 },
"0x55": { version: 2 },
"0x63": { version: 2 },
"0x86": { version: 3 },
"0x84": { version: 3 },
"0x6A": { version: 1 },
"0x23": { version: 4 },
"0x4F": { version: 1 },
"0x5F": { version: 1 },
"0x68": { version: 1 },
"0x58": { version: 1 },
"0x61": { version: 1 },
"0x5E": { version: 2 },
};
function getLatestVersion(ccName: string) {
const cc = CommandClasses[ccName as any] as any as number;
const version = ccVersions[num2hex(cc, true)];
if (version == undefined) {
return { version: 0, obsolete: true };
}
return version;
}