Markdown Converter
Agent skill for markdown-converter
zigbee-herdsman-converters is a TypeScript library that provides device converters for Zigbee devices, used by zigbee-herdsman. It contains:
Sign in to like and favorite skills
zigbee-herdsman-converters is a TypeScript library that provides device converters for Zigbee devices, used by zigbee-herdsman. It contains:
Architecture:
src/converters/ - fromZigbee and toZigbee converter implementationssrc/devices/ - Device definitions organized by vendor (700+ files)src/lib/ - Core utilities, modern extends, vendor libraries, typestest/ - Vitest test suiteTech Stack:
Prerequisites:
# Install pnpm globally if not already installed npm install -g pnpm # Install dependencies (REQUIRED: use --frozen-lockfile) pnpm install --frozen-lockfile
Build:
# Full build (compile TypeScript + generate device index) pnpm run build # Watch mode (auto-rebuild on changes) pnpm run build:watch # Clean build artifacts pnpm run clean
Before starting development:
pnpm install --frozen-lockfile after pulling changespnpm run build.github/copilot-instructions.md for detailed coding standardsKey development files:
src/devices/[vendor].tssrc/converters/fromZigbee.ts and src/converters/toZigbee.tssrc/lib/modernExtend.tssrc/lib/[vendor].ts (e.g., philips.ts, ikea.ts, tuya.ts)src/lib/types.tsAdding a new device:
src/devices/definitions array using modern extendsCode formatting:
pnpm run check --fix to auto-formatRun all tests:
pnpm test
Run tests with coverage:
pnpm run test:coverage
Run benchmarks:
pnpm bench
Test file patterns:
test/ directory.test.ts extensionimport {describe, expect, it, vi} from "vitest"mockDevice from test/utils.tsTest structure pattern:
import {describe, expect, it} from "vitest"; describe("Feature Name", () => { it("should describe expected behavior", () => { // Arrange // Act // Assert expect(result).toStrictEqual(expected); }); });
Before committing, ensure all tests pass:
pnpm run build pnpm run check pnpm test
Import patterns:
import * as fz from "../converters/fromZigbee"; import * as tz from "../converters/toZigbee"; import * as exposes from "../lib/exposes"; import * as m from "../lib/modernExtend"; import * as [vendor] from "../lib/[vendor]"; // e.g., philips, ikea, tuya import type {DefinitionWithExtend} from "../lib/types"; const e = exposes.presets; const ea = exposes.access;
Device definition structure:
export const definitions: DefinitionWithExtend[] = [ { zigbeeModel: ["model_id"], model: "PRODUCT_CODE", vendor: "Vendor Name", description: "Product description", extend: [ m.light({colorTemp: true, color: true}), m.battery(), m.identify(), ], }, ];
Naming conventions:
camelCaseUPPER_SNAKE_CASE, camelCase, or PascalCase (flexible)PascalCaseconst NS = "zhc:modulename"fz, tz, e, ea, mTypeScript rules:
noImplicitAny: truenoImplicitThis: trueLinting rules (enforced by Biome):
const a = 1; const b = 2;)async/await in async functionsas const assertions where appropriateError handling:
// Use node:assert for assertions import assert from "node:assert"; assert(condition, "Error message"); // Throw descriptive errors throw new Error("The on_time value must be a number!"); // Validate inputs utils.assertNumber(value, "property_name"); utils.validateValue(state, ["on", "off", "toggle"]);
Logging:
const NS = "zhc:modulename"; import {logger} from "../lib/logger"; logger.debug(`Message`, NS); logger.info(`Message`, NS); logger.warning(`Message`, NS); logger.error(`Message`, NS);
Build process:
tsc) outputs to dist/indexer.js generates models-index.json for device lookup.d.ts) and source maps createdBuild outputs:
dist/ - Compiled JavaScript and type definitionsmodels-index.json - Generated device model indextsconfig.tsbuildinfo - TypeScript incremental build infoPackage exports:
./dist/index.js./dist/converters/*.js./dist/devices/*.js./dist/lib/*.jsPre-commit hooks (automated via Husky):
pnpm run build pnpm run check pnpm run test
CI/CD pipeline (.github/workflows/ci.yml):
Before creating a PR:
# 1. Format code pnpm run check --fix # 2. Build pnpm run build # 3. Run tests pnpm test
PR requirements:
MODEL.png (exact match to device model)zigbee2mqtt.io/public/images/devices/Code review focus:
pnpm run check)FromZigbee converter:
export const converter_name: Fz.Converter< "clusterName", undefined, ["attributeReport", "readResponse"] > = { cluster: "clusterName", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { // Extract and transform data return {property: value}; }, };
ToZigbee converter:
export const converter_name: Tz.Converter = { key: ["property_name"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value, "property_name"); await entity.command("clusterName", "commandName", {param: value}, utils.getOptions(meta.mapped, entity) ); return {state: {property_name: value}}; }, convertGet: async (entity, key, meta) => { await entity.read("clusterName", ["attributeName"]); }, };
Modern extends (PREFERRED):
extend: [ m.light({colorTemp: {range: [153, 500]}, color: true}), m.battery(), m.identify(), m.onOff(), m.temperature(), m.humidity(), ]
Vendor-specific extends:
import * as philips from "../lib/philips"; import * as ikea from "../lib/ikea"; import * as tuya from "../lib/tuya"; extend: [ philips.m.light({colorTemp: true, color: true}), ikea.ikeaBattery(), tuya.modernExtend.tuyaLight(), ]
Validation utilities:
import * as utils from "../lib/utils"; utils.assertNumber(value, "property_name"); utils.assertString(value, "property_name"); utils.validateValue(state, ["on", "off", "toggle"]); utils.isNumber(value); utils.isString(value); utils.isObject(value);
State management:
import * as globalStore from "../lib/store"; globalStore.getValue(device, "key", defaultValue); globalStore.putValue(device, "key", value); globalStore.hasValue(device, "key"); globalStore.clearValue(device, "key");
Build errors:
pnpm --version should be 10.12.1pnpm run clean && pnpm run buildpnpm list typescriptTest failures:
pnpm vitest run -t "test name"pnpm install --frozen-lockfileLinting errors:
pnpm run check --fixpnpm run checkawait in async functionsType errors:
src/lib/types.ts for type definitionsimport type for type-only importsFz.Converter<ClusterType, Options, MessageTypes>Tz.ConverterDevice not found:
zigbeeModel matches device's model ID exactlysrc/devices/pnpm run build.github/copilot-instructions.md# Initial setup pnpm install --frozen-lockfile # Development cycle pnpm run build:watch # Watch mode pnpm run check --fix # Format code pnpm test # Run tests # Pre-commit (automatic via Husky) pnpm run build pnpm run check pnpm test # Coverage and benchmarks pnpm run test:coverage pnpm bench # Clean slate pnpm run clean
Remember: Always use modern extends for device definitions (not manual converter arrays), follow patterns in existing device files, and ensure all pre-commit checks pass before creating a PR.