Skip to content

Commit

Permalink
Added motion sensor
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleBoyer committed Jul 13, 2024
1 parent 9008c54 commit 145b471
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 7 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"private": false,
"displayName": "Konnected BlaQ",
"name": "homebridge-blaq",
"version": "0.2.1",
"version": "0.2.2",
"description": "Control and view your garage door(s) remotely with real-time updates using Konnected's BlaQ hardware",
"license": "Apache-2.0",
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions src/accessory/garage-light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class BlaQGarageLightAccessory implements BaseBlaQAccessory {
}: BlaQGarageLightAccessoryConstructorParams) {
this.platform = platform;
this.logger = this.platform.logger;
this.logger.debug('Initializing BlaQGarageDoorAccessory...');
this.logger.debug('Initializing BlaQGarageLightAccessory...');
this.accessory = accessory;
this.model = model;
this.serialNumber = serialNumber;
Expand Down Expand Up @@ -88,7 +88,7 @@ export class BlaQGarageLightAccessory implements BaseBlaQAccessory {
this.accessoryInformationService
.getCharacteristic(this.platform.characteristic.FirmwareRevision)
.onGet(this.getFirmwareVersion.bind(this));
this.logger.debug('Initialized BlaQGarageDoorAccessory!');
this.logger.debug('Initialized BlaQGarageLightAccessory!');
}

getFirmwareVersion(): CharacteristicValue {
Expand Down
4 changes: 2 additions & 2 deletions src/accessory/garage-lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class BlaQGarageLockAccessory implements BaseBlaQAccessory {
}: BlaQGarageLockAccessoryConstructorParams) {
this.platform = platform;
this.logger = this.platform.logger;
this.logger.debug('Initializing BlaQGarageDoorAccessory...');
this.logger.debug('Initializing BlaQGarageLockAccessory...');
this.accessory = accessory;
this.model = model;
this.serialNumber = serialNumber;
Expand Down Expand Up @@ -90,7 +90,7 @@ export class BlaQGarageLockAccessory implements BaseBlaQAccessory {
this.accessoryInformationService
.getCharacteristic(this.platform.characteristic.FirmwareRevision)
.onGet(this.getFirmwareVersion.bind(this));
this.logger.debug('Initialized BlaQGarageDoorAccessory!');
this.logger.debug('Initialized BlaQGarageLockAccessory!');
}

getFirmwareVersion(): CharacteristicValue {
Expand Down
155 changes: 155 additions & 0 deletions src/accessory/garage-motion-sensor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { CharacteristicValue, Logger, PlatformAccessory, Service } from 'homebridge';

import { BlaQHomebridgePluginPlatform } from '../platform.js';
import {
BlaQBinarySensorEvent,
BlaQTextSensorEvent,
} from '../types.js';
import { LogMessageEvent, StateUpdateMessageEvent, StateUpdateRecord } from '../utils/eventsource.js';
import { BaseBlaQAccessory } from './base.js';

const correctAPIBaseURL = (inputURL: string) => {
let correctedAPIBaseURL = inputURL;
if(!correctedAPIBaseURL.includes('://')){
correctedAPIBaseURL = `http://${correctedAPIBaseURL}`;
}
if(correctedAPIBaseURL.endsWith('/')){
correctedAPIBaseURL = correctedAPIBaseURL.slice(0, -1);
}
return correctedAPIBaseURL;
};

type BlaQGarageMotionSensorAccessoryConstructorParams = {
platform: BlaQHomebridgePluginPlatform;
accessory: PlatformAccessory;
model: string;
serialNumber: string;
apiBaseURL: string;
};

/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
export class BlaQGarageMotionSensorAccessory implements BaseBlaQAccessory {
private logger: Logger;
private accessoryInformationService: Service;
private motionSensorService: Service;
private apiBaseURL: string;
private firmwareVersion?: string;
private motionDetected?: boolean;
private readonly platform: BlaQHomebridgePluginPlatform;
private readonly accessory: PlatformAccessory;
private readonly model: string;
private readonly serialNumber: string;

constructor({
platform,
accessory,
model,
serialNumber,
apiBaseURL,
}: BlaQGarageMotionSensorAccessoryConstructorParams) {
this.platform = platform;
this.logger = this.platform.logger;
this.logger.debug('Initializing BlaQGarageLightAccessory...');
this.accessory = accessory;
this.model = model;
this.serialNumber = serialNumber;
this.apiBaseURL = correctAPIBaseURL(apiBaseURL);
this.motionSensorService = this.accessory.getService(this.platform.service.MotionSensor)
|| this.accessory.addService(this.platform.service.MotionSensor);

this.accessoryInformationService = this.accessory.getService(this.platform.service.AccessoryInformation)
|| this.accessory.addService(this.platform.service.AccessoryInformation);

// set accessory information
this.accessoryInformationService
.setCharacteristic(this.platform.characteristic.Manufacturer, 'Konnected')
.setCharacteristic(this.platform.characteristic.Model, this.model)
.setCharacteristic(this.platform.characteristic.SerialNumber, this.serialNumber);

// Set the service name. This is what is displayed as the name on the Home
// app. We use what we stored in `accessory.context` in `discoverDevices`.
this.motionSensorService.setCharacteristic(this.platform.characteristic.Name, accessory.context.device.displayName);

this.motionSensorService.getCharacteristic(this.platform.characteristic.MotionDetected)
.onGet(this.getMotionDetected.bind(this));

// Publish firmware version; this may not be initialized yet, so we set a getter.
// Note that this is against the AccessoryInformation service, not the GDO service.
this.accessoryInformationService
.getCharacteristic(this.platform.characteristic.FirmwareRevision)
.onGet(this.getFirmwareVersion.bind(this));
this.logger.debug('Initialized BlaQGarageMotionSensorAccessory!');
}

getFirmwareVersion(): CharacteristicValue {
return this.firmwareVersion || '';
}

private setFirmwareVersion(version: string) {
this.firmwareVersion = version;
this.accessoryInformationService.setCharacteristic(
this.platform.characteristic.FirmwareRevision,
version,
);
}

getMotionDetected(): CharacteristicValue {
return this.motionDetected || false;
}

setMotionDetected(motionDetected: boolean) {
this.motionDetected = motionDetected;
this.motionSensorService.setCharacteristic(
this.platform.characteristic.MotionDetected,
this.motionDetected,
);
}

setAPIBaseURL(url: string){
this.apiBaseURL = correctAPIBaseURL(url);
}

handleStateEvent(stateEvent: StateUpdateMessageEvent){
this.logger.debug('Processing state event:', stateEvent.data);
try {
const stateInfo = JSON.parse(stateEvent.data) as StateUpdateRecord;
if (['binary_sensor-motion'].includes(stateInfo.id)) {
const sensorEvent = stateInfo as BlaQBinarySensorEvent;
if(['OFF', 'ON'].includes(sensorEvent.state.toUpperCase())){
this.setMotionDetected(sensorEvent.state.toUpperCase() === 'ON');
}
} else if (['text_sensor-esphome_version', 'text_sensor-firmware_version'].includes(stateInfo.id)) {
const b = stateInfo as BlaQTextSensorEvent;
if (b.value === b.state && b.value !== '' && b.value !== null && b.value !== undefined) {
this.logger.info('Firmware version:', b.value);
this.setFirmwareVersion(b.value);
} else {
this.logger.error('Mismatched firmware versions in value/state:', b.value, b.state);
this.firmwareVersion = undefined;
}
}
} catch(e) {
this.logger.error('Cannot deserialize message:', stateEvent);
this.logger.error('Deserialization yielded:', e);
}
}

handleLogEvent(logEvent: LogMessageEvent){
this.logger.debug('BlaQ log:', logEvent.data);
try {
const logStr = logEvent.data;
const lowercaseLogStr = logStr.toLowerCase();
if (lowercaseLogStr.includes('motion') && lowercaseLogStr.includes('state') && lowercaseLogStr.includes('on')) {
this.setMotionDetected(true);
} else if (lowercaseLogStr.includes('motion') && lowercaseLogStr.includes('state') && lowercaseLogStr.includes('off')) {
this.setMotionDetected(false);
}
} catch(e) {
this.logger.error('Log parsing error:', e);
}
}
}

0 comments on commit 145b471

Please sign in to comment.