Skip to content

Commit

Permalink
feat: Modern extend improvements (#7239)
Browse files Browse the repository at this point in the history
* add pm25 extend

* add option for marking identify as sleepy

* fix spelling

* add commandsOnOff extend

* add linkquality extend

* fix and consolidate identify extend

* Sort modern extends

* add deviceTemperature extend

* add pm25 to definition generator

* add flow extend

* add soilMoisture extend

* add commandsLevelCtrl extend

* add multiendpoint exposes for commandsOnOff

* add commandsWindowCovering extend

* add commandsOnOff and commandsLevelCtrl to definition generator

* add windowCovering extend

* update battery extend

* consilidate toZigbee converters in battery extend

* fix exposes access type

* add action expose merging on extend processing

* mark action as diagnostic extend

* add commandsColorCtrl extend

* bind commands by default

* move identify to input extenders

* add ota extend to definition generator

* fix test with output genIdentify

* fix index test with output genIdentify

* filter esposes only once

* simplify filtering

* Import exposes as exposesLib

* fix regression of changes from 6b3911a

* combine to getEndpointsWithCluster

* fix condition

* use existing fz and tz converters for now

* remove identify_sleepy key

* add setupConfigureForBinding
  • Loading branch information
mrskycriper authored Mar 25, 2024
1 parent 9c5e61f commit 8f80a50
Show file tree
Hide file tree
Showing 6 changed files with 1,205 additions and 748 deletions.
28 changes: 23 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as configureKey from './lib/configureKey';
import * as exposes from './lib/exposes';
import * as exposesLib from './lib/exposes';
import type {Feature, Numeric, Enum, Binary, Text, Composite, List, Light, Climate, Switch, Lock, Cover, Fan} from './lib/exposes';
import {Enum as EnumClass} from './lib/exposes';
import toZigbee from './converters/toZigbee';
import fromZigbee from './converters/fromZigbee';
import assert from 'assert';
Expand Down Expand Up @@ -103,9 +104,10 @@ function processExtensions(definition: Definition): Definition {
assert.fail(`'${definition.model}' has function exposes which is not allowed`);
}

exposes = [...exposes ?? []]
toZigbee = [...toZigbee ?? []];
fromZigbee = [...fromZigbee ?? []];
exposes = [...exposes ?? []];

const configures: Configure[] = definitionConfigure ? [definitionConfigure] : [];

for (const ext of extend) {
Expand Down Expand Up @@ -137,6 +139,22 @@ function processExtensions(definition: Definition): Definition {
}
}

// Filtering out action exposes to combine them one
const actionExposes = exposes.filter((e) => e.name === 'action');
exposes = exposes.filter((e) => e.name !== 'action');
if (actionExposes.length > 0) {
const actions: string[] = [];
for (const expose of actionExposes) {
if (expose instanceof EnumClass) {
for (const action of expose.values) {
actions.push(action.toString())
}
}
}
const uniqueActions = actions.filter((value, index, array) => array.indexOf(value) === index);
exposes.push(exposesLib.presets.action(uniqueActions));
}

let configure: Configure = null;
if (configures.length !== 0) {
configure = async (device, coordinatorEndpoint, logger) => {
Expand All @@ -160,7 +178,7 @@ function prepareDefinition(definition: Definition): Definition {
toZigbee.command, toZigbee.factory_reset);

if (definition.exposes && Array.isArray(definition.exposes) && !definition.exposes.find((e) => e.name === 'linkquality')) {
definition.exposes = definition.exposes.concat([exposes.presets.linkquality()]);
definition.exposes = definition.exposes.concat([exposesLib.presets.linkquality()]);
}

validateDefinition(definition);
Expand All @@ -175,9 +193,9 @@ function prepareDefinition(definition: Definition): Definition {
// Battery voltage is not calibratable
if (expose.name === 'voltage' && expose.unit === 'mV') continue;
const type = utils.calibrateAndPrecisionRoundOptionsIsPercentual(expose.name) ? 'percentual' : 'absolute';
definition.options.push(exposes.options.calibration(expose.name, type));
definition.options.push(exposesLib.options.calibration(expose.name, type));
if (utils.calibrateAndPrecisionRoundOptionsDefaultPrecision[expose.name] !== 0) {
definition.options.push(exposes.options.precision(expose.name));
definition.options.push(exposesLib.options.precision(expose.name));
}
optionKeys.push(expose.name);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/exposes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ export const presets = {
switch_: () => new Switch(),
// Specific
ac_frequency: () => new Numeric('ac_frequency', access.STATE).withLabel('AC frequency').withUnit('Hz').withDescription('Measured electrical AC frequency'),
action: (values: string[]) => new Enum('action', access.STATE, values).withDescription('Triggered action (e.g. a button click)'),
action: (values: string[]) => new Enum('action', access.STATE, values).withDescription('Triggered action (e.g. a button click)').withCategory('diagnostic'),
action_duration: () => new Numeric('action_duration', access.STATE).withUnit('s').withDescription('Triggered action duration in seconds').withCategory('diagnostic'),
action_group: () => new Numeric('action_group', access.STATE).withDescription('Group where the action was triggered on'),
angle: (name: string) => new Numeric(name, access.STATE).withValueMin(-360).withValueMax(360).withUnit('°'),
Expand Down
24 changes: 23 additions & 1 deletion src/lib/generateDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,32 @@ const inputExtenders: Extender[] = [
[['ssIasWd'], async (d, eps) => [
new Generator({extend: m.iasWarning, source: 'iasWarning'}),
]],
[['genDeviceTempCfg'], async (d, eps) => [
new Generator({extend: m.deviceTemperature, args: maybeEndpointArgs(d, eps), source: 'deviceTemperature'}),
]],
[['pm25Measurement'], async (d, eps) => [new Generator({extend: m.pm25, args: maybeEndpointArgs(d, eps), source: 'pm25'})]],
[['msFlowMeasurement'], async (d, eps) => [new Generator({extend: m.flow, args: maybeEndpointArgs(d, eps), source: 'flow'})]],
[['msSoilMoisture'], async (d, eps) => [new Generator({extend: m.soilMoisture, args: maybeEndpointArgs(d, eps), source: 'soilMoisture'})]],
[['closuresWindowCovering'], async (d, eps) => [
new Generator({extend: m.windowCovering, args: {controls: ['lift', 'tilt']}, source: 'windowCovering'}),
]],
[['genIdentify'], async (d, eps) => [new Generator({extend: m.identify, source: 'identify'})]],
];

const outputExtenders: Extender[] = [
[['genIdentify'], async (d, eps) => [new Generator({extend: m.identify, source: 'identify'})]],
[['genOta'], async (d, eps) => [new Generator({extend: m.ota, source: 'ota'})]],
[['genOnOff'], async (d, eps) => [
new Generator({extend: m.commandsOnOff, args: maybeEndpointArgs(d, eps), source: 'commandsOnOff'}),
]],
[['genLevelCtrl'], async (d, eps) => [
new Generator({extend: m.commandsLevelCtrl, args: maybeEndpointArgs(d, eps), source: 'commandsLevelCtrl'}),
]],
[['lightingColorCtrl'], async (d, eps) => [
new Generator({extend: m.commandsColorCtrl, args: maybeEndpointArgs(d, eps), source: 'commandsColorCtrl'}),
]],
[['closuresWindowCovering'], async (d, eps) => [
new Generator({extend: m.commandsWindowCovering, args: maybeEndpointArgs(d, eps), source: 'commandsWindowCovering'}),
]],
];

async function extenderLock(device: Zh.Device, endpoints: Zh.Endpoint[]): Promise<GeneratedExtend[]> {
Expand Down
Loading

0 comments on commit 8f80a50

Please sign in to comment.