Skip to content

Commit

Permalink
Allow to enable/disable motion detection via mqtt
Browse files Browse the repository at this point in the history
  • Loading branch information
beele committed Jun 28, 2021
1 parent 6f5e8c0 commit 6198567
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 9 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "homebridge-unifi-protect-camera-motion",
"version": "0.4.4",
"version": "0.4.5",
"description": "Unifi Protect cameras & motion sensors for Homebridge. AI enabled Motion detection for Unifi Protect cameras.",
"main": "src/index.js",
"scripts": {
Expand Down
13 changes: 11 additions & 2 deletions src/characteristics/unifi-camera-motion-sensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
PlatformAccessory,
} from "homebridge";
import {CameraConfig} from "../streaming/camera-config";
import { Mqtt } from "../utils/mqtt";

export class UnifiCameraMotionSensor {

public static setupMotionSensor(cameraConfig: CameraConfig, accessory: PlatformAccessory, config: any, hap: HAP, log: Logging): void {
public static setupMotionSensor(cameraConfig: CameraConfig, accessory: PlatformAccessory, config: any, mqtt: Mqtt, hap: HAP, log: Logging): void {
const Service = hap.Service;

const motion = accessory.getService(hap.Service.MotionSensor);
Expand All @@ -30,13 +31,21 @@ export class UnifiCameraMotionSensor {
accessory.addService(new Service.MotionSensor(cameraConfig.name + ' Motion sensor'));
accessory.addService(motionSwitch);

mqtt.subscribeToTopic(cameraConfig.name + '/set', (payload: {enabled: boolean}) => {
if (payload && payload.enabled !== undefined) {
accessory.context.motionEnabled = payload.enabled;
log.info('Motion detection for ' + cameraConfig.name + ' has been turned ' + (accessory.context.motionEnabled ? 'ON' : 'OFF'));
}
});

motionSwitch
.getCharacteristic(hap.Characteristic.On)
.on(hap.CharacteristicEventTypes.GET, (callback: CharacteristicSetCallback) => {
callback(null, accessory.context.motionEnabled);
})
.on(hap.CharacteristicEventTypes.SET, (state: CharacteristicValue, callback: CharacteristicSetCallback) => {
accessory.context.motionEnabled = state;
mqtt.sendMessageOnTopic(JSON.stringify({enabled: accessory.context.motionEnabled}), cameraConfig.name + '/set');
log.info('Motion detection for ' + cameraConfig.name + ' has been turned ' + (accessory.context.motionEnabled ? 'ON' : 'OFF'));
callback();
});
Expand All @@ -62,4 +71,4 @@ export class UnifiCameraMotionSensor {
});
}
}
}
}
4 changes: 2 additions & 2 deletions src/motion/motion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class MotionDetector {
private mqtt: Mqtt;
private configuredAccessories: any[];

constructor(api: API, config: PlatformConfig, unifiFlows: UnifiFlows, cameras: UnifiCamera[], log: Logging) {
constructor(api: API, config: PlatformConfig, mqtt: Mqtt, unifiFlows: UnifiFlows, cameras: UnifiCamera[], log: Logging) {
this.api = api;

this.config = config;
Expand All @@ -41,7 +41,7 @@ export class MotionDetector {
const userStoragePath: string = this.api.user.storagePath();
ImageUtils.userStoragePath = userStoragePath;
this.gPhotos = config.upload_gphotos && this.googlePhotosConfig ? new GooglePhotos(config.googlePhotos as GooglePhotosConfig, userStoragePath, log) : null;
this.mqtt = new Mqtt(config.mqtt_enabled ? config.mqtt : null , log);
this.mqtt = mqtt;
}

public async setupMotionChecking(configuredAccessories: PlatformAccessory[]): Promise<any> {
Expand Down
9 changes: 6 additions & 3 deletions src/unifi-protect-motion-platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import {UnifiCameraMotionSensor} from "./characteristics/unifi-camera-motion-sen
import {UnifiCameraDoorbell} from "./characteristics/unifi-camera-doorbell";
import {UnifiCameraStreaming} from "./streaming/unifi-camera-streaming";
import {UnifiStreamingDelegate} from "./streaming/unifi-streaming-delegate";
import { Mqtt } from './utils/mqtt';

export class UnifiProtectMotionPlatform implements DynamicPlatformPlugin {

public readonly hap: HAP = this.api.hap;
public readonly Accessory: typeof PlatformAccessory = this.api.platformAccessory;

private mqtt: Mqtt;
private accessories: Array<PlatformAccessory> = [];

private uFlows: UnifiFlows;
Expand Down Expand Up @@ -103,17 +105,18 @@ export class UnifiProtectMotionPlatform implements DynamicPlatformPlugin {

// Set up the motion detection for all valid accessories
try {
this.mqtt = new Mqtt(this.config.mqtt_enabled ? this.config.mqtt : null , this.log);
this.accessories.forEach((accessory) => {
const cameraConfig: CameraConfig = accessory.context.cameraConfig;
// Update the camera object
cameraConfig.camera = cameras.find((cam: UnifiCamera) => cam.id === accessory.context.cameraConfig.camera.id);

UnifiCameraMotionSensor.setupMotionSensor(cameraConfig, accessory, this.config, this.hap, this.log);
UnifiCameraMotionSensor.setupMotionSensor(cameraConfig, accessory, this.config, this.mqtt, this.hap, this.log);
UnifiCameraDoorbell.setupDoorbell(cameraConfig, accessory, this.config, this.hap, this.log);
UnifiCameraStreaming.setupStreaming(cameraConfig, accessory, this.config, this.api, this.log);
});

const motionDetector: MotionDetector = new MotionDetector(this.api, this.config, this.uFlows, cameras, this.log);
const motionDetector: MotionDetector = new MotionDetector(this.api, this.config, this.mqtt, this.uFlows, cameras, this.log);
await motionDetector.setupMotionChecking(this.accessories);
this.log.info('Motion checking setup done!');
} catch (error) {
Expand Down Expand Up @@ -145,4 +148,4 @@ export class UnifiProtectMotionPlatform implements DynamicPlatformPlugin {
}
});
}
}
}
31 changes: 30 additions & 1 deletion src/utils/mqtt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,40 @@ export class Mqtt {
});
}

public sendMessageOnTopic(message: string, topic: string) {
public sendMessageOnTopic(message: string, topic: string): void {
if (this.client && this.client.connected) {
this.client.publish(this.config.topicPrefix + '/' + topic, message);
}
}

private onConnection(): Promise<void> {
return new Promise<void>((resolve, reject) => {
let interval = setInterval(() => {
if (this.client.connected) {
clearInterval(interval);
resolve();
}
}, 10);
});
}

public subscribeToTopic(topic: string, callback: (payload: {enabled: boolean}) => void): void {
this.onConnection().then(() => {
this.log.debug('Subscribing to: ' + this.config.topicPrefix + '/' + topic);
this.client.subscribe(this.config.topicPrefix + '/' + topic, (err, granted) => {
console.log(granted);
if (!err) {
if (granted && granted.length === 1) {
this.client.on('message', (messageTopic, messagePayload, packet) => {
if (messageTopic === this.config.topicPrefix + '/' + topic) {
callback(JSON.parse(messagePayload.toString()) as any);
}
});
}
}
});
});
}
}

export interface MqttConfig {
Expand Down

0 comments on commit 6198567

Please sign in to comment.