homebridge-freeathome-local-api
Version:
Control your free@home setup using the local API provided by your System Access Point
259 lines (247 loc) • 8.27 kB
text/typescript
import { Channel, Device } from "freeathome-local-api-client";
import {
Characteristic,
CharacteristicValue,
Service,
WithUUID,
PlatformAccessory,
} from "homebridge";
import { FreeAtHomeContext } from "../src/freeAtHomeContext.js";
import { MotionSensorAccessory } from "../src/motionSensorAccessory.js";
import {
createPlatformAccessory,
MockFreeAtHomeHomebridgePlatform,
} from "./platform.mock.js";
describe("Motion Sensor Accessory", () => {
let channel: Channel;
let device: Device;
let platform: MockFreeAtHomeHomebridgePlatform;
let platformAccessory: PlatformAccessory<FreeAtHomeContext>;
beforeEach(() => {
channel = {};
device = {};
platform = new MockFreeAtHomeHomebridgePlatform();
platformAccessory = createPlatformAccessory("Motion Sensor Accessory");
platformAccessory.context = {
channel: channel,
channelId: "ch1234",
device: device,
deviceSerial: "ABB7xxxxxxxx",
};
});
afterEach(() => {
platform.resetLoggerCalls();
jasmine.clock().uninstall();
});
it("should throw if expected data points are missing on channel", () => {
channel.outputs = undefined;
expect(
() => new MotionSensorAccessory(platform, platformAccessory)
).toThrowError("Channel lacks expected input or output data points.");
});
it("should be created", async () => {
channel.outputs = {
odp123: {
pairingID: 6,
value: "0",
},
};
const accessory = new MotionSensorAccessory(platform, platformAccessory);
expect(accessory).toBeTruthy();
const instance = accessory as unknown as {
service: Service;
};
const characteristic = instance.service.getCharacteristic(
accessory.platform.Characteristic.MotionDetected
);
expect(await characteristic.handleGetRequest()).toBeFalse();
});
it("should be created with non-default state", async () => {
channel.outputs = {
odp123: {
pairingID: 6,
value: "1",
},
};
const accessory = new MotionSensorAccessory(platform, platformAccessory);
expect(accessory).toBeTruthy();
const instance = accessory as unknown as {
service: Service;
};
const characteristic = instance.service.getCharacteristic(
accessory.platform.Characteristic.MotionDetected
);
expect(await characteristic.handleGetRequest()).toBeTrue();
});
it("should ignore update if datapoint is unknown", async () => {
channel.outputs = {
odp123: {
pairingID: 6,
value: "0",
},
};
const accessory = new MotionSensorAccessory(platform, platformAccessory);
const instance = accessory as unknown as {
service: Service;
doUpdateDatapoint: (
acccessoryDisplayType: string,
service: Service,
characteristic: WithUUID<new () => Characteristic>,
characteristicValue: CharacteristicValue
) => void;
};
const spy = spyOn(instance, "doUpdateDatapoint");
const characteristic = instance.service.getCharacteristic(
accessory.platform.Characteristic.MotionDetected
);
expect(await characteristic.handleGetRequest()).toBeFalse();
accessory.updateDatapoint("test", "0");
expect(spy).not.toHaveBeenCalled();
});
it("should process update to known datapoint", async () => {
channel.outputs = {
odp123: {
pairingID: 6,
value: "0",
},
};
const accessory = new MotionSensorAccessory(platform, platformAccessory);
const instance = accessory as unknown as {
service: Service;
doUpdateDatapoint: (
acccessoryDisplayType: string,
service: Service,
characteristic: WithUUID<new () => Characteristic>,
characteristicValue: CharacteristicValue
) => void;
};
const spy = spyOn(instance, "doUpdateDatapoint");
const characteristic = instance.service.getCharacteristic(
accessory.platform.Characteristic.MotionDetected
);
expect(await characteristic.handleGetRequest()).toBeFalse();
accessory.updateDatapoint("odp123", "1");
expect(spy).toHaveBeenCalledWith(
"Motion Sensor",
instance.service,
accessory.platform.Characteristic.MotionDetected,
true
);
});
it("should not set a reset timer if the sensor is reset manually", async () => {
platform.config.motionSensorAutoReset = true;
channel.outputs = {
odp123: {
pairingID: 6,
value: "0",
},
};
const accessory = new MotionSensorAccessory(platform, platformAccessory);
const instance = accessory as unknown as {
resetTimeout?: NodeJS.Timeout;
service: Service;
doUpdateDatapoint: (
acccessoryDisplayType: string,
service: Service,
characteristic: WithUUID<new () => Characteristic>,
characteristicValue: CharacteristicValue
) => void;
};
const spy = spyOn(instance, "doUpdateDatapoint");
const characteristic = instance.service.getCharacteristic(
accessory.platform.Characteristic.MotionDetected
);
expect(await characteristic.handleGetRequest()).toBeFalse();
expect(instance.resetTimeout).toBeUndefined();
accessory.updateDatapoint("odp123", "0");
expect(spy).toHaveBeenCalledWith(
"Motion Sensor",
instance.service,
accessory.platform.Characteristic.MotionDetected,
false
);
expect(instance.resetTimeout).toBeUndefined();
});
it("should cancel a set reset timer if the sensor is reset manually", async () => {
jasmine.clock().install();
platform.config.motionSensorAutoReset = true;
channel.outputs = {
odp123: {
pairingID: 6,
value: "0",
},
};
const accessory = new MotionSensorAccessory(platform, platformAccessory);
const instance = accessory as unknown as {
resetTimeout?: NodeJS.Timeout;
service: Service;
doUpdateDatapoint: (
acccessoryDisplayType: string,
service: Service,
characteristic: WithUUID<new () => Characteristic>,
characteristicValue: CharacteristicValue
) => void;
};
const spy = spyOn(instance, "doUpdateDatapoint");
const characteristic = instance.service.getCharacteristic(
accessory.platform.Characteristic.MotionDetected
);
expect(await characteristic.handleGetRequest()).toBeFalse();
instance.resetTimeout = setTimeout(
() => fail("Timer should be cancelled"),
10000
);
accessory.updateDatapoint("odp123", "0");
expect(spy).toHaveBeenCalledWith(
"Motion Sensor",
instance.service,
accessory.platform.Characteristic.MotionDetected,
false
);
expect(instance.resetTimeout).toBeUndefined();
jasmine.clock().tick(15000);
});
it("should reset the sensor automatically after the specified time", async () => {
jasmine.clock().install();
platform.config.motionSensorAutoReset = true;
channel.outputs = {
odp123: {
pairingID: 6,
value: "0",
},
};
const accessory = new MotionSensorAccessory(platform, platformAccessory);
const instance = accessory as unknown as {
resetTimeout?: NodeJS.Timeout;
service: Service;
doUpdateDatapoint: (
acccessoryDisplayType: string,
service: Service,
characteristic: WithUUID<new () => Characteristic>,
characteristicValue: CharacteristicValue
) => void;
};
const spy = spyOn(instance, "doUpdateDatapoint");
const characteristic = instance.service.getCharacteristic(
accessory.platform.Characteristic.MotionDetected
);
expect(await characteristic.handleGetRequest()).toBeFalse();
expect(instance.resetTimeout).toBeUndefined();
accessory.updateDatapoint("odp123", "1");
expect(spy).toHaveBeenCalledWith(
"Motion Sensor",
instance.service,
accessory.platform.Characteristic.MotionDetected,
true
);
expect(instance.resetTimeout).toBeDefined();
jasmine.clock().tick(20000);
expect(instance.resetTimeout).toBeUndefined();
expect(spy).toHaveBeenCalledWith(
"Motion Sensor",
instance.service,
accessory.platform.Characteristic.MotionDetected,
false
);
});
});