-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Generate definition for unsupported devices (disabled for now) (#…
…6692) * feat: Generate definition for unknown devices * add test * fix definition processing * Update definition generation to be dynamic * Update generator * Add tests * update extenders * update generation tests * fix lint * Update generateDefinition.ts * Update index.ts * small refactor --------- Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
- Loading branch information
Showing
8 changed files
with
369 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import {Cluster} from 'zigbee-herdsman/dist/zcl/tstype'; | ||
import {Definition, ModernExtend, Zh} from './types'; | ||
import * as e from './modernExtend'; | ||
import {Endpoint} from 'zigbee-herdsman/dist/controller/model'; | ||
|
||
export function generateDefinition(device: Zh.Device): Definition { | ||
const deviceExtenders: ModernExtend[] = []; | ||
|
||
device.endpoints.forEach((endpoint) => { | ||
const addExtenders = (cluster: Cluster, knownExtenders: extendersObject) => { | ||
const clusterName = cluster.name || cluster.ID.toString(); | ||
if (!knownExtenders.hasOwnProperty(clusterName)) { | ||
return; | ||
} | ||
|
||
const extenderProviders = knownExtenders[clusterName]; | ||
const extenders = extenderProviders.map((extender: extenderProvider): ModernExtend => { | ||
if (typeof extender !== 'function') { | ||
return extender; | ||
} | ||
return extender(endpoint, cluster); | ||
}); | ||
|
||
deviceExtenders.push(...(extenders)); | ||
}; | ||
|
||
endpoint.getInputClusters().forEach((cluster) => { | ||
addExtenders(cluster, inputExtenders); | ||
}); | ||
endpoint.getOutputClusters().forEach((cluster) => { | ||
addExtenders(cluster, outputExtenders); | ||
}); | ||
}); | ||
|
||
const definition: Definition = { | ||
zigbeeModel: [device.modelID], | ||
model: device.modelID ?? '', | ||
vendor: device.manufacturerName ?? '', | ||
description: 'Generated from device information', | ||
extend: deviceExtenders, | ||
generated: true, | ||
}; | ||
|
||
return definition; | ||
} | ||
|
||
// This configurator type provides some flexibility in terms of how ModernExtend configuration can be obtained. | ||
// I.e. if cluster has optional attributes - this type can be used | ||
// to define function that will generate more feature-full extension. | ||
type extenderConfigurator = (endpoint: Endpoint, cluster: Cluster) => ModernExtend | ||
// extenderProvider defines a type that will produce a `ModernExtend` | ||
// either directly, or by calling a function. | ||
type extenderProvider = ModernExtend | extenderConfigurator | ||
type extendersObject = {[name: string]: extenderProvider[]} | ||
|
||
const inputExtenders: extendersObject = { | ||
'msTemperatureMeasurement': [e.temperature()], | ||
'msPressureMeasurement': [e.pressure()], | ||
'msRelativeHumidity': [e.humidity()], | ||
'genOnOff': [e.onOff({powerOnBehavior: false})], | ||
}; | ||
|
||
const outputExtenders: extendersObject = { | ||
'genIdentify': [e.identify()], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import { Definition } from '../src/lib/types'; | ||
import fz from '../src/converters/fromZigbee' | ||
|
||
import { repInterval } from '../src/lib/constants'; | ||
import {assertDefintion, AssertDefinitionArgs, mockDevice, reportingItem} from './utils'; | ||
import { findByDevice} from '../src'; | ||
import Device from 'zigbee-herdsman/dist/controller/model/device'; | ||
|
||
const assertGeneratedDefinition = async (args: AssertDefinitionArgs) => { | ||
const getDefinition = (device: Device): Definition => { | ||
return findByDevice(device, true); | ||
} | ||
|
||
const definition = getDefinition(args.device) | ||
|
||
expect(definition.model).toEqual(args.device.modelID) | ||
|
||
return await assertDefintion({findByDeviceFn: getDefinition, ...args}) | ||
} | ||
|
||
describe('GenerateDefinition', () => { | ||
test('empty', async () => { | ||
await assertGeneratedDefinition({ | ||
device: mockDevice({modelID: 'empty', endpoints: [{inputClusters: [], outputClusters:[]}]}), | ||
meta: undefined, | ||
fromZigbee: [], | ||
toZigbee: [], | ||
exposes: ['linkquality'], | ||
bind: [], | ||
read: [], | ||
configureReporting: [], | ||
}); | ||
}); | ||
|
||
test('input(msTemperatureMeasurement),output(genIdentify)', async () => { | ||
await assertGeneratedDefinition({ | ||
device: mockDevice({modelID: 'temp', endpoints: [{inputClusters: ['msTemperatureMeasurement'], outputClusters:['genIdentify']}]}), | ||
meta: undefined, | ||
fromZigbee: [expect.objectContaining({cluster: 'msTemperatureMeasurement'})], | ||
toZigbee: ['temperature', 'identify'], | ||
exposes: ['linkquality', 'temperature'], | ||
bind: {1: ['msTemperatureMeasurement']}, | ||
read: {1: [['msTemperatureMeasurement', ['measuredValue']]]}, | ||
configureReporting: { | ||
1: [ | ||
['msTemperatureMeasurement', [reportingItem('measuredValue', 10, repInterval.HOUR, 100)]], | ||
], | ||
}, | ||
}); | ||
}); | ||
|
||
test('input(msPressureMeasurement)', async () => { | ||
await assertGeneratedDefinition({ | ||
device: mockDevice({modelID: 'pressure', endpoints: [{inputClusters: ['msPressureMeasurement'], outputClusters:[]}]}), | ||
meta: undefined, | ||
fromZigbee: [expect.objectContaining({cluster: 'msPressureMeasurement'})], | ||
toZigbee: ['pressure'], | ||
exposes: ['linkquality', 'pressure'], | ||
bind: {1: ['msPressureMeasurement']}, | ||
read: {1: [['msPressureMeasurement', ['measuredValue']]]}, | ||
configureReporting: { | ||
1: [ | ||
['msPressureMeasurement', [reportingItem('measuredValue', 10, repInterval.HOUR, 100)]], | ||
], | ||
}, | ||
}); | ||
}); | ||
|
||
test('input(msRelativeHumidity)', async () => { | ||
await assertGeneratedDefinition({ | ||
device: mockDevice({modelID: 'humidity', endpoints: [{inputClusters: ['msRelativeHumidity'], outputClusters:[]}]}), | ||
meta: undefined, | ||
fromZigbee: [expect.objectContaining({cluster: 'msRelativeHumidity'})], | ||
toZigbee: ['humidity'], | ||
exposes: ['humidity', 'linkquality'], | ||
bind: {1: ['msRelativeHumidity']}, | ||
read: {1: [['msRelativeHumidity', ['measuredValue']]]}, | ||
configureReporting: { | ||
1: [ | ||
['msRelativeHumidity', [reportingItem('measuredValue', 10, repInterval.HOUR, 100)]], | ||
], | ||
}, | ||
}); | ||
}); | ||
|
||
test('input(msTemperatureMeasurement, genOnOff)', async () => { | ||
await assertGeneratedDefinition({ | ||
device: mockDevice({modelID: 'combo', endpoints: [{inputClusters: ['msTemperatureMeasurement', 'genOnOff'], outputClusters:[]}]}), | ||
meta: undefined, | ||
fromZigbee: [expect.objectContaining({cluster: 'msTemperatureMeasurement'}), fz.on_off], | ||
toZigbee: ['temperature', 'state', 'on_time', 'off_wait_time'], | ||
exposes: ['linkquality', 'switch(state)', 'temperature'], | ||
bind: {1: ['msTemperatureMeasurement', 'genOnOff']}, | ||
read: {1: [ | ||
['msTemperatureMeasurement', ['measuredValue']], | ||
['genOnOff', ['onOff']], | ||
]}, | ||
configureReporting: { | ||
1: [ | ||
['msTemperatureMeasurement', [reportingItem('measuredValue', 10, repInterval.HOUR, 100)]], | ||
['genOnOff', [reportingItem('onOff', 0, repInterval.MAX, 1)]], | ||
], | ||
}, | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.