Skip to content
This repository has been archived by the owner on Oct 20, 2024. It is now read-only.

Commit

Permalink
feat: add support for motion sensor T100
Browse files Browse the repository at this point in the history
  • Loading branch information
goncaloacteixeira authored and RaresAil committed Dec 1, 2023
1 parent 4a9e220 commit 80bae06
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 2 deletions.
7 changes: 6 additions & 1 deletion src/@types/Accessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export enum AccessoryType {
export enum ChildType {
Unknown = 'Unknown',
Button = 'LightBulb',
Contact = 'Contact'
Contact = 'Contact',
MotionSensor = 'MotionSensor'
}

abstract class Accessory {
Expand Down Expand Up @@ -49,6 +50,10 @@ abstract class Accessory {
if (deviceInfo?.category?.includes('contact-sensor')) {
return ChildType.Contact;
}

if (deviceInfo?.category?.includes('motion-sensor')) {
return ChildType.MotionSensor;
}
}

return ChildType.Unknown;
Expand Down
18 changes: 18 additions & 0 deletions src/accessories/MotionSensor/characteristics/StatusActive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
CharacteristicGetHandler,
CharacteristicValue,
Nullable
} from 'homebridge';

import { AccessoryThisType } from '..';

const characteristic: {
get: CharacteristicGetHandler;
} & AccessoryThisType = {
get: async function (): Promise<Nullable<CharacteristicValue>> {
const deviceInfo = await this.getInfo();
return deviceInfo.status === 'online';
}
};

export default characteristic;
18 changes: 18 additions & 0 deletions src/accessories/MotionSensor/characteristics/StatusLowBattery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
CharacteristicGetHandler,
CharacteristicValue,
Nullable
} from 'homebridge';

import { AccessoryThisType } from '..';

const characteristic: {
get: CharacteristicGetHandler;
} & AccessoryThisType = {
get: async function (): Promise<Nullable<CharacteristicValue>> {
const deviceInfo = await this.getInfo();
return deviceInfo.at_low_battery;
}
};

export default characteristic;
111 changes: 111 additions & 0 deletions src/accessories/MotionSensor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { PlatformAccessory, Logger } from 'homebridge';

import { ChildInfo } from '../../api/@types/ChildListInfo';
import HubAccessory, { HubContext } from '../Hub';
import Accessory from '../../@types/Accessory';
import Context from '../../@types/Context';
import Platform from '../../platform';
import delay from '../../utils/delay';

import StatusLowBattery from './characteristics/StatusLowBattery';
import StatusActive from './characteristics/StatusActive';

export type AccessoryThisType = ThisType<{
readonly hub: HubAccessory;
readonly getInfo: () => Promise<ChildInfo>;
}>;

type State = {
detected: boolean;
active: boolean;
};

export default class MotionSensorAccessory extends Accessory {
private interval?: NodeJS.Timeout;
private lastEventUpdate = 0;

public get UUID() {
return this.accessory.UUID.toString();
}

private getInfo() {
return this.hub.getChildInfo(this.deviceInfo.device_id);
}

constructor(
private readonly hub: HubAccessory,
platform: Platform,
accessory: PlatformAccessory<HubContext>,
log: Logger,
deviceInfo: ChildInfo
) {
super(
platform,
accessory as unknown as PlatformAccessory<Context>,
log,
deviceInfo
);

this.accessory
.getService(this.platform.Service.AccessoryInformation)!
.setCharacteristic(
this.platform.Characteristic.Manufacturer,
'TP-Link Technologies'
)
.setCharacteristic(this.platform.Characteristic.Model, this.model)
.setCharacteristic(this.platform.Characteristic.SerialNumber, this.mac);

const service =
this.accessory.getService(this.platform.Service.MotionSensor) ||
this.accessory.addService(this.platform.Service.MotionSensor);

const motionDetected = service.getCharacteristic(
this.platform.Characteristic.MotionDetected
);
const isActive = service.getCharacteristic(
this.platform.Characteristic.StatusActive)
.onGet(StatusActive.get.bind(this));

service
.getCharacteristic(this.platform.Characteristic.StatusLowBattery)
.onGet(StatusLowBattery.get.bind(this));

const checkStatus = async (initStatus?: State) => {
if (initStatus) {
motionDetected.updateValue(initStatus.detected);
isActive.updateValue(initStatus.active);
}

try {
const response = await this.getInfo();
if (!response) {
this.log.warn('Failed to check for updates, delaying 500ms');
await delay(500);
}

motionDetected.updateValue(response.detected);
} catch (error) {
this.log.error('Failed to check for updates', error);
await delay(500);
}
};

this.setup(checkStatus.bind(this));
}

cleanup() {
clearInterval(this.interval!);
}

private async setup(callback: (x?: State) => Promise<void>) {
const init = await this.getInfo();

await callback({
detected: init.detected,
active: init.status === 'online'
});
this.interval = setInterval(() => {
callback();
}, 5);
}
}
1 change: 1 addition & 0 deletions src/api/@types/ChildListInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface ChildInfo {
avatar: string;
report_interval: number;
region: string;
detected: boolean;
}

interface ChildListInfo {
Expand Down
4 changes: 3 additions & 1 deletion src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import OutletAccessory from './accessories/Outlet';
import { ChildInfo } from './api/@types/ChildListInfo';
import ButtonAccessory from './accessories/Button';
import ContactAccessory from './accessories/Contact';
import MotionSensorAccessory from './accessories/MotionSensor';

export default class Platform implements DynamicPlatformPlugin {
private readonly TIMEOUT_TRIES = 20;
Expand Down Expand Up @@ -360,7 +361,8 @@ export default class Platform implements DynamicPlatformPlugin {

private readonly childClasses = {
[ChildType.Button]: ButtonAccessory,
[ChildType.Contact]: ContactAccessory
[ChildType.Contact]: ContactAccessory,
[ChildType.MotionSensor]: MotionSensorAccessory,
};

private registerChild(
Expand Down

0 comments on commit 80bae06

Please sign in to comment.