Skip to content

Commit

Permalink
fix: create supportedNotificationTypes and `supportedNotificationEv…
Browse files Browse the repository at this point in the history
…ents` values with `alarmMapping` compat flag (#6914)
  • Loading branch information
AlCalzone authored Jun 10, 2024
1 parent 5feb4ab commit ec221e9
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 76 deletions.
27 changes: 27 additions & 0 deletions packages/cc/src/cc/NotificationCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ export class NotificationCC extends CommandClass {
?.compat?.alarmMapping;
if (mappings) {
// Find all mappings to a valid notification variable
const supportedNotifications = new Map<number, Set<number>>();
for (const { to } of mappings) {
const notificationConfig = applHost.configManager
.lookupNotification(
Expand All @@ -748,6 +749,18 @@ export class NotificationCC extends CommandClass {
const valueConfig = notificationConfig.lookupValue(
to.notificationEvent,
);

// Remember supported notification types and events to create the internal values later
if (!supportedNotifications.has(to.notificationType)) {
supportedNotifications.set(
to.notificationType,
new Set(),
);
}
const supportedNotificationTypesSet = supportedNotifications
.get(to.notificationType)!;
supportedNotificationTypesSet.add(to.notificationEvent);

if (valueConfig?.type !== "state") continue;

const notificationValue = NotificationCCValues
Expand Down Expand Up @@ -780,6 +793,20 @@ export class NotificationCC extends CommandClass {
}
}
}

// Remember supported notification types and events in the cache
this.setValue(
applHost,
NotificationCCValues.supportedNotificationTypes,
[...supportedNotifications.keys()],
);
for (const [type, events] of supportedNotifications) {
this.setValue(
applHost,
NotificationCCValues.supportedNotificationEvents(type),
[...events],
);
}
}

// Remember that the interview is complete
Expand Down
136 changes: 60 additions & 76 deletions packages/zwave-js/src/lib/test/compat/notificationAlarmMapping.test.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,72 @@
import { CommandClass } from "@zwave-js/cc";
import { ManufacturerSpecificCCValues } from "@zwave-js/cc/ManufacturerSpecificCC";
import { NotificationCCReport } from "@zwave-js/cc/NotificationCC";
import {
NotificationCCReport,
NotificationCCValues,
} from "@zwave-js/cc/NotificationCC";
import { CommandClasses } from "@zwave-js/core";
import type { ThrowingMap } from "@zwave-js/shared";
import { MockController } from "@zwave-js/testing";
import ava, { type TestFn } from "ava";
import { createDefaultMockControllerBehaviors } from "../../../Utils";
import type { Driver } from "../../driver/Driver";
import { createAndStartTestingDriver } from "../../driver/DriverMock";
import { ZWaveNode } from "../../node/Node";
import { createMockZWaveRequestFrame } from "@zwave-js/testing";
import { wait } from "alcalzone-shared/async";
import sinon from "sinon";
import { integrationTest } from "../integrationTestSuite";

interface TestContext {
driver: Driver;
node2: ZWaveNode;
controller: MockController;
}
integrationTest(
"the alarmMapping compat flag works correctly (using the example Kwikset 910)",
{
// debug: true,

const test = ava as TestFn<TestContext>;
nodeCapabilities: {
manufacturerId: 0x90,
productType: 0x01,
productId: 0x01,

test.beforeEach(async (t) => {
t.timeout(30000);

const { driver } = await createAndStartTestingDriver({
skipNodeInterview: true,
beforeStartup(mockPort) {
const controller = new MockController({ serial: mockPort });
controller.defineBehavior(
...createDefaultMockControllerBehaviors(),
);
t.context.controller = controller;
commandClasses: [
{
ccId: CommandClasses.Notification,
version: 1, // To make sure we rely on the compat flag
isSupported: true,
},
CommandClasses["Manufacturer Specific"],
CommandClasses.Version,
],
},
});

const node2 = new ZWaveNode(2, driver);
(driver.controller.nodes as ThrowingMap<number, ZWaveNode>).set(
node2.id,
node2,
);

node2.addCC(CommandClasses.Notification, {
isSupported: true,
version: 1,
});

t.context.driver = driver;
t.context.node2 = node2;
});
async testBody(t, driver, node, mockController, mockNode) {
// Send a report that should be mapped to notifications
const cc = new NotificationCCReport(mockNode.host, {
nodeId: 2,
alarmType: 18,
alarmLevel: 2,
});

test.afterEach.always(async (t) => {
const { driver } = t.context;
await driver.destroy();
driver.removeAllListeners();
});
const nodeNotification = sinon.spy();
node.on("notification", nodeNotification);

test("the alarmMapping compat flag works correctly (using the example Kwikset 910)", async (t) => {
const { driver, node2 } = t.context;

node2.valueDB.setValue(
ManufacturerSpecificCCValues.manufacturerId.id,
0x90,
);
node2.valueDB.setValue(ManufacturerSpecificCCValues.productType.id, 0x01);
node2.valueDB.setValue(ManufacturerSpecificCCValues.productId.id, 0x01);
await node2["loadDeviceConfig"]();
await mockNode.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
await wait(100);

const rawNotification = new NotificationCCReport(driver, {
nodeId: 2,
alarmType: 18,
alarmLevel: 2,
});
const serialized = rawNotification.serialize();
// The correct events should be emitted
sinon.assert.calledOnce(nodeNotification);
const event = nodeNotification.getCall(0).args[2];

const deserialized = CommandClass.from(driver, {
data: serialized,
nodeId: 2,
}) as NotificationCCReport;
t.is(event.type, 0x06);
t.is(event.event, 0x05);
t.deepEqual(event.parameters, {
userId: 2,
});

// Call persistValues to trigger the mapping
deserialized.persistValues(driver);
// And they should be known to be supported
const supportedNotificationTypes: number[] | undefined = node
.getValue(NotificationCCValues.supportedNotificationTypes.id);
t.true(supportedNotificationTypes?.includes(0x06));

// Keypad lock
t.is(deserialized.notificationType, 0x06);
t.is(deserialized.notificationEvent, 0x05);
t.deepEqual(deserialized.eventParameters, {
userId: 2,
});
});
const supportedAccessControlEvents: number[] | undefined = node
.getValue(
NotificationCCValues.supportedNotificationEvents(0x06).id,
);
t.true(supportedAccessControlEvents?.includes(0x05));
},
},
);

0 comments on commit ec221e9

Please sign in to comment.