UNPKG

homebridge-keyble

Version:
223 lines (192 loc) 8.69 kB
import { Service, PlatformAccessory, CharacteristicValue, CharacteristicSetCallback, CharacteristicGetCallback } from 'homebridge'; import { KeyblePlatform } from './platform'; import { Key_Ble } from "keyble"; enum Position { Lock = 0, Unlock = 2, Open = 3, Unlock_MinStep = 1 } /** * 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 KeybleAccessory { private service: Service; private lock: Key_Ble; /** * These are just used to create a working example * You should implement your own code to track the state of your accessory */ private state = { currentPosition: Position.Lock, movement: this.platform.Characteristic.PositionState.STOPPED, targetPosition: Position.Lock }; constructor( private readonly platform: KeyblePlatform, private readonly accessory: PlatformAccessory, ) { this.lock = new Key_Ble({ address: accessory.context.config.address, user_id: accessory.context.config.user, user_key: accessory.context.config.key, auto_disconnect_time: 15, status_update_time: 600 }) // set accessory information const accessoryService = this.accessory.getService(this.platform.Service.AccessoryInformation); if (accessoryService) { accessoryService.setCharacteristic(this.platform.Characteristic.Manufacturer, 'eqiva') .setCharacteristic(this.platform.Characteristic.Model, 'eq3') .setCharacteristic(this.platform.Characteristic.SerialNumber, 'Default-Serial'); } // get the Door service if it exists, otherwise create a new Door service // you can create multiple services for each accessory this.service = this.accessory.getService(this.platform.Service.Door) || this.accessory.addService(this.platform.Service.Door); // 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.config.name); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/Door this.service.getCharacteristic(this.platform.Characteristic.CurrentPosition) .setProps({ minValue: Position.Lock, minStep: Position.Unlock_MinStep, maxValue: Position.Unlock }); this.service.getCharacteristic(this.platform.Characteristic.TargetPosition) .setProps({ minValue: Position.Lock, minStep: Position.Unlock_MinStep, maxValue: Position.Open }); // register handlers for the CurrentPosition Characteristic this.service.getCharacteristic(this.platform.Characteristic.CurrentPosition) .on('get', this.getCurrentPosition.bind(this)); // register handlers for the State Characteristic this.service.getCharacteristic(this.platform.Characteristic.PositionState) .on('get', this.getState.bind(this)); // register handlers for the TargetPosition Characteristic this.service.getCharacteristic(this.platform.Characteristic.TargetPosition) .on('get', this.getTargetPosition.bind(this)) .on('set', this.setTargetPosition.bind(this)); // register handler for status changes from lock this.lock .on('status_change', this.handleStatusChange.bind(this)); this.lock.request_status(); } /** * Handle the "GET" requests from HomeKit * These are sent when HomeKit wants to know the current state of the accessory, for example, checking if a Light bulb is on. * * GET requests should return as fast as possbile. A long delay here will result in * HomeKit being unresponsive and a bad user experience in general. * * If your device takes time to respond you should update the status of your device * asynchronously instead using the `updateCharacteristic` method instead. * @example * this.service.updateCharacteristic(this.platform.Characteristic.On, true) */ getCurrentPosition(callback: CharacteristicGetCallback): void { const currentPosition = this.state.currentPosition; this.platform.log.debug('Get Characteristic Current Position ->', currentPosition); callback(null, currentPosition); } getState(callback: CharacteristicGetCallback): void { const movement = this.state.movement; this.platform.log.debug('Get Characteristic State ->', movement); callback(null, movement); } getTargetPosition(callback: CharacteristicGetCallback): void { const targetPosition = this.state.targetPosition; this.platform.log.debug('Get Characteristic Target Position ->', targetPosition); callback(null, targetPosition); } /** * Handle "SET" requests from HomeKit * These are sent when the user changes the state of an accessory, for example, changing the Brightness */ setTargetPosition(value: CharacteristicValue, callback: CharacteristicSetCallback): void { if ( this.state.targetPosition != value ) { this.state.targetPosition = value as number; this.lock.ensure_connected(); this.platform.log.info('Set Characteristic Target Position -> ', value); switch(value) { case Position.Open: this.platform.log.debug('Open') this.state.movement = this.platform.Characteristic.PositionState.INCREASING; this.lock.open() .then(() => {this.updateCurrentPosition(value);}) .catch((error) => {this.handleLockError(error);}); break; case Position.Unlock: case Position.Unlock_MinStep: this.platform.log.debug('Unlock') this.state.movement = this.platform.Characteristic.PositionState.INCREASING; this.lock.unlock() .then(() => {this.updateCurrentPosition(value);}) .catch((error) => {this.handleLockError(error);}); break; case Position.Lock: this.platform.log.debug('Lock') this.state.movement = this.platform.Characteristic.PositionState.DECREASING; this.lock.lock() .then(() => {this.updateCurrentPosition(value);}) .catch((error) => {this.handleLockError(error);}); } } callback(null); } /* Meant to be called with then after operating the lock * This probably can be replaced by handleStatusChange */ updateCurrentPosition(position: Position): void { this.platform.log.debug('updateCurrentPosition -> ', position) this.service.getCharacteristic(this.platform.Characteristic.CurrentPosition) .setValue(Math.min(position, Position.Unlock)); this.service.getCharacteristic(this.platform.Characteristic.PositionState) .setValue(this.platform.Characteristic.PositionState.STOPPED); } handleLockError(error: string): void { this.lock.request_status(); this.platform.log.error(error); } handleStatusChange(newStatus : {lock_status_id: number, lock_status: string}): void { this.platform.log.debug('Status update: ', newStatus.lock_status); switch(newStatus.lock_status_id) { case 3: // LOCKED this.state.targetPosition = Position.Lock; this.state.currentPosition = Position.Lock; this.state.movement = this.platform.Characteristic.PositionState.STOPPED; break; case 2: // UNLOCKED case 4: // OPENED this.state.targetPosition = Position.Unlock; this.state.currentPosition = Position.Unlock; this.state.movement = this.platform.Characteristic.PositionState.STOPPED; break; case 1: // MOVING if (this.state.targetPosition > Position.Lock) { this.state.movement = this.platform.Characteristic.PositionState.INCREASING; } else { this.state.movement = this.platform.Characteristic.PositionState.DECREASING; } break; case 0: // UNKNOWN this.platform.log.info("Lock reports state unknown"); break; default: this.platform.log.error("Received unkown state: ", newStatus); break; } // Update homebridge values this.service.getCharacteristic(this.platform.Characteristic.CurrentPosition) .updateValue(this.state.currentPosition); this.service.getCharacteristic(this.platform.Characteristic.TargetPosition) .updateValue(this.state.targetPosition); this.service.getCharacteristic(this.platform.Characteristic.PositionState) .updateValue(this.state.movement); } }