import { Service , PlatformAccessory , CharacteristicValue } from 'homebridge' ; import { HoneywellHomePlatform } from '../platform' ; import { interval , Subject } from 'rxjs' ; import { skipWhile } from 'rxjs/operators' ; import { DeviceURL , location , device , devicesConfig } from '../settings' ; /**
 * 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 LeakSensor { private service : Service ; temperatureService ? : Service ; humidityService ? : Service ; leakService ? : Service ; StatusActive ! : CharacteristicValue ; LeakDetected ! : CharacteristicValue ; BatteryLevel ! : CharacteristicValue ; ChargingState ! : CharacteristicValue ; StatusLowBattery ! : CharacteristicValue ; CurrentTemperature ! : CharacteristicValue ; CurrentRelativeHumidity ! : CharacteristicValue ; action ! : string ; deviceLogging ! : string ; deviceRefreshRate ! : number ; SensorUpdateInProgress ! : boolean ; doSensorUpdate ! : Subject < void > ; constructor ( private readonly platform , private accessory , public readonly locationId , public device ) { this . logs ( device ) ; this . refreshRate ( device ) ; this . config ( device ) ; this . doSensorUpdate = new Subject ( ) ; this . SensorUpdateInProgress = false ; accessory . getService ( this . platform . Service . AccessoryInformation ) ! . setCharacteristic ( this . platform . Characteristic . Manufacturer , 'Honeywell' ) . setCharacteristic ( this . platform . Characteristic . Model , device . deviceType ) . setCharacteristic ( this . platform . Characteristic . SerialNumber , device . deviceID ) . setCharacteristic ( this . platform . Characteristic . FirmwareRevision , accessory . context . firmwareRevision ) . getCharacteristic ( this . platform . Characteristic . FirmwareRevision ) . updateValue ( accessory . context . firmwareRevision ) ; ( this . service = this . accessory . getService ( this . platform . Service . Battery ) || this . accessory . addService ( this . platform . Service . Battery ) ) , `${ accessory . displayName } Battery` ; this . service . setCharacteristic ( this . platform . Characteristic . Name , accessory . displayName ) ; this . parseStatus ( ) ; this . service . setCharacteristic ( this . platform . Characteristic . ChargingState , 2 ) ; if ( this . device . leaksensor ?. hide_leak ) { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Removing Leak Sensor Service` ) ; this . leakService = this . accessory . getService ( this . platform . Service . LeakSensor ) ; accessory . removeService ( this . leakService ! ) ; } else if ( ! this . leakService ) { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Add Leak Sensor Service` ) ; ( this . leakService = this . accessory . getService ( this . platform . Service . LeakSensor ) || this . accessory . addService ( this . platform . Service . LeakSensor ) ) , `${ accessory . displayName } Leak Sensor` ; this . leakService . setCharacteristic ( this . platform . Characteristic . Name , `${ accessory . displayName } Leak Sensor` ) ; } else { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Leak Sensor Service Not Added` ) ; } if ( this . device . leaksensor ?. hide_temperature ) { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Removing Temperature Sensor Service` ) ; this . temperatureService = this . accessory . getService ( this . platform . Service . TemperatureSensor ) ; accessory . removeService ( this . temperatureService ! ) ; } else if ( ! this . temperatureService ) { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Add Temperature Sensor Service` ) ; ( this . temperatureService = this . accessory . getService ( this . platform . Service . TemperatureSensor ) || this . accessory . addService ( this . platform . Service . TemperatureSensor ) ) , `${ accessory . displayName } Temperature Sensor` ; this . temperatureService . setCharacteristic ( this . platform . Characteristic . Name , `${ accessory . displayName } Temperature Sensor` ) ; this . temperatureService . getCharacteristic ( this . platform . Characteristic . CurrentTemperature ) . setProps ( { minValue : - 273.15 , maxValue : 100 , minStep : 0.1 , } ) . onGet ( async ( ) => { return this . CurrentTemperature ; } ) ; } else { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Temperature Sensor Service Not Added` ) ; } if ( this . device . leaksensor ?. hide_humidity ) { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Removing Humidity Sensor Service` ) ; this . humidityService = this . accessory . getService ( this . platform . Service . HumiditySensor ) ; accessory . removeService ( this . humidityService ! ) ; } else if ( ! this . humidityService ) { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Add Humidity Sensor Service` ) ; ( this . humidityService = this . accessory . getService ( this . platform . Service . HumiditySensor ) || this . accessory . addService ( this . platform . Service . HumiditySensor ) ) , `${ accessory . displayName } Humidity Sensor` ; this . humidityService . setCharacteristic ( this . platform . Characteristic . Name , `${ accessory . displayName } Humidity Sensor` ) ; this . humidityService . getCharacteristic ( this . platform . Characteristic . CurrentRelativeHumidity ) . setProps ( { minStep : 0.1 , } ) . onGet ( async ( ) => { return this . CurrentRelativeHumidity ; } ) ; } else { this . debugLog ( `Leak Sensor: ${ accessory . displayName } Humidity Sensor Service Not Added` ) ; } this . updateHomeKitCharacteristics ( ) ; interval ( this . platform . config . options ! . refreshRate ! * 1000 ) . pipe ( skipWhile ( ( ) => this . SensorUpdateInProgress ) ) . subscribe ( ( ) => { this . refreshStatus ( ) ; } ) ; } /**
     * Parse the device status from the honeywell api
     */ parseStatus ( ) { this . StatusActive = this . device . hasDeviceCheckedIn ; if ( this . device . waterPresent === true ) { this . LeakDetected = 1 ; } else { this . LeakDetected = 0 ; } this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } LeakDetected: ${ this . LeakDetected }` ) ; if ( ! this . device . leaksensor ?. hide_temperature ) { this . CurrentTemperature = this . device . currentSensorReadings . temperature ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } CurrentTemperature: ${ this . CurrentTemperature }°` ) ; } if ( ! this . device . leaksensor ?. hide_humidity ) { this . CurrentRelativeHumidity = this . device . currentSensorReadings . humidity ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } CurrentRelativeHumidity: ${ this . CurrentRelativeHumidity }%` ) ; } this . BatteryLevel = Number ( this . device . batteryRemaining ) ; this . service . getCharacteristic ( this . platform . Characteristic . BatteryLevel ) . updateValue ( this . BatteryLevel ) ; if ( this . device . batteryRemaining < 15 ) { this . StatusLowBattery = this . platform . Characteristic . StatusLowBattery . BATTERY_LEVEL_LOW ; } else { this . StatusLowBattery = this . platform . Characteristic . StatusLowBattery . BATTERY_LEVEL_NORMAL ; } this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } BatteryLevel: ${ this . BatteryLevel },` + ` StatusLowBattery: ${ this . StatusLowBattery }` ) ; } /**
     * Asks the Honeywell Home API for the latest device information
     */ async refreshStatus ( ) { try { const device = ( await this . platform . axios . get ( `${ DeviceURL }/waterLeakDetectors/${ this . device . deviceID }` , { params : { locationId : this . locationId , } , } ) ) . data ; this . device = device ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } device: ${ JSON . stringify ( this . device ) }` ) ; this . parseStatus ( ) ; this . updateHomeKitCharacteristics ( ) ; } catch ( e ) { this . action = 'refreshStatus' ; this . honeywellAPIError ( e ) ; this . apiError ( e ) ; } } /**
     * Updates the status for each of the HomeKit Characteristics
     */ updateHomeKitCharacteristics ( ) { if ( this . BatteryLevel === undefined ) { this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } BatteryLevel: ${ this . BatteryLevel }` ) ; } else { this . service . updateCharacteristic ( this . platform . Characteristic . BatteryLevel , this . BatteryLevel ) ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } updateCharacteristic BatteryLevel: ${ this . BatteryLevel }` ) ; } if ( this . StatusLowBattery === undefined ) { this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } StatusLowBattery: ${ this . StatusLowBattery }` ) ; } else { this . service . updateCharacteristic ( this . platform . Characteristic . StatusLowBattery , this . StatusLowBattery ) ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } updateCharacteristic StatusLowBattery: ${ this . StatusLowBattery }` ) ; } if ( ! this . device . leaksensor ?. hide_leak ) { if ( this . LeakDetected === undefined ) { this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } LeakDetected: ${ this . LeakDetected }` ) ; } else { this . leakService ?. updateCharacteristic ( this . platform . Characteristic . LeakDetected , this . LeakDetected ) ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } updateCharacteristic LeakDetected: ${ this . LeakDetected }` ) ; } if ( this . StatusActive === undefined ) { this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } StatusActive: ${ this . StatusActive }` ) ; } else { this . leakService ?. updateCharacteristic ( this . platform . Characteristic . StatusActive , this . StatusActive ) ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } updateCharacteristic StatusActive: ${ this . StatusActive }` ) ; } } if ( this . device . leaksensor ?. hide_temperature && this . CurrentTemperature === undefined ) { this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } CurrentTemperature: ${ this . CurrentTemperature }` ) ; } else { this . temperatureService ?. updateCharacteristic ( this . platform . Characteristic . CurrentTemperature , this . CurrentTemperature ) ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } updateCharacteristic CurrentTemperature: ${ this . CurrentTemperature }` ) ; } if ( this . device . leaksensor ?. hide_humidity && this . CurrentRelativeHumidity === undefined ) { this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } CurrentRelativeHumidity: ${ this . CurrentRelativeHumidity }` ) ; } else { this . humidityService ?. updateCharacteristic ( this . platform . Characteristic . CurrentRelativeHumidity , this . CurrentRelativeHumidity ) ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName }` + ` updateCharacteristic CurrentRelativeHumidity: ${ this . CurrentRelativeHumidity }` ) ; } } public apiError ( e ) { this . service . updateCharacteristic ( this . platform . Characteristic . BatteryLevel , e ) ; this . service . updateCharacteristic ( this . platform . Characteristic . StatusLowBattery , e ) ; if ( ! this . device . leaksensor ?. hide_leak ) { this . leakService ?. updateCharacteristic ( this . platform . Characteristic . LeakDetected , e ) ; this . leakService ?. updateCharacteristic ( this . platform . Characteristic . StatusActive , e ) ; } } public honeywellAPIError ( e ) { if ( e . message . includes ( '400' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Bad Request` ) ; this . debugLog ( 'The client has issued an invalid request. This is commonly used to specify validation errors in a request payload.' ) ; } else if ( e . message . includes ( '401' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Unauthorized Request` ) ; this . debugLog ( 'Authorization for the API is required, but the request has not been authenticated.' ) ; } else if ( e . message . includes ( '403' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Forbidden Request` ) ; this . debugLog ( 'The request has been authenticated but does not have appropriate permissions, or a requested resource is not found.' ) ; } else if ( e . message . includes ( '404' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Requst Not Found` ) ; this . debugLog ( 'Specifies the requested path does not exist.' ) ; } else if ( e . message . includes ( '406' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Request Not Acceptable` ) ; this . debugLog ( 'The client has requested a MIME type via the Accept header for a value not supported by the server.' ) ; } else if ( e . message . includes ( '415' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Unsupported Requst Header` ) ; this . debugLog ( 'The client has defined a contentType header that is not supported by the server.' ) ; } else if ( e . message . includes ( '422' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Unprocessable Entity` ) ; this . debugLog ( 'The client has made a valid request, but the server cannot process it.' + ' This is often used for APIs for which certain limits have been exceeded.' ) ; } else if ( e . message . includes ( '429' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Too Many Requests` ) ; this . debugLog ( 'The client has exceeded the number of requests allowed for a given time window.' ) ; } else if ( e . message . includes ( '500' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action }, Internal Server Error` ) ; this . debugLog ( 'An unexpected error on the SmartThings servers has occurred. These errors should be rare.' ) ; } else { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to ${ this . action },` ) ; } if ( this . deviceLogging . includes ( 'debug' ) ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } failed to pushChanges, Error Message: ${ JSON . stringify ( e . message ) }` ) ; } if ( this . deviceLogging . includes ( 'debug' ) || this . platform . debugMode ) { this . platform . log . error ( `Leak Sensor: ${ this . accessory . displayName } Error: ${ JSON . stringify ( e ) }` ) ; } } config ( device ) { let config = { } ; if ( device . leaksensor ) { config = device . leaksensor ; } if ( device . logging !== undefined ) { config [ 'logging' ] = device . logging ; } if ( device . refreshRate !== undefined ) { config [ 'refreshRate' ] = device . refreshRate ; } if ( Object . entries ( config ) . length !== 0 ) { this . warnLog ( `Leak Sensor: ${ this . accessory . displayName } Config: ${ JSON . stringify ( config ) }` ) ; } } refreshRate ( device ) { if ( device . refreshRate ) { this . deviceRefreshRate = this . accessory . context . refreshRate = device . refreshRate ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } Using Device Config refreshRate: ${ this . deviceRefreshRate }` ) ; } else if ( this . platform . config . options ! . refreshRate ) { this . deviceRefreshRate = this . accessory . context . refreshRate = this . platform . config . options ! . refreshRate ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } Using Platform Config refreshRate: ${ this . deviceRefreshRate }` ) ; } } logs ( device ) { if ( this . platform . debugMode ) { this . deviceLogging = this . accessory . context . logging = 'debugMode' ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } Using Debug Mode Logging: ${ this . deviceLogging }` ) ; } else if ( device . logging ) { this . deviceLogging = this . accessory . context . logging = device . logging ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } Using Device Config Logging: ${ this . deviceLogging }` ) ; } else if ( this . platform . config . options ?. logging ) { this . deviceLogging = this . accessory . context . logging = this . platform . config . options ?. logging ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } Using Platform Config Logging: ${ this . deviceLogging }` ) ; } else { this . deviceLogging = this . accessory . context . logging = 'standard' ; this . debugLog ( `Leak Sensor: ${ this . accessory . displayName } Logging Not Set, Using: ${ this . deviceLogging }` ) ; } } /**
   * Logging for Device
   */ infoLog ( ... log ) { if ( this . enablingDeviceLogging ( ) ) { this . platform . log . info ( String ( ... log ) ) ; } } warnLog ( ... log ) { if ( this . enablingDeviceLogging ( ) ) { this . platform . log . warn ( String ( ... log ) ) ; } } errorLog ( ... log ) { if ( this . enablingDeviceLogging ( ) ) { this . platform . log . error ( String ( ... log ) ) ; } } debugLog ( ... log ) { if ( this . enablingDeviceLogging ( ) ) { if ( this . deviceLogging === 'debug' ) { this . platform . log . info ( '[DEBUG]' , String ( ... log ) ) ; } else { this . platform . log . debug ( String ( ... log ) ) ; } } } enablingDeviceLogging ( ) { return this . deviceLogging . includes ( 'debug' ) || this . deviceLogging === 'standard' ; } }