diff --git a/.vscode/launch.json b/.vscode/launch.json index a42b64a..4f9c5ce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,17 +4,6 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "program": "${workspaceFolder}/bin/index.js", - "preLaunchTask": "build", - "console": "internalConsole", - "internalConsoleOptions": "openOnSessionStart", - "sourceMaps": true, - "outFiles": ["${workspaceFolder}/**/*.js"] - }, { "name": "Current TS File", "type": "node", @@ -24,6 +13,18 @@ "sourceMaps": true, "cwd": "${workspaceRoot}", "protocol": "inspector" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "preLaunchTask": "build", + "internalConsoleOptions": "openOnSessionStart", + "program": "/Users/brandonwatson/.nvm/versions/node/v14.15.0/lib/node_modules/homebridge/bin/homebridge", + "env": { + "HOMEBRIDGE_OPTS": "/Users/brandonwatson/.homebridge" + }, + "sourceMaps": true } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 08f2a53..ab4213d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,10 +4,15 @@ "version": "2.0.0", "tasks": [ { + "type": "npm", + "script": "build", "label": "build", - "type": "typescript", - "tsconfig": "tsconfig.json", - "problemMatcher": ["$tsc"] + "problemMatcher": [] + }, + { + "type": "shell", + "label": "build and install", + "command": "npm run build&&sudo npm install -g --unsafe-perm ." } ] } diff --git a/homebridge-face-location.code-workspace b/homebridge-face-location.code-workspace index 29c0eb7..ae7f19c 100644 --- a/homebridge-face-location.code-workspace +++ b/homebridge-face-location.code-workspace @@ -5,6 +5,7 @@ } ], "settings": { - "editor.tabSize": 2 + "editor.tabSize": 2, + "debug.javascript.unmapMissingSources": true } } \ No newline at end of file diff --git a/package.json b/package.json index daf8e12..64748c5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "homebridge-face-location", "version": "1.0.0", "description": "", - "main": "index.ts", + "main": "bin/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "npm run copy-files && tsc --build", @@ -15,8 +15,12 @@ }, "keywords": [ "homebridge", - "typescript" + "typescript", + "homebridge-plugin" ], + "engines": { + "homebridge": ">=1.1.6" + }, "author": "Brandon Watson", "license": "ISC", "dependencies": { @@ -25,16 +29,16 @@ "@types/mime-types": "^2.1.0", "@vladmandic/face-api": "^0.8.8", "canvas": "^2.6.1", - "copyfiles": "^2.4.0", "dotenv-extended": "^2.9.0", - "homebridge": "^1.1.6", "mime-types": "^2.1.27", "rtsp-stream": "file:../rtsp-stream", - "ts-node": "^9.0.0", - "tsyringe": "^4.4.0", - "typescript": "^4.0.5" + "tsyringe": "^4.4.0" }, "devDependencies": { - "@types/webgl2": "0.0.5" + "@types/webgl2": "0.0.5", + "typescript": "^4.0.5", + "ts-node": "^9.0.0", + "homebridge": "^1.1.6", + "copyfiles": "^2.4.0" } } diff --git a/src/config.ts b/src/config.ts index 5eb15c5..43559a1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -14,14 +14,18 @@ export interface IRoom { } export const isRoom = (object: any): object is IRoom => { - return "name" in object && "rtspCameraConnectionString" in object; + return "name" in object && "rtspConnectionStrings" in object; }; export const isConfig = (object: any): object is IConfig => { + const roomsOkay = + object["rooms"].filter((room: any) => isRoom(room)).length === + object["rooms"].length; return ( "refImageDirectory" in object && "trainedModelDirectory" in object && + "weightDirectory" in object && "rooms" in object && - isRoom(object["rooms"]) + roomsOkay ); }; diff --git a/src/monitor.ts b/src/locationMonitor.ts similarity index 100% rename from src/monitor.ts rename to src/locationMonitor.ts diff --git a/src/monitorAccessory.ts b/src/monitorAccessory.ts index 4fa5ad9..8a7ea6f 100644 --- a/src/monitorAccessory.ts +++ b/src/monitorAccessory.ts @@ -5,7 +5,7 @@ import { CharacteristicSetCallback, CharacteristicGetCallback, } from "homebridge"; - +import { LocationMonitor } from "./locationMonitor"; import { HomeLocationPlatform } from "./platform"; /** @@ -27,7 +27,8 @@ export class MonitorAccessory { constructor( private readonly platform: HomeLocationPlatform, - private readonly accessory: PlatformAccessory + private readonly accessory: PlatformAccessory, + private monitor: LocationMonitor ) { // set accessory information this.accessory @@ -42,17 +43,17 @@ export class MonitorAccessory { "Default-Serial" ); - // get the LightBulb service if it exists, otherwise create a new LightBulb service + // get the MotionSensor service if it exists, otherwise create a new MotionSensor service // you can create multiple services for each accessory this.service = - this.accessory.getService(this.platform.Service.Lightbulb) || - this.accessory.addService(this.platform.Service.Lightbulb); + this.accessory.getService(this.platform.Service.MotionSensor) || + this.accessory.addService(this.platform.Service.MotionSensor); // set the service name, this is what is displayed as the default name on the Home app // in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method. this.service.setCharacteristic( this.platform.Characteristic.Name, - accessory.context.device.exampleDisplayName + accessory.context["DeviceName"] ); // each service must implement at-minimum the "required characteristics" for the given service type diff --git a/src/platform.ts b/src/platform.ts index dd2ecaa..2291199 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -19,7 +19,8 @@ import { FaceMatcher, } from "@vladmandic/face-api"; import * as mime from "mime-types"; -import { getFaceDetectorOptions } from "../src/common"; +import { LocationMonitor } from "./locationMonitor"; +import { getFaceDetectorOptions } from "./common"; require("@tensorflow/tfjs-node"); const { Canvas, Image, ImageData } = canvas; @@ -99,16 +100,7 @@ export class HomeLocationPlatform implements DynamicPlatformPlugin { faceMatcher = FaceMatcher.fromJSON(JSON.parse(raw)); } - const exampleDevices = [ - { - exampleUniqueId: "ABCD", - exampleDisplayName: "Bedroom", - }, - { - exampleUniqueId: "EFGH", - exampleDisplayName: "Kitchen", - }, - ]; + const locationMonitor = new LocationMonitor(this.config.rooms, faceMatcher); const labels = faceMatcher.labeledDescriptors.map((e) => e.label); for (const room of this.config.rooms) { @@ -122,28 +114,23 @@ export class HomeLocationPlatform implements DynamicPlatformPlugin { existingAccessory.displayName ); - // this is imported from `platformAccessory.ts` - new MonitorAccessory(this, existingAccessory); + new MonitorAccessory(this, existingAccessory, locationMonitor); - // update accessory cache with any changes to the accessory details and information this.api.updatePlatformAccessories([existingAccessory]); } else { - // the accessory does not yet exist, so we need to create it - this.log.info("Adding new accessory:", `${room}+${label}`); + this.log.info("Adding new accessory:", `${room.name}+${label}`); // create a new accessory const accessory = new this.api.platformAccessory( - `${room}+${label}`, + `${room.name} ${label}`, uuid ); - // store a copy of the device object in the `accessory.context` - // the `context` property can be used to store any data about the accessory you may need - // accessory.context.device = device; + accessory.context["DeviceName"] = `${room.name} ${label}`; // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` - new MonitorAccessory(this, accessory); + new MonitorAccessory(this, accessory, locationMonitor); // link the accessory to your platform this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ @@ -152,73 +139,6 @@ export class HomeLocationPlatform implements DynamicPlatformPlugin { } } } - - // loop over the discovered devices and register each one if it has not already been registered - for (const device of exampleDevices) { - // generate a unique id for the accessory this should be generated from - // something globally unique, but constant, for example, the device serial - // number or MAC address - const uuid = this.api.hap.uuid.generate(device.exampleUniqueId); - - // see if an accessory with the same uuid has already been registered and restored from - // the cached devices we stored in the `configureAccessory` method above - const existingAccessory = this.accessories.find( - (accessory) => accessory.UUID === uuid - ); - - if (existingAccessory) { - // the accessory already exists - if (device) { - this.log.info( - "Restoring existing accessory from cache:", - existingAccessory.displayName - ); - - // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: - // existingAccessory.context.device = device; - // this.api.updatePlatformAccessories([existingAccessory]); - - // create the accessory handler for the restored accessory - // this is imported from `platformAccessory.ts` - new MonitorAccessory(this, existingAccessory); - - // update accessory cache with any changes to the accessory details and information - this.api.updatePlatformAccessories([existingAccessory]); - } else if (!device) { - // it is possible to remove platform accessories at any time using `api.unregisterPlatformAccessories`, eg.: - // remove platform accessories when no longer present - this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ - existingAccessory, - ]); - this.log.info( - "Removing existing accessory from cache:", - existingAccessory.displayName - ); - } - } else { - // the accessory does not yet exist, so we need to create it - this.log.info("Adding new accessory:", device.exampleDisplayName); - - // create a new accessory - const accessory = new this.api.platformAccessory( - device.exampleDisplayName, - uuid - ); - - // store a copy of the device object in the `accessory.context` - // the `context` property can be used to store any data about the accessory you may need - accessory.context.device = device; - - // create the accessory handler for the newly create accessory - // this is imported from `platformAccessory.ts` - new MonitorAccessory(this, accessory); - - // link the accessory to your platform - this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ - accessory, - ]); - } - } } private async trainModels(): Promise { diff --git a/src/settings.ts b/src/settings.ts index 8f37150..334161e 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -6,4 +6,4 @@ export const PLATFORM_NAME = "HomeLocation"; /** * This must match the name of your plugin as defined the package.json */ -export const PLUGIN_NAME = "homebridge-home-location"; +export const PLUGIN_NAME = "homebridge-face-location";