zigbee-herdsman-converters
Version:
Collection of device converters to be used with zigbee-herdsman
431 lines • 16.9 kB
TypeScript
import type { Models as ZHModels } from "zigbee-herdsman";
import type { ClusterCommandKeys, ClusterCommandResponseKeys, ClusterOrRawAttributeKeys, TCustomCluster, TCustomClusterPayload, ZigbeeOtaImageMeta } from "zigbee-herdsman/dist/controller/tstype";
import type { Header as ZHZclHeader, PowerSource as ZHZclPowerSource } from "zigbee-herdsman/dist/zspec/zcl";
import type { TClusterAttributeKeys, TClusterPayload, TPartialClusterAttributes } from "zigbee-herdsman/dist/zspec/zcl/definition/clusters-types";
import type { FrameControl } from "zigbee-herdsman/dist/zspec/zcl/definition/tstype";
import type * as exposes from "./exposes";
export interface Logger {
debug: (messageOrLambda: string | (() => string), namespace: string) => void;
info: (messageOrLambda: string | (() => string), namespace: string) => void;
warning: (messageOrLambda: string | (() => string), namespace: string) => void;
error: (messageOrLambda: string | (() => string), namespace: string) => void;
}
export type Range = [number, number];
export type ValuesOf<T> = T[keyof T];
export type PowerSource = keyof typeof ZHZclPowerSource;
export interface KeyValue {
[s: string]: unknown;
}
export interface KeyValueString {
[s: string]: string;
}
export interface KeyValueNumberString {
[s: number]: string;
}
export interface KeyValueAny {
[s: string]: any;
}
export type Publish = (payload: KeyValue) => void;
export type Access = 0b001 | 0b010 | 0b100 | 0b011 | 0b101 | 0b111;
export type Expose = exposes.Numeric | exposes.Binary | exposes.Enum | exposes.Composite | exposes.List | exposes.Light | exposes.Switch | exposes.Lock | exposes.Cover | exposes.Climate | exposes.Fan | exposes.Text;
export type Option = exposes.Numeric | exposes.Binary | exposes.Composite | exposes.Enum | exposes.List | exposes.Text;
export interface Fingerprint {
applicationVersion?: number;
manufacturerID?: number;
type?: "EndDevice" | "Router";
dateCode?: string;
hardwareVersion?: number;
manufacturerName?: string;
modelID?: string;
powerSource?: PowerSource;
softwareBuildID?: string;
stackVersion?: number;
zclVersion?: number;
ieeeAddr?: RegExp;
endpoints?: {
ID?: number;
profileID?: number;
deviceID?: number;
inputClusters?: number[];
outputClusters?: number[];
}[];
priority?: number;
}
export type WhiteLabel = {
vendor?: string;
model: string;
description?: string;
fingerprint: Fingerprint[];
} | {
vendor?: string;
model: string;
description?: string;
};
export interface MockProperty {
property: string;
value: KeyValue | string;
}
export interface DiscoveryEntry {
mockProperties: MockProperty[];
type: string;
object_id: string;
discovery_payload: KeyValue;
}
export type BatteryLinearVoltage = {
min: number;
max: number;
vOffset?: number;
};
export type BatteryNonLinearVoltage = "3V_2100" | "3V_1500_2800";
export interface DefinitionMeta {
separateWhite?: boolean;
/**
* Enables the multi endpoint functionality in e.g. `fromZigbee.on_off`, example: normally this converter would return `{"state": "OFF"}`, when
* multiEndpoint is enabled the `endpoint` method of the device definition will be called to determine the endpoint name which is then used as ke
* y e.g. `{"state_left": "OFF"}`. Only needed when device sends the same attribute from multiple endpoints.
*
* @defaultValue false
*/
multiEndpoint?: boolean;
/**
* enforce a certain endpoint for an attribute, e.g. `{"power": 4}` see `utils.enforceEndpoint()`
*/
multiEndpointEnforce?: {
[s: string]: number;
};
/**
* Attributes to not suffix with the endpoint name
*/
multiEndpointSkip?: string[];
publishDuplicateTransaction?: boolean;
tuyaDatapoints?: Tuya.MetaTuyaDataPoints;
/**
* used by toZigbee converters to disable the default response of some devices as they don't provide one.
*
* @defaultValue false
*/
disableDefaultResponse?: boolean | ((entity: Zh.Endpoint) => boolean);
/**
* Amount of pincodes the lock can handle
*/
pinCodeCount?: number;
/**
* Set to true for cover controls that report position=100 as open.
*
* @defaultValue false
*/
coverInverted?: boolean;
/**
* timeout for commands to this device used in toZigbee.
*
* @defaultValue 10000
*/
timeout?: number;
tuyaSendCommand?: "sendData" | "dataRequest";
/**
* Set cover state based on tilt
*/
coverStateFromTilt?: boolean;
/**
* see e.g. HT-08 definition
*/
thermostat?: {
weeklyScheduleMaxTransitions?: number;
weeklyScheduleSupportedModes?: number[];
weeklyScheduleFirstDayDpId?: number;
weeklyScheduleConversion?: string;
/**
* Do not map `pIHeatingDemand`/`pICoolingDemand` from 0-255 -\> 0-100, see `fromZigbee.thermostat`
*
* @defaultValue false
*/
dontMapPIHeatingDemand?: boolean;
};
battery?: {
/**
* convert voltage to percentage using specified option. See `utils.batteryVoltageToPercentage()`
*
* @example '3V_2100'
* @defaultValue null
*/
voltageToPercentage?: BatteryNonLinearVoltage | BatteryLinearVoltage;
/**
* Prevents batteryPercentageRemaining from being divided (ZCL 200=100%, but some report 100=100%)
*
* @defaultValue false
*/
dontDividePercentage?: boolean;
};
/**
* see `toZigbee.light_color`
*
* @defaultValue false
*/
applyRedFix?: boolean;
/**
* Indicates light turns off when brightness 1 is set
*
* @defaultValue false
*/
turnsOffAtBrightness1?: boolean;
moveToLevelWithOnOffDisable?: boolean | ((entity: Zh.Endpoint) => boolean);
/**
* Omit optional optionsMask/optionsOverride parameters for devices with strict ZCL v1 compliance
*
* @defaultValue false
*/
omitOptionalLevelParams?: boolean;
tuyaThermostatPreset?: {
[s: number]: string;
};
/** Tuya specific thermostat options */
tuyaThermostatSystemMode?: {
[s: number]: string;
};
/** Tuya specific thermostat options */
tuyaThermostatPresetToSystemMode?: {
[s: number]: string;
};
/**
* see `toZigbee.light_color`
*
* @defaultValue false
*/
supportsEnhancedHue?: boolean | ((entity: Zh.Endpoint) => boolean);
/**
* Prevents some converters adding the `action_group` to the payload.
*
* @defaultValue false
*/
disableActionGroup?: boolean;
/**
* see `toZigbee.light_color`, usually set by `light_*` extends via options.
*
* @defaultValue true
*/
supportsHueAndSaturation?: boolean;
/**
* Do not set `position` or `tilt` to target value on /set. See `toZigbee.cover_position_tilt`
*
* @defaultValue false
*/
coverPositionTiltDisableReport?: boolean;
/**
* Override the Home Assistant discovery payload using a custom function.
*/
overrideHaDiscoveryPayload?(payload: KeyValueAny): void;
/**
* Never use a transition when transitioning to off (even when specified)
*/
noOffTransitionWhenOff?: boolean | ((entity: Zh.Endpoint) => boolean);
/**
* Manufacturer specific
*/
sinopeAlternateBacklightAutoDim?: boolean;
}
export type Configure = (device: Zh.Device, coordinatorEndpoint: Zh.Endpoint, definition: Definition) => Promise<void> | void;
export declare namespace OnEvent {
type BaseData = {
device: Zh.Device;
deviceExposesChanged: () => void;
state: KeyValue;
options: KeyValue;
};
type Event = {
type: "stop";
data: {
ieeeAddr: string;
};
} | {
type: "deviceNetworkAddressChanged" | "deviceAnnounce" | "deviceJoined" | "start";
data: BaseData;
} | {
type: "deviceOptionsChanged";
data: BaseData & {
from: KeyValue;
to: KeyValue;
};
} | {
type: "deviceInterview";
data: BaseData & {
status: "started" | "successful" | "failed";
};
};
type Handler = (event: Event) => Promise<void> | void;
}
export interface ModernExtend {
fromZigbee?: Definition["fromZigbee"];
toZigbee?: Definition["toZigbee"];
exposes?: (Expose | DefinitionExposesFunction)[];
configure?: Definition["configure"][];
meta?: Definition["meta"];
ota?: Definition["ota"];
options?: Option[];
onEvent?: Definition["onEvent"][];
endpoint?: Definition["endpoint"];
isModernExtend: true;
}
export type DummyDevice = {
manufacturerName?: string;
isDummyDevice: true;
applicationVersion?: number;
};
export type DefinitionExposesFunction = (device: Zh.Device | DummyDevice, options: KeyValue) => Expose[];
export type DefinitionExposes = Expose[] | DefinitionExposesFunction;
type DefinitionMatcher = {
zigbeeModel: string[];
fingerprint?: Fingerprint[];
} | {
zigbeeModel?: string[];
fingerprint: Fingerprint[];
};
type DefinitionBase = {
model: string;
vendor: string;
description: string;
whiteLabel?: WhiteLabel[];
generated?: true;
externalConverterName?: string;
};
type DefinitionConfig = {
endpoint?: (device: Zh.Device) => {
[s: string]: number;
};
/**
* Semver version of the definition.
* Changing this from one ZHC version to another, informs the application that it should trigger specific behavior (migration-like):
* - major: reserved for future use
* - minor: reserved for future use
* - patch: the application should re-`configure` the device
*/
version?: `0.0.${number}`;
configure?: Configure;
options?: Option[];
meta?: DefinitionMeta;
onEvent?: OnEvent.Handler;
ota?: boolean | (Pick<ZigbeeOtaImageMeta, "modelId" | "otaHeaderString" | "hardwareVersionMin" | "hardwareVersionMax"> & {
manufacturerName?: string;
});
};
type DefinitionFeatures = {
fromZigbee: Fz.Converter<any, any, any>[];
toZigbee: Tz.Converter[];
exposes: DefinitionExposes;
};
export type Definition = DefinitionMatcher & DefinitionBase & DefinitionConfig & DefinitionFeatures & Required<Pick<DefinitionConfig, "version">>;
export type DefinitionWithExtend = DefinitionMatcher & DefinitionBase & DefinitionConfig & (({
extend: ModernExtend[];
} & Partial<DefinitionFeatures>) | DefinitionFeatures);
export type ExternalDefinitionWithExtend = DefinitionWithExtend & {
externalConverterName: string;
};
export type ElementOf<T> = T extends readonly (infer U)[] ? U : T;
/** TFoundationRepetitive from ZSpec Zcl mapped to names used by ZHC (TODO: refactor names to match ZSpec Zcl directly / breaking ext. conv) */
export type TFoundationRepetitiveMapped = "read" | "readResponse" | "write" | "attributeReport";
export declare namespace Fz {
export type ConverterTypeCmd<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined> = `command${Capitalize<ClusterCommandKeys<Cl, Custom>[number] & string>}` | `command${Capitalize<ClusterCommandResponseKeys<Cl, Custom>[number] & string>}`;
type ConverterType<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined> = "raw" | TFoundationRepetitiveMapped | ClusterOrRawAttributeKeys<Cl, Custom>[number] | ConverterTypeCmd<Cl, Custom>;
type ConverterTypeStringOrArray<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined> = ConverterType<Cl, Custom> | readonly ConverterType<Cl, Custom>[];
type MessageTypeDataMap<Cl extends string | number> = {
raw: Buffer;
read: (TClusterAttributeKeys<Cl>[number] | number)[];
readResponse: TPartialClusterAttributes<Cl>;
write: TPartialClusterAttributes<Cl>;
attributeReport: TPartialClusterAttributes<Cl>;
};
type MessageTypeCustomDataMap<Custom extends TCustomCluster> = {
raw: Buffer;
read: (keyof Custom["attributes"] | number)[];
readResponse: Partial<Custom["attributes"]>;
write: Partial<Custom["attributes"]>;
attributeReport: Partial<Custom["attributes"]>;
};
export interface Message<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined, Ty extends ConverterTypeStringOrArray<Cl, Custom> = ConverterTypeStringOrArray<Cl, Custom>> {
data: (ElementOf<Ty> extends infer Single ? Custom extends undefined ? Single extends keyof MessageTypeDataMap<Cl> ? MessageTypeDataMap<Cl>[Single] : Single extends string | number ? Single extends `command${infer Co}` ? TClusterPayload<Cl, Uncapitalize<Co>> : TClusterPayload<Cl, Single> : never : Custom extends TCustomCluster ? Single extends keyof MessageTypeCustomDataMap<Custom> ? MessageTypeDataMap<Cl>[Single] extends never ? MessageTypeCustomDataMap<Custom>[Single] extends never ? Record<number, unknown> : MessageTypeCustomDataMap<Custom>[Single] : MessageTypeDataMap<Cl>[Single] & MessageTypeCustomDataMap<Custom>[Single] : Single extends string | number ? Single extends `command${infer Co}` ? TClusterPayload<Cl, Uncapitalize<Co>> extends never ? TCustomClusterPayload<Custom, Uncapitalize<Co>> : TClusterPayload<Cl, Uncapitalize<Co>> & TCustomClusterPayload<Custom, Uncapitalize<Co>> : TClusterPayload<Cl, Single> extends never ? TCustomClusterPayload<Custom, Single> : TClusterPayload<Cl, Single> & TCustomClusterPayload<Custom, Single> : never : never : never) & Record<number, unknown>;
endpoint: Zh.Endpoint;
device: Zh.Device;
meta: {
zclTransactionSequenceNumber?: number;
manufacturerCode?: number;
frameControl?: FrameControl;
rawData: Buffer;
};
groupID: number;
type: ElementOf<Ty>;
cluster: string | number;
linkquality: number;
}
export interface Meta {
state: KeyValue;
device: Zh.Device;
deviceExposesChanged: () => void;
}
export interface Converter<Cl extends number | string, Custom extends TCustomCluster | undefined = undefined, Ty extends ConverterTypeStringOrArray<Cl, Custom> = ConverterTypeStringOrArray<Cl, Custom>> {
cluster: Cl;
type: Ty;
options?: Option[] | ((definition: Definition) => Option[]);
convert: (model: Definition, msg: Message<Cl, Custom, Ty>, publish: Publish, options: KeyValue, meta: Fz.Meta) => KeyValueAny | void | Promise<void>;
}
export {};
}
export declare namespace Tz {
interface Meta {
message: KeyValue;
device: Zh.Device | undefined;
mapped: Definition | Definition[];
options: KeyValue;
state: KeyValue;
endpoint_name: string | undefined;
membersState?: {
[s: string]: KeyValue;
};
publish: Publish;
converterOptions?: KeyValue;
}
type ConvertSetResult = {
state?: KeyValue;
membersState?: {
[s: string]: KeyValue;
};
} | void;
interface Converter {
key?: string[];
options?: Option[] | ((definition: Definition) => Option[]);
endpoints?: string[];
convertSet?: (entity: Zh.Endpoint | Zh.Group, key: string, value: unknown, meta: Tz.Meta) => Promise<ConvertSetResult> | ConvertSetResult;
convertGet?: (entity: Zh.Endpoint | Zh.Group, key: string, meta: Tz.Meta) => Promise<void>;
}
}
export declare namespace Zh {
type Endpoint = ZHModels.Endpoint;
type Device = ZHModels.Device;
type Group = ZHModels.Group;
type ZclHeader = ZHZclHeader;
}
export declare namespace Tuya {
interface DpValue {
dp: number;
datatype: number;
data: Buffer;
}
interface ValueConverterSingle {
to?: (value: any, meta?: Tz.Meta) => unknown;
from?: (value: any, meta?: Fz.Meta, options?: KeyValue, publish?: Publish, msg?: Fz.Message<any>) => number | string | boolean | KeyValue | KeyValue[] | null;
}
interface MetaTuyaDataPointsMeta {
skip?: (meta: Tz.Meta) => boolean;
optimistic?: boolean;
}
type MetaTuyaDataPointsSingle = [number, string, ValueConverterSingle, MetaTuyaDataPointsMeta?];
type MetaTuyaDataPoints = MetaTuyaDataPointsSingle[];
}
export declare namespace Reporting {
interface Override {
min?: number;
max?: number;
change?: number;
}
}
export type LevelConfigFeatures = ("on_off_transition_time" | "on_transition_time" | "off_transition_time" | "execute_if_off" | "on_level" | "current_level_startup")[];
export {};
//# sourceMappingURL=types.d.ts.map