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

feat: add notification variable for tilt #6958

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
23 changes: 23 additions & 0 deletions packages/cc/src/cc/NotificationCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,29 @@ export const NotificationCCValues = Object.freeze({
autoCreate: shouldAutoCreateSimpleDoorSensorValue,
} as const,
),

// Binary tilt value extracted from the Door state variable.
...V.staticPropertyAndKeyWithName(
"doorTiltState",
"Access Control",
"Door tilt state",
{
// Must be a number for compatibility reasons
...ValueMetadata.ReadOnlyUInt8,
label: "Door tilt state",
states: {
[0x00]: "Window/door is not tilted",
[0x01]: "Window/door is tilted",
},
ccSpecific: {
notificationType: 0x06,
},
} as const,
{
// This is created when the tilt state is first received.
autoCreate: false,
} as const,
),
}),

...V.defineDynamicCCValues(CommandClasses.Notification, {
Expand Down
20 changes: 19 additions & 1 deletion packages/zwave-js/src/lib/node/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5252,15 +5252,33 @@ protocol version: ${this.protocolVersion}`;
// actually support them, which makes working with the Door state variable
// very cumbersome. Also, this is currently the only notification where the enum values
// extend the state value.

// To work around this, we hard-code a notification value for the door status
// which only includes the "legacy" states for open/closed.

this.valueDB.setValue(
NotificationCCValues.doorStateSimple.endpoint(
command.endpointIndex,
),
command.notificationEvent === 0x17 ? 0x17 : 0x16,
);

// In addition to that, we also hard-code a notification value for only the tilt status.
// This will only be created after receiving a notification for the tilted state.
// Only after it exists, it will be updated. Otherwise, we'd get phantom
// values, since some devices send the enum value, even when they don't support tilt.
const tiltValue = NotificationCCValues.doorTiltState;
const tiltValueId = tiltValue.endpoint(command.endpointIndex);
let tiltValueWasCreated = this.valueDB.hasMetadata(tiltValueId);
if (command.eventParameters === 0x01 && !tiltValueWasCreated) {
this.valueDB.setMetadata(tiltValueId, tiltValue.meta);
tiltValueWasCreated = true;
}
if (tiltValueWasCreated) {
this.valueDB.setValue(
tiltValueId,
command.eventParameters === 0x01 ? 0x01 : 0x00,
);
}
}
}

Expand Down
165 changes: 165 additions & 0 deletions packages/zwave-js/src/lib/test/cc-specific/notificationEnums.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,171 @@ integrationTest("The 'simple' Door state value works correctly", {
},
});

integrationTest("The synthetic 'Door tilt state' value works correctly", {
// debug: true,

nodeCapabilities: {
commandClasses: [
CommandClasses.Version,
{
ccId: CommandClasses.Notification,
isSupported: true,
version: 8,
supportsV1Alarm: false,
notificationTypesAndEvents: {
// Access Control - Window open and Window closed
[0x06]: [0x16, 0x17],
},
},
],
},

testBody: async (t, driver, node, mockController, mockNode) => {
await node.commandClasses.Notification.getSupportedEvents(0x06);

const tiltVID = NotificationCCValues.doorTiltState.id;

const hasTiltVID = () =>
node.getDefinedValueIDs().some(
(vid) => NotificationCCValues.doorTiltState.is(vid),
);
// Before receiving any notifications with the tilt enum, the synthetic value should not exist
t.false(hasTiltVID());

// Send a notification to the node where the window is not tilted
let cc = new NotificationCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
notificationType: 0x06,
notificationEvent: 0x16, // Window/door is open
eventParameters: Buffer.from([0x00]), // ... in regular position
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
// wait a bit for the value to be updated
await wait(100);

// The value should still not exist
t.false(hasTiltVID());

// ===

// Again with tilt
cc = new NotificationCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
notificationType: 0x06,
notificationEvent: 0x16, // Window/door is open
eventParameters: Buffer.from([0x01]), // ... in tilt position
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
// wait a bit for the value to be updated
await wait(100);

// The value should now exist
t.true(hasTiltVID());
t.is(node.getValue(tiltVID), 0x01);

// ===

// Again without tilt
cc = new NotificationCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
notificationType: 0x06,
notificationEvent: 0x16, // Window/door is open
eventParameters: Buffer.from([0x00]), // ... in regular position
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
// wait a bit for the value to be updated
await wait(100);

t.is(node.getValue(tiltVID), 0x00);

// ===

// Again with tilt to be able to detect changes
cc = new NotificationCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
notificationType: 0x06,
notificationEvent: 0x16, // Window/door is open
eventParameters: Buffer.from([0x01]), // ... in tilt position
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
// wait a bit for the value to be updated
await wait(100);

t.is(node.getValue(tiltVID), 0x01);

// ===

// And now without the enum
cc = new NotificationCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
notificationType: 0x06,
notificationEvent: 0x17, // Window/door is closed
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
// wait a bit for the value to be updated
await wait(100);

t.is(node.getValue(tiltVID), 0x00);

// ===

// Again with tilt to be able to detect changes
cc = new NotificationCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
notificationType: 0x06,
notificationEvent: 0x16, // Window/door is open
eventParameters: Buffer.from([0x01]), // ... in tilt position
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
// wait a bit for the value to be updated
await wait(100);

t.is(node.getValue(tiltVID), 0x01);

// ===

// And again without the enum
cc = new NotificationCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
notificationType: 0x06,
notificationEvent: 0x16, // Window/door is open
});
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
// wait a bit for the value to be updated
await wait(100);

t.is(node.getValue(tiltVID), 0x00);
},
});

integrationTest(
"Notification types with 'replace'-type enums fall back to the default value if the event parameter is not contained in the CC",
{
Expand Down
Loading