Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Niko switch action reporting functionality #8635

Merged
merged 7 commits into from
Jan 27, 2025
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions src/devices/niko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,16 @@ const local = {
convert: (model, msg, publish, options, meta) => {
const state: KeyValue = {};

if (msg.data.switchActionReporting !== undefined) {
const actionReportingMap: KeyValue = {0x00: false, 0x1f: true};
state['action_reporting'] = utils.getFromLookup(msg.data.switchActionReporting, actionReportingMap);
}
if (msg.data.switchAction !== undefined) {
// NOTE: a single press = two separate values reported, 16 followed by 64
// a hold/release cycle = three separate values, 16, 32, and 48
// NOTE: these values should be interpreted bitwise
// when pushing multiple buttons at the same time, multiple bits can be set simultaneously and should generate multiple events
// currently, these values are not mapped and thus ignored
svenjochems marked this conversation as resolved.
Show resolved Hide resolved
const actionMap: KeyValue =
model.model == '552-721X1'
? {
Expand Down Expand Up @@ -122,6 +129,26 @@ const local = {
await utils.enforceEndpoint(entity, key, meta).read('manuSpecificNiko1', ['switchOperationMode']);
},
} satisfies Tz.Converter,
switch_action_reporting: {
key: ['action_reporting'],
convertSet: async (entity, key, value, meta) => {
// @ts-expect-error ignore
if (actionReportingMap[value] === undefined) {
throw new Error(`action_reporting was called with an invalid value (${value})`);
} else {
await entity.write(
'manuSpecificNiko2',
// @ts-expect-error ignore
{switchActionReporting: actionReportingMap[value]},
);
await entity.read('manuSpecificNiko2', ['switchActionReporting']);
return {state: {action_reporting: value}};
}
},
convertGet: async (entity, key, meta) => {
await entity.read('manuSpecificNiko2', ['switchActionReporting']);
},
} satisfies Tz.Converter,
switch_led_enable: {
key: ['led_enable'],
convertSet: async (entity, key, value, meta) => {
Expand Down Expand Up @@ -276,17 +303,27 @@ const definitions: DefinitionWithExtend[] = [
vendor: 'Niko',
description: 'Single connectable switch',
fromZigbee: [fz.on_off, local.fz.switch_operation_mode, local.fz.switch_action, local.fz.switch_status_led],
toZigbee: [tz.on_off, local.tz.switch_operation_mode, local.tz.switch_led_enable, local.tz.switch_led_state],
toZigbee: [
tz.on_off,
local.tz.switch_operation_mode,
local.tz.switch_action_reporting,
local.tz.switch_led_enable,
local.tz.switch_led_state,
],
configure: async (device, coordinatorEndpoint) => {
const endpoint = device.getEndpoint(1);
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
await reporting.onOff(endpoint);
await endpoint.read('manuSpecificNiko1', ['switchOperationMode', 'outletLedState', 'outletLedColor']);
// Enable action reporting by default
await endpoint.write('manuSpecificNiko2', {switchActionReporting: true});
await endpoint.read('manuSpecificNiko2', ['switchActionReporting']);
},
exposes: [
e.switch(),
e.action(['single', 'hold', 'release', 'single_ext', 'hold_ext', 'release_ext']),
e.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled']),
e.binary('action_reporting', ea.ALL, true, false),
e.binary('led_enable', ea.ALL, true, false).withDescription('Enable LED'),
e.binary('led_state', ea.ALL, 'ON', 'OFF').withDescription('LED State'),
],
Expand All @@ -297,7 +334,13 @@ const definitions: DefinitionWithExtend[] = [
vendor: 'Niko',
description: 'Double connectable switch',
fromZigbee: [fz.on_off, local.fz.switch_operation_mode, local.fz.switch_action, local.fz.switch_status_led],
toZigbee: [tz.on_off, local.tz.switch_operation_mode, local.tz.switch_led_enable, local.tz.switch_led_state],
toZigbee: [
tz.on_off,
local.tz.switch_operation_mode,
local.tz.switch_action_reporting,
local.tz.switch_led_enable,
local.tz.switch_led_state,
],
endpoint: (device) => {
return {l1: 1, l2: 2};
},
Expand All @@ -311,6 +354,9 @@ const definitions: DefinitionWithExtend[] = [
await reporting.onOff(ep2);
await ep1.read('manuSpecificNiko1', ['switchOperationMode', 'outletLedState', 'outletLedColor']);
await ep2.read('manuSpecificNiko1', ['switchOperationMode', 'outletLedState', 'outletLedColor']);
// Enable action reporting by default
await ep1.write('manuSpecificNiko2', {switchActionReporting: true});
await ep1.read('manuSpecificNiko2', ['switchActionReporting']);
},
exposes: [
e.switch().withEndpoint('l1'),
Expand All @@ -330,6 +376,7 @@ const definitions: DefinitionWithExtend[] = [
'release_right_ext',
]),
e.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled']),
e.binary('action_reporting', ea.ALL, true, false),
svenjochems marked this conversation as resolved.
Show resolved Hide resolved
e.binary('led_enable', ea.ALL, true, false).withEndpoint('l1').withDescription('Enable LED'),
e.binary('led_enable', ea.ALL, true, false).withEndpoint('l2').withDescription('Enable LED'),
e.binary('led_state', ea.ALL, 'ON', 'OFF').withEndpoint('l1').withDescription('LED State'),
Expand Down
Loading