From dd1eab903692a3ae455b1fd06e0a7b2bf86cab3f Mon Sep 17 00:00:00 2001 From: QuickSander Date: Sun, 21 Mar 2021 12:27:24 +0100 Subject: [PATCH] feat: Event based updates Updates will be done based on Miele event server updates instead of poll based. Unloads Homebridge by not polling every n seconds and increases responsiveness of the device. BREAKING CHANGE: poll timout timer removed from configuration. --- README.md | 6 +- config.schema.json | 14 +- src/mieleBasePlatformAccessory.ts | 103 +++++++------- src/mieleCharacteristics.ts | 174 ++++++++++++++--------- src/mieleFridgePlatformAccessory.ts | 22 +-- src/mieleWasherDryerPlatformAccessory.ts | 8 +- src/platform.ts | 2 +- src/settings.ts | 6 +- 8 files changed, 188 insertions(+), 147 deletions(-) diff --git a/README.md b/README.md index 35125a0..cd58155 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,17 @@ It (currently) requires a ## Features - Easy setup: guided process to retrieve token via OAuth2 from Miele. - Automatic token refreshing. +- Event based. - Start / stop (dish) washing machine program (with an option to disable to prevent unintentional program stop requests). - Remaining run time. - Washing machine / dish washer program target temperature. - HomeKit identify support via Homebridge log. ## Breaking changes +### Versions > 2.8.0 +- The introduction of event based updating removed the need for the _Poll interval_ setting. This option can be removed from +your config when you see a need to clean up your config. + ### Versions > 2.5.2 - _Disable temperature sensor_ and _disable stop action ability_ need to be re-configured as the settings have become finer grained (per specific device type instead of per group of device types). @@ -59,7 +64,6 @@ Fridge / Fridge Freezer combination: ## Planned features - Add support for oven, hob and coffee machine? - Add Custom characteristic to display current program running. -- Event based instead of poll based. ## Thanks - [MichelRabozee](https://github.com/MichelRabozee) diff --git a/config.schema.json b/config.schema.json index 096dbd9..661242d 100644 --- a/config.schema.json +++ b/config.schema.json @@ -35,13 +35,13 @@ ], "description": "Determines in what language the device name will be displayed in HomeKit." }, - "pollInterval": { - "title": "Poll interval [s]", + "reconnectEventServerInterval": { + "title": "Force event server reconnect interval [min]", "type": "number", "required": false, - "minimum": 0, + "minimum": 1, "default": 60, - "description": "The number of seconds between state retrievals or 0 to disable auto-update." + "description": "The interval at which a reconnect to the Miele event server is enforced." }, "disableStopActionFor": { "title": "Prevent stop/off request ability for", @@ -53,9 +53,7 @@ "Washer", "Dryer", "WasherDryer", - "Dishwasher", - "Fridge", - "FridgeFreezer" + "Dishwasher" ] } }, @@ -102,7 +100,6 @@ "expandable": true, "title": "General", "items": [ - "pollInterval", "language" ] }, @@ -112,6 +109,7 @@ "title": "Advanced", "description": "", "items": [ + "reconnectEventServerInterval", "disableStopActionFor", "disableTempSensorFor", "disableSetTargetTempFor" diff --git a/src/mieleBasePlatformAccessory.ts b/src/mieleBasePlatformAccessory.ts index 49b51c6..a44d60f 100644 --- a/src/mieleBasePlatformAccessory.ts +++ b/src/mieleBasePlatformAccessory.ts @@ -1,14 +1,13 @@ // Apacche License // Copyright (c) 2020, Sander van Woensel -import { PlatformAccessory, CharacteristicGetCallback, CharacteristicSetCallback, Service, CharacteristicValue } from 'homebridge'; +import { PlatformAccessory, CharacteristicSetCallback, Service, CharacteristicValue } from 'homebridge'; -import { DEVICES_INFO_URL, CACHE_RETIREMENT_TIME_MS } from './settings'; -import { MieleAtHomePlatform, createErrorString } from './platform'; +import { DEVICES_INFO_URL, EVENT_SERVER_RECONNECT_DELAY_S } from './settings'; +import { MieleAtHomePlatform } from './platform'; import { IMieleCharacteristic } from './mieleCharacteristics'; -import axios from 'axios'; - +import EventSource from 'eventsource'; export enum MieleState { Off = 1, @@ -38,13 +37,10 @@ export interface MieleStatusResponse { // Class Base Miele Accessory //------------------------------------------------------------------------------------------------- export abstract class MieleBasePlatformAccessory { - private stateUrl = DEVICES_INFO_URL + '/' + this.accessory.context.device.uniqueId + '/state'; - private lastCacheUpdateTime = 0; - private cacheUpdateQueued = false; - private cachedResponse: MieleStatusResponse | null = null; + private eventUrl = DEVICES_INFO_URL + '/' + this.accessory.context.device.uniqueId + '/events'; protected mainService!: Service; protected characteristics: IMieleCharacteristic[] = []; - + private eventSource: EventSource | null = null; //------------------------------------------------------------------------------------------------- constructor( @@ -61,30 +57,51 @@ export abstract class MieleBasePlatformAccessory { .getCharacteristic(this.platform.Characteristic.Identify) .on('set', this.identify.bind(this)); - // Start polling - if(this.platform.pollInterval > 0) { - setInterval(this.update.bind(this), this.platform.pollInterval*1000); - } - - } + this.connectToEventServer(); - //------------------------------------------------------------------------------------------------- - protected isCacheRetired(): boolean { - const retired = (this.lastCacheUpdateTime < Date.now() - CACHE_RETIREMENT_TIME_MS) && - !this.cacheUpdateQueued; + setInterval(this.connectToEventServer.bind(this), this.platform.reconnectEventServerInterval*60*1000); - if (retired) { - this.platform.log.debug('Cache retired. Status update enforced.'); - } - return retired; } //----------------------------------------------------------------------------------------------- - protected getGeneric(characteristic: IMieleCharacteristic, callback: CharacteristicGetCallback) { - if (this.isCacheRetired()) { - this.update(); + private connectToEventServer() { + // Close previous + if(this.eventSource) { + this.eventSource.close(); + this.eventSource = null; } - return characteristic.get(callback); + + const config = this.platform.getHttpRequestConfig(); + config.headers['Authorization'] = this.platform.token?.getAccessToken(); + config.headers['Accept'] = 'text/event-stream'; + + this.eventSource = new EventSource(this.eventUrl, config); + + this.eventSource.addEventListener('device', (event) => { + this.platform.log.debug(`${this.accessory.context.device.displayName}: Event '${event.type}' received.`); + const data = JSON.parse(event['data']); + this.update(data.state); + }); + + this.eventSource.onopen = (_message) => { + this.platform.log.info(`${this.accessory.context.device.displayName}: `+ + 'Connection with Miele event server succesfully (re-)established.'); + }; + + // TODO: improve. Maybe check what Miele raises manually first + this.eventSource.onerror = (error) => { + this.eventSource?.close(); + this.platform.log.error(`${this.accessory.context.device.displayName}: Error received from Miele event server: `+ + `'${JSON.stringify(error)}'`); + this.mainService.setCharacteristic(this.platform.Characteristic.StatusFault, this.platform.Characteristic.StatusFault.GENERAL_FAULT); + + this.platform.log.info(`${this.accessory.context.device.displayName}: Will attempt to reconnect to the Miele event server after`+ + ` ${EVENT_SERVER_RECONNECT_DELAY_S}[s].`); + setTimeout(()=> { + this.connectToEventServer(); + }, EVENT_SERVER_RECONNECT_DELAY_S*1000); + }; + } //----------------------------------------------------------------------------------------------- @@ -95,29 +112,13 @@ export abstract class MieleBasePlatformAccessory { //----------------------------------------------------------------------------------------------- // Update all characteristics - protected update(): void { - this.platform.log.debug(`Update called. Requesting: ${this.stateUrl}`); - this.cacheUpdateQueued = true; - - axios.get(this.stateUrl, this.platform.getHttpRequestConfig()).then( (response) => { - - if(JSON.stringify(response.data) !== JSON.stringify(this.cachedResponse)) { - - for(const characteristic of this.characteristics) { - characteristic.update(response.data); - } - this.cachedResponse = response.data; - } - - this.lastCacheUpdateTime = Date.now(); - this.cacheUpdateQueued = false; - - this.mainService.setCharacteristic(this.platform.Characteristic.StatusFault, this.platform.Characteristic.StatusFault.NO_FAULT); - - }).catch(response => { - this.platform.log.error( createErrorString(response) ); - this.mainService.setCharacteristic(this.platform.Characteristic.StatusFault, this.platform.Characteristic.StatusFault.GENERAL_FAULT); - }); + protected update(deviceData: MieleStatusResponse): void { + for(const characteristic of this.characteristics) { + characteristic.update(deviceData); + } + + this.mainService.setCharacteristic(this.platform.Characteristic.StatusFault, this.platform.Characteristic.StatusFault.NO_FAULT); + } } diff --git a/src/mieleCharacteristics.ts b/src/mieleCharacteristics.ts index e5f3b3f..706f2c9 100644 --- a/src/mieleCharacteristics.ts +++ b/src/mieleCharacteristics.ts @@ -28,27 +28,31 @@ export interface IMieleCharacteristic { } //------------------------------------------------------------------------------------------------- -// Miele Read Only Characteristic +// Miele Base Characteristic //------------------------------------------------------------------------------------------------- -abstract class MieleReadOnlyCharacteristic implements IMieleCharacteristic { +abstract class MieleBaseCharacteristic implements IMieleCharacteristic { + + protected readonly deviceName; constructor( protected platform: MieleAtHomePlatform, protected service: Service, + protected readonly characteristic, protected value, ) { + this.deviceName = this.service.getCharacteristic(this.platform.Characteristic.Name).value; } //------------------------------------------------------------------------------------------------- - // These methods always returns the status from cache wich might be outdated, but will be - // updated as soon as possible by the update function. + // These methods always returns the status from cache as the cache is updated from the event server. get(callback: CharacteristicGetCallback) { + this.platform.log.debug(`${this.deviceName}: Returning ${this.value} for ${this.characteristic.name}.`); callback(null, this.value); } //------------------------------------------------------------------------------------------------- set(_value: CharacteristicValue, _callback: CharacteristicSetCallback): void { - this.platform.log.error('Attempt to set a read only characteristic. Ignored.'); + throw new Error('"set method mut be overridden.'); } //------------------------------------------------------------------------------------------------- @@ -56,23 +60,50 @@ abstract class MieleReadOnlyCharacteristic implements IMieleCharacteristic { throw new Error('"update" method must be overridden.'); } + //------------------------------------------------------------------------------------------------- + // Update value only when not equal to cached value. + protected updateCharacteristic(value: string | number, logInfo = true) { + if(value!==this.value) { + const logStr = `${this.deviceName}: Updating characteristic ${this.characteristic.name} to ${value}.`; + if(logInfo) { + this.platform.log.info(logStr); + } else { + this.platform.log.debug(logStr); + } + this.value = value; + this.service.updateCharacteristic(this.characteristic, this.value); + } + } + + //------------------------------------------------------------------------------------------------- + // Undo state + protected undoSetState(value: CharacteristicValue) { + if(value !== this.value) { + this.platform.log.info(`${this.deviceName}: Reverting state to ${this.value}.`); + + setTimeout(()=> { + this.service.updateCharacteristic(this.characteristic, this.value); + }, REVERT_ACTIVATE_REQUEST_TIMEOUT_MS); + } + } + } //------------------------------------------------------------------------------------------------- // Base class: Miele Binary State Characteristic //------------------------------------------------------------------------------------------------- -abstract class MieleBinaryStateCharacteristic extends MieleReadOnlyCharacteristic { +abstract class MieleBinaryStateCharacteristic extends MieleBaseCharacteristic { constructor( platform: MieleAtHomePlatform, service: Service, private readonly inactiveStates: MieleState[] | null, private readonly activeStates: MieleState[] | null, - protected readonly characteristicType, + characteristicType, protected readonly offState: number, protected readonly onState: number, ) { - super(platform, service, offState); + super(platform, service, characteristicType, offState); } set(_value: CharacteristicValue, _callback: CharacteristicSetCallback): void { @@ -81,25 +112,21 @@ abstract class MieleBinaryStateCharacteristic extends MieleReadOnlyCharacteristi //------------------------------------------------------------------------------------------------- update(response: MieleStatusResponse): void { + let value = this.offState; if (this.inactiveStates) { - if(this.inactiveStates.includes(response.status.value_raw)) { - this.value = this.offState; - } else { - this.value = this.onState; + if(!this.inactiveStates.includes(response.status.value_raw)) { + value = this.onState; } } else if (this.activeStates) { if(this.activeStates.includes(response.status.value_raw)) { - this.value = this.onState; - } else { - this.value = this.offState; + value = this.onState; } } else { - throw new Error('Neither active or inactive states supplied. Cannot determine state.'); + throw new Error(`${this.deviceName}: Neither active or inactive states supplied. Cannot determine state.`); } - this.platform.log.debug(`Parsed ${this.characteristicType.name} from API response: ${this.value}`); - this.service.updateCharacteristic(this.characteristicType, this.value); + this.updateCharacteristic(value); } } @@ -168,12 +195,13 @@ export class MieleWritableBinaryStateCharacteristic extends MieleBinaryStateChar //------------------------------------------------------------------------------------------------- // Set async set(value: CharacteristicValue, callback: CharacteristicSetCallback) { - this.platform.log.debug(`Set characteristic ${this.characteristicType.name} to: ${value}`); + this.platform.log.debug(`${this.deviceName}: Set characteristic ${this.characteristic.name} to: ${value}`); callback(null); if(this.disableDeactivateAction && value===this.offState) { - this.platform.log.info(`${this.serialNumber}: Ignoring deactivate request. User disabled deactivation request for this device.`); + this.platform.log.info(`${this.deviceName} (${this.serialNumber}): Ignoring deactivate request. `+ + 'User disabled deactivation request for this device.'); this.undoSetState(value); return; } @@ -181,7 +209,7 @@ export class MieleWritableBinaryStateCharacteristic extends MieleBinaryStateChar try { // Retrieve allowed actions for this device in the current state. const response = await axios.get(this.platform.getActionsUrl(this.serialNumber), this.platform.getHttpRequestConfig()); - this.platform.log.debug(`${this.serialNumber}: Allowed process actions: ${response.data.processAction} `); + this.platform.log.debug(`${this.deviceName} (${this.serialNumber}): Allowed process actions: ${response.data.processAction}.`); let mieleProcesAction = this.mieleOffState; if(value===this.onState) { @@ -190,15 +218,16 @@ export class MieleWritableBinaryStateCharacteristic extends MieleBinaryStateChar // If allowed to execute action. if(response.data.processAction.includes(mieleProcesAction)) { - this.platform.log.info(`${this.serialNumber}: Process action "${MieleProcessAction[mieleProcesAction]}" (${mieleProcesAction}).`); + this.platform.log.info(`${this.deviceName} (${this.serialNumber}): Process action `+ + `"${MieleProcessAction[mieleProcesAction]}" (${mieleProcesAction}).`); const response = await axios.put(this.platform.getActionsUrl(this.serialNumber), {processAction: mieleProcesAction}, this.platform.getHttpRequestConfig()); - this.platform.log.debug(`Process action response code: ${response.status}: "${response.statusText}"`); + this.platform.log.debug(`${this.deviceName}: Process action response code: ${response.status}: "${response.statusText}"`); } else { // Requested action not allowed - this.platform.log.info(`${this.serialNumber}: Ignoring request to set device to HomeKit value ${value}. Miele action `+ - `"${MieleProcessAction[mieleProcesAction]}" (${mieleProcesAction}) not allowed in current device state. Allowed Miele process `+ - `actions: ${response.data.processAction ? '' : response.data.processAction}`); + this.platform.log.info(`${this.deviceName} (${this.serialNumber}): Ignoring request to set device to HomeKit value ${value}. `+ + `Miele action "${MieleProcessAction[mieleProcesAction]}" (${mieleProcesAction}) not allowed in current device state. `+ + `Allowed Miele process actions: ${response.data.processAction ? '' : response.data.processAction}`); // Undo state change to emulate a readonly state (since HomeKit valves are read/write) this.undoSetState(value); @@ -210,18 +239,6 @@ export class MieleWritableBinaryStateCharacteristic extends MieleBinaryStateChar } } - //------------------------------------------------------------------------------------------------- - // Undo state - private undoSetState(value: CharacteristicValue) { - if(value !== this.value) { - this.platform.log.info(`${this.serialNumber}: Reverting state to ${this.value}.`); - - setTimeout(()=> { - this.service.updateCharacteristic(this.characteristicType, this.value); - }, REVERT_ACTIVATE_REQUEST_TIMEOUT_MS); - } - } - } //------------------------------------------------------------------------------------------------- @@ -268,7 +285,7 @@ export class MieleTargetCoolingCharacteristic extends MieleWritableBinaryStateCh //------------------------------------------------------------------------------------------------- // Miele Remaining Duration Characteristic //------------------------------------------------------------------------------------------------- -export class MieleRemainingDurationCharacteristic extends MieleReadOnlyCharacteristic { +export class MieleRemainingDurationCharacteristic extends MieleBaseCharacteristic { //private readonly MAX_HOMEKIT_DURATION_S = 3600; @@ -276,13 +293,13 @@ export class MieleRemainingDurationCharacteristic extends MieleReadOnlyCharacter protected platform: MieleAtHomePlatform, protected service: Service, ) { - super(platform, service, 0); + super(platform, service, platform.Characteristic.RemainingDuration, 0); } //------------------------------------------------------------------------------------------------- update(response: MieleStatusResponse): void { - this.value = response.remainingTime[0]*3600 + response.remainingTime[1]*60; - this.platform.log.debug('Parsed Remaining Duration from API response:', this.value, '[s]'); + let value = response.remainingTime[0]*3600 + response.remainingTime[1]*60; + this.platform.log.debug(`${this.deviceName}: Remaing Duration update received: ${value}[s]`); // Clip to min and max value. const characteristic = this.service.getCharacteristic(this.platform.Characteristic.RemainingDuration); @@ -290,11 +307,11 @@ export class MieleRemainingDurationCharacteristic extends MieleReadOnlyCharacter const minValue = characteristic.props.minValue; if(maxValue && minValue) { - this.value = this.value > maxValue ? maxValue : this.value; - this.value = this.value < minValue ? minValue : this.value; + value = value > maxValue ? maxValue : value; + value = value < minValue ? minValue : value; } - this.service.updateCharacteristic(this.platform.Characteristic.RemainingDuration, this.value); + this.updateCharacteristic(value, false); } } @@ -307,7 +324,7 @@ export enum TemperatureType { Current } -export class MieleTempCharacteristic extends MieleReadOnlyCharacteristic { +export class MieleTempCharacteristic extends MieleBaseCharacteristic { private readonly NULL_VALUE = 2**16/-2; private readonly TEMP_CONVERSION_FACTOR = 100.0; protected readonly DEFAULT_ZONE = 1; // Currently only 1 temperature zone supported. @@ -316,39 +333,46 @@ export class MieleTempCharacteristic extends MieleReadOnlyCharacteristic { platform: MieleAtHomePlatform, service: Service, protected type: TemperatureType, - private offTemp: number, + characteristic, + private offTemp: number, ) { - super(platform, service, offTemp); + super(platform, service, characteristic, offTemp); } //------------------------------------------------------------------------------------------------- update(response: MieleStatusResponse): void { let tempArray: MieleStatusResponseTemp[]; - let characteristic; - this.value = this.offTemp; // Set target temperature to 'off' when no target temperature available since device is off. + let value = this.offTemp; // Set target temperature to 'off' when no target temperature available since device is off. switch(this.type) { case TemperatureType.Target: tempArray = response.targetTemperature; - characteristic = this.platform.Characteristic.TargetTemperature; break; case TemperatureType.Current: tempArray = response.temperature; - characteristic = this.platform.Characteristic.CurrentTemperature; - break; } if(tempArray.length > 0) { const valueRaw = tempArray[this.DEFAULT_ZONE-1].value_raw; // Fetch first temperature only. - this.platform.log.debug(`Parsed zone ${this.DEFAULT_ZONE} ${TemperatureType[this.type]} `+ - `Temperature from API response: ${valueRaw} [C/${this.TEMP_CONVERSION_FACTOR}]`); + this.platform.log.debug(`${this.deviceName}: Zone ${this.DEFAULT_ZONE} ${TemperatureType[this.type]} `+ + `Temperature update received: ${valueRaw}[C/${this.TEMP_CONVERSION_FACTOR}]`); if(valueRaw !== this.NULL_VALUE) { - this.value = valueRaw / this.TEMP_CONVERSION_FACTOR; // Miele returns values in centi-Celsius + value = valueRaw / this.TEMP_CONVERSION_FACTOR; // Miele returns values in centi-Celsius + } + + // Clip to min and max value. + const characteristicObj = this.service.getCharacteristic(this.characteristic); + const maxValue = characteristicObj.props.maxValue; + const minValue = characteristicObj.props.minValue; + + if(maxValue && minValue) { + value = value > maxValue ? maxValue : value; + value = value < minValue ? minValue : value; } - this.service.updateCharacteristic(characteristic, this.value); + this.updateCharacteristic(value); } } @@ -365,8 +389,9 @@ export class MieleTargetTempCharacteristic extends MieleTempCharacteristic { service: Service, private serialNumber: string, offTemp: number, + private disableSetTargetTemp: boolean, ) { - super(platform, service, TemperatureType.Target, offTemp); + super(platform, service, TemperatureType.Target, platform.Characteristic.TargetTemperature, offTemp); } //------------------------------------------------------------------------------------------------- @@ -374,7 +399,14 @@ export class MieleTargetTempCharacteristic extends MieleTempCharacteristic { async set(value: CharacteristicValue, callback: CharacteristicSetCallback) { callback(null); - this.platform.log.debug(`Set characteristic "${TemperatureType[this.type]} temperature" to: ${value}`); + if(this.disableSetTargetTemp) { + this.platform.log.info(`${this.deviceName} (${this.serialNumber}): Ignoring set ${TemperatureType[this.type]} temperature request. `+ + `User disabled modifying ${TemperatureType[this.type]} temperature for this device.`); + this.undoSetState(value); + return; + } + + this.platform.log.debug(`${this.deviceName}: Set characteristic "${TemperatureType[this.type]} temperature" to: ${value}`); try { const response = await axios.put(this.platform.getActionsUrl(this.serialNumber), @@ -382,13 +414,17 @@ export class MieleTargetTempCharacteristic extends MieleTempCharacteristic { [{zone:this.DEFAULT_ZONE, value: value}], // Setting is done in Celsius, retrieving returns centi-Celsius. }, this.platform.getHttpRequestConfig()); - this.platform.log.debug(`Set target temperature response code: ${response.status}: "${response.statusText}"`); + this.platform.log.debug(`${this.deviceName}: Set target temperature response code: ${response.status}: "${response.statusText}"`); + } catch(response) { if(response.response && response.response.status === 500) { - this.platform.log.warn(`Set target temperature: ignoring Miele API fault reply code ${response.response.status}. `+ + this.platform.log.warn(`${this.deviceName}: Set target temperature: ignoring Miele API fault reply code `+ + ` ${response.response.status}. `+ 'Device most probably still acknowlegded (known Miele API misbehaviour).'); } else { + this.undoSetState(value); + this.platform.log.error( createErrorString(response) ); // TODO: This characteristic needs to be added first. this.service.setCharacteristic(this.platform.Characteristic.StatusFault, this.platform.Characteristic.StatusFault.GENERAL_FAULT); @@ -402,24 +438,24 @@ export class MieleTargetTempCharacteristic extends MieleTempCharacteristic { //------------------------------------------------------------------------------------------------- // Miele Temperature Unit Characteristic //------------------------------------------------------------------------------------------------- -export class MieleTemperatureUnitCharacteristic extends MieleReadOnlyCharacteristic { +export class MieleTemperatureUnitCharacteristic extends MieleBaseCharacteristic { constructor( platform: MieleAtHomePlatform, service: Service, ) { - super(platform, service, platform.Characteristic.TemperatureDisplayUnits.CELSIUS); + super(platform, service, platform.Characteristic.TemperatureDisplayUnits, platform.Characteristic.TemperatureDisplayUnits.CELSIUS); } //------------------------------------------------------------------------------------------------- update(response: MieleStatusResponse): void { - if(response.temperature[0].unit === 'Celsius' ) { - this.value = this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS; - } else { - this.value = this.platform.Characteristic.TemperatureDisplayUnits.FAHRENHEIT; + let value = this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS; + + if(response.temperature[0].unit === 'Fahrenheit' ) { + value = this.platform.Characteristic.TemperatureDisplayUnits.FAHRENHEIT; } - this.platform.log.debug(`Parsed Temperature Unit from API response: ${response.temperature[0].unit} (HomeKit value: ${this.value})`); - this.service.updateCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits, this.value); + + this.updateCharacteristic(value); } } diff --git a/src/mieleFridgePlatformAccessory.ts b/src/mieleFridgePlatformAccessory.ts index a04188d..aba81bb 100644 --- a/src/mieleFridgePlatformAccessory.ts +++ b/src/mieleFridgePlatformAccessory.ts @@ -33,11 +33,12 @@ export class MieleFridgePlatformAccessory extends MieleBasePlatformAccessory { const currentCoolingStateCharacteristic = new MieleCurrentCoolingCharacteristic(this.platform, this.mainService, null, [MieleState.InUse]); const targetCoolingStateCharacterictic = new MieleTargetCoolingCharacteristic(this.platform, this.mainService, - null, [MieleState.InUse], accessory.context.device.uniqueId, disableChangeTargetTemperature); + null, [MieleState.InUse], accessory.context.device.uniqueId, disableStopAction); - const currentTemperatureCharacteristic = new MieleTempCharacteristic(this.platform, this.mainService, TemperatureType.Current, 0); + const currentTemperatureCharacteristic = new MieleTempCharacteristic(this.platform, this.mainService, + TemperatureType.Current, this.platform.Characteristic.CurrentTemperature, 0); const targetTemperatureCharacteristic = new MieleTargetTempCharacteristic(this.platform, - this.mainService, accessory.context.device.uniqueId, 1); + this.mainService, accessory.context.device.uniqueId, 1, disableChangeTargetTemperature); const temperatureUnitCharacteristic = new MieleTemperatureUnitCharacteristic(this.platform, this.mainService); this.characteristics.push(currentCoolingStateCharacteristic); @@ -49,11 +50,11 @@ export class MieleFridgePlatformAccessory extends MieleBasePlatformAccessory { // Each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/Thermostat this.mainService.getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState) - .on('get', this.getGeneric.bind(this, currentCoolingStateCharacteristic)); + .on('get', currentCoolingStateCharacteristic.get.bind(currentCoolingStateCharacteristic)); // Fridge can only cool. this.mainService.getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState) - .on('get', this.getGeneric.bind(this, targetCoolingStateCharacterictic)) + .on('get', targetCoolingStateCharacterictic.get.bind(targetCoolingStateCharacterictic)) .on('set', targetCoolingStateCharacterictic.set.bind(targetCoolingStateCharacterictic)) .setProps({ validValues: [ @@ -62,17 +63,18 @@ export class MieleFridgePlatformAccessory extends MieleBasePlatformAccessory { ]}); this.mainService.getCharacteristic(this.platform.Characteristic.CurrentTemperature) - .on('get', this.getGeneric.bind(this, currentTemperatureCharacteristic)); + .on('get', currentTemperatureCharacteristic.get.bind(currentTemperatureCharacteristic)); this.mainService.getCharacteristic(this.platform.Characteristic.TargetTemperature) - .on('get', this.getGeneric.bind(this, targetTemperatureCharacteristic)) - .on('set', targetTemperatureCharacteristic.set.bind(targetTemperatureCharacteristic)) .setProps({ minValue: 1, maxValue: 9, // TODO: set min/max based on API reply, hard coded for now. minStep: 1, - }); + }) + .on('get', targetTemperatureCharacteristic.get.bind(targetTemperatureCharacteristic)) + .on('set', targetTemperatureCharacteristic.set.bind(targetTemperatureCharacteristic)); + this.mainService.getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits) - .on('get', this.getGeneric.bind(this, temperatureUnitCharacteristic)); + .on('get', temperatureUnitCharacteristic.get.bind(temperatureUnitCharacteristic)); // TODO: base on what miele initial state returns and disallow any other setting } diff --git a/src/mieleWasherDryerPlatformAccessory.ts b/src/mieleWasherDryerPlatformAccessory.ts index 449c606..441e160 100644 --- a/src/mieleWasherDryerPlatformAccessory.ts +++ b/src/mieleWasherDryerPlatformAccessory.ts @@ -46,14 +46,14 @@ export class MieleWasherDryerPlatformAccessory extends MieleBasePlatformAccessor // Each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/Valve this.mainService.getCharacteristic(this.platform.Characteristic.Active) - .on('get', this.getGeneric.bind(this, activeCharacteristic)) + .on('get', activeCharacteristic.get.bind(activeCharacteristic)) .on('set', activeCharacteristic.set.bind(activeCharacteristic)); this.mainService.getCharacteristic(this.platform.Characteristic.InUse) - .on('get', this.getGeneric.bind(this, inUseCharacteristic)); + .on('get', inUseCharacteristic.get.bind(inUseCharacteristic)); this.mainService.getCharacteristic(this.platform.Characteristic.RemainingDuration) - .on('get', this.getGeneric.bind(this, remainingDurationCharacteristic)) + .on('get', remainingDurationCharacteristic.get.bind(remainingDurationCharacteristic)) .setProps({ minValue: 0, maxValue: this.MAX_REMAINING_DURATION, @@ -73,7 +73,7 @@ export class MieleWasherDryerPlatformAccessory extends MieleBasePlatformAccessor this.platform.Characteristic.CurrentTemperature, 0); this.characteristics.push(tempCharacteristic); this.tempService.getCharacteristic(this.platform.Characteristic.CurrentTemperature) - .on('get', this.getGeneric.bind(this, tempCharacteristic)) + .on('get', tempCharacteristic.get.bind(tempCharacteristic)) .setProps({ minValue: 0, maxValue: 110, diff --git a/src/platform.ts b/src/platform.ts index ffac536..f50c54f 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -49,7 +49,7 @@ export class MieleAtHomePlatform implements DynamicPlatformPlugin { public readonly accessories: PlatformAccessory[] = []; public token: Token | null = null; - public readonly pollInterval: number = parseInt(this.config.pollInterval); + public readonly reconnectEventServerInterval: number = parseInt(this.config.reconnectEventServerInterval); public readonly language = this.config.language || ''; public readonly disableStopActionFor: string[] = this.config.disableStopActionFor || []; public readonly disableTempSensorFor: string[] = this.config.disableTempSensorFor || []; diff --git a/src/settings.ts b/src/settings.ts index 95c68cf..83e229e 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -16,9 +16,6 @@ export const DEVICES_INFO_URL = BASE_URL+'/v1/devices'; // Token refresh URL. export const REFRESH_TOKEN_URL = BASE_URL + '/thirdparty/token'; -// Cache is considered invalid / I need of a refresh when it is older than this value in milliseconds. -export const CACHE_RETIREMENT_TIME_MS = 1000; - // Duration in milliseconds afterwhich to undo the status of the water tap activated/deactivate when not allowed // to turn on/off the washing machine according to the Miele API. export const REVERT_ACTIVATE_REQUEST_TIMEOUT_MS = 500; @@ -29,3 +26,6 @@ export const TOKEN_STORAGE_NAME = PLATFORM_NAME+'.Token.json'; // Interval at which the token is checked for a possible required refresh. // Miele tokens are valid for 30 days, checking once every 30min should be sufficient. export const TOKEN_REFRESH_CHECK_INTERVAL_S = 60*30; + +// Delay before attempting to reconnect. +export const EVENT_SERVER_RECONNECT_DELAY_S = 60;