From 8b80f39420c7821df9cac1eccac8c7e20654a52c Mon Sep 17 00:00:00 2001 From: Takeshi Kishi Date: Mon, 27 Dec 2021 20:35:31 +0900 Subject: [PATCH] refactor error handling and logger (#19) --- src/airConAccessory.ts | 126 ++++++++++++++--------------------------- src/lightAccessory.ts | 37 ++++-------- src/natureRemoApi.ts | 84 +++++++++++++-------------- src/platform.ts | 13 ++--- src/sensorAccessory.ts | 100 +++++++++++++------------------- src/tvAccessory.ts | 63 +++++++-------------- src/types.ts | 28 --------- 7 files changed, 160 insertions(+), 291 deletions(-) diff --git a/src/airConAccessory.ts b/src/airConAccessory.ts index bf1369a..8749943 100644 --- a/src/airConAccessory.ts +++ b/src/airConAccessory.ts @@ -16,13 +16,17 @@ export class NatureNemoAirConAccessory { private readonly platform: NatureRemoPlatform, private readonly accessory: PlatformAccessory, ) { + this.name = this.accessory.context.appliance.nickname; + this.id = this.accessory.context.appliance.id; + this.deviceId = this.accessory.context.appliance.device.id; + this.accessory.category = this.platform.api.hap.Categories.AIR_CONDITIONER; this.accessory.getService(this.platform.Service.AccessoryInformation)! .setCharacteristic(this.platform.Characteristic.Manufacturer, this.accessory.context.appliance.model.manufacturer) .setCharacteristic(this.platform.Characteristic.Model, this.accessory.context.appliance.model.name) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.appliance.id) - .setCharacteristic(this.platform.Characteristic.Name, this.accessory.context.appliance.nickname); + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.id) + .setCharacteristic(this.platform.Characteristic.Name, this.name); this.service = this.accessory.getService(this.platform.Service.Thermostat) || this.accessory.addService(this.platform.Service.Thermostat); @@ -39,107 +43,70 @@ export class NatureNemoAirConAccessory { .onGet(this.getTargetTemperature.bind(this)) .onSet(this.setTargetTemperature.bind(this)); - this.platform.logger.debug('[%s] id -> %s', this.accessory.context.appliance.nickname, this.accessory.context.appliance.id); - this.name = this.accessory.context.appliance.nickname; - this.id = this.accessory.context.appliance.id; - this.deviceId = this.accessory.context.appliance.device.id; + this.platform.logger.debug('[%s] id -> %s', this.name, this.id); } async getCurrentHeatingCoolingState(): Promise { this.platform.logger.debug('getCurrentHeatingCoolingState called'); - try { - const airConState = await this.platform.natureRemoApi.getAirConState(this.id); - this.platform.logger.info('[%s] Current Heater Cooler State -> %s, %s', this.name, airConState.on, airConState.mode); - return this.convertHeatingCoolingState(airConState.on, airConState.mode); - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; - } + const airConState = await this.platform.natureRemoApi.getAirConState(this.id); + this.platform.logger.info('[%s] Current Heater Cooler State -> %s, %s', this.name, airConState.button || 'power-on', airConState.mode); + return this.convertHeatingCoolingState(airConState.button, airConState.mode); } async getTargetHeatingCoolingState(): Promise { this.platform.logger.debug('getTargetHeatingCoolingState called'); - try { - const airConState = await this.platform.natureRemoApi.getAirConState(this.id); - this.platform.logger.info('[%s] Target Heater Cooler State -> %s, %s', this.name, airConState.on, airConState.mode); - const state = this.convertHeatingCoolingState(airConState.on, airConState.mode); - this.state.targetHeatingCoolingState = state; - return state; - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; - } + const airConState = await this.platform.natureRemoApi.getAirConState(this.id); + this.platform.logger.info('[%s] Target Heater Cooler State -> %s, %s', this.name, airConState.button || 'power-on', airConState.mode); + const state = this.convertHeatingCoolingState(airConState.button, airConState.mode); + this.state.targetHeatingCoolingState = state; + return state; } async setTargetHeatingCoolingState(value: CharacteristicValue): Promise { this.platform.logger.debug('setTargetHeatingCoolingState called ->', value); if (typeof value !== 'number') { - throw new Error('value must be a number'); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } if (value === this.state.targetHeatingCoolingState) { this.platform.logger.debug('[%s] Same state. skip sending', this.name); return; } this.state.targetHeatingCoolingState = value; - try { - if (value === this.platform.Characteristic.TargetHeatingCoolingState.AUTO) { - throw new Error('This plugin does not support auto'); - } else if (value === this.platform.Characteristic.TargetHeatingCoolingState.OFF) { - await this.platform.natureRemoApi.setAirconPowerOff(this.id); - this.platform.logger.info('[%s] Target Heater Cooler State <- OFF', this.name); - } else { - const mode = this.convertOperationMode(value); - await this.platform.natureRemoApi.setAirconOperationMode(this.id, mode); - this.platform.logger.info('[%s] Target Heater Cooler State <- %s', this.name, mode); - } - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; + if (value === this.platform.Characteristic.TargetHeatingCoolingState.AUTO) { + this.platform.logger.error('This plugin does not support auto'); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); + } else if (value === this.platform.Characteristic.TargetHeatingCoolingState.OFF) { + await this.platform.natureRemoApi.setAirconPowerOff(this.id); + this.platform.logger.info('[%s] Target Heater Cooler State <- OFF', this.name); + } else { + const mode = this.convertOperationMode(value); + await this.platform.natureRemoApi.setAirconOperationMode(this.id, mode); + this.platform.logger.info('[%s] Target Heater Cooler State <- %s', this.name, mode); } } async getCurrentTemperature(): Promise { - try { - const sensorValue = await this.platform.natureRemoApi.getSensorValue(this.deviceId); - this.platform.logger.info('[%s] Current Temperature -> %s', this.name, sensorValue.te); - if (sensorValue.te) { - return sensorValue.te; - } else { - throw new Error('cannnot get sensor value'); - } - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; + const device = await this.platform.natureRemoApi.getSensorValue(this.deviceId); + if (device.newest_events.te) { + this.platform.logger.info('[%s] Current Temperature -> %s', this.name, device.newest_events.te.val); + return device.newest_events.te.val; + } else { + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST); } } async getTargetTemperature(): Promise { this.platform.logger.debug('getTargetTemperature called'); - try { - const airConState = await this.platform.natureRemoApi.getAirConState(this.id); - this.platform.logger.info('[%s] Target Temperature -> %s', this.name, airConState.temp); - this.state.targetTemperature = parseFloat(airConState.temp); - return airConState.temp; - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; - } + const airConState = await this.platform.natureRemoApi.getAirConState(this.id); + this.platform.logger.info('[%s] Target Temperature -> %s', this.name, airConState.temp); + this.state.targetTemperature = parseFloat(airConState.temp); + return airConState.temp; } async setTargetTemperature(value: CharacteristicValue): Promise { this.platform.logger.debug('setTargetTemperature called ->', value); if (typeof value !== 'number') { - throw new Error('value must be a number'); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } if (value === this.state.targetTemperature) { this.platform.logger.debug('[%s] Same state. skip sending', this.name); @@ -147,19 +114,12 @@ export class NatureNemoAirConAccessory { } this.state.targetTemperature = value; const targetTemp = `${Math.round(value)}`; - try { - await this.platform.natureRemoApi.setAirconTemperature(this.id, targetTemp); - this.platform.logger.info('[%s] Target Temperature <- %s', this.name, targetTemp); - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; - } + await this.platform.natureRemoApi.setAirconTemperature(this.id, targetTemp); + this.platform.logger.info('[%s] Target Temperature <- %s', this.name, targetTemp); } - private convertHeatingCoolingState(on: boolean, mode: string): number { - if (!on) { + private convertHeatingCoolingState(button: string, mode: string): number { + if (button === 'power-off') { return this.platform.Characteristic.CurrentHeatingCoolingState.OFF; } else { if (mode === 'warm') { @@ -167,7 +127,7 @@ export class NatureNemoAirConAccessory { } else if (mode === 'cool') { return this.platform.Characteristic.CurrentHeatingCoolingState.COOL; } else { - throw new Error(`This plugin does not support ${mode}`); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } } } @@ -178,7 +138,7 @@ export class NatureNemoAirConAccessory { } else if (state === this.platform.Characteristic.TargetHeatingCoolingState.COOL) { return 'cool'; } else { - throw new Error(`This plugin does not support ${state}`); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } } } diff --git a/src/lightAccessory.ts b/src/lightAccessory.ts index bd9de67..99db498 100644 --- a/src/lightAccessory.ts +++ b/src/lightAccessory.ts @@ -10,51 +10,38 @@ export class NatureNemoLightAccessory { private readonly platform: NatureRemoPlatform, private readonly accessory: PlatformAccessory, ) { + this.name = this.accessory.context.appliance.nickname; + this.id = this.accessory.context.appliance.id; + this.accessory.category = this.platform.api.hap.Categories.LIGHTBULB; this.accessory.getService(this.platform.Service.AccessoryInformation)! .setCharacteristic(this.platform.Characteristic.Manufacturer, this.accessory.context.appliance.model.manufacturer) .setCharacteristic(this.platform.Characteristic.Model, this.accessory.context.appliance.model.name) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.appliance.id) - .setCharacteristic(this.platform.Characteristic.Name, this.accessory.context.appliance.nickname); + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.id) + .setCharacteristic(this.platform.Characteristic.Name, this.name); this.service = this.accessory.getService(this.platform.Service.Lightbulb) || this.accessory.addService(this.platform.Service.Lightbulb); this.service.getCharacteristic(this.platform.Characteristic.On) .onGet(this.getOn.bind(this)) .onSet(this.setOn.bind(this)); - this.platform.logger.debug('[%s] id -> %s', this.accessory.context.appliance.nickname, this.accessory.context.appliance.id); - this.name = this.accessory.context.appliance.nickname; - this.id = this.accessory.context.appliance.id; + this.platform.logger.debug('[%s] id -> %s', this.name, this.id); } async getOn(): Promise { this.platform.logger.debug('getOn called'); - try { - const lightState = await this.platform.natureRemoApi.getLightState(this.id); - this.platform.logger.info('[%s] On -> %s', this.name, lightState.on); - return lightState.on; - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; - } + const lightState = await this.platform.natureRemoApi.getLightState(this.id); + this.platform.logger.info('[%s] Power -> %s', this.name, lightState.power); + return lightState.power === 'on'; } async setOn(value: CharacteristicValue): Promise { this.platform.logger.debug('setOn called ->', value); if (typeof value !== 'boolean') { - throw new Error('value must be a boolean'); - } - try { - await this.platform.natureRemoApi.setLight(this.id, value); - this.platform.logger.info('[%s] On <- %s', this.name, value); - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } + await this.platform.natureRemoApi.setLight(this.id, value); + this.platform.logger.info('[%s] On <- %s', this.name, value); } } diff --git a/src/natureRemoApi.ts b/src/natureRemoApi.ts index 031820a..7c0b361 100644 --- a/src/natureRemoApi.ts +++ b/src/natureRemoApi.ts @@ -1,16 +1,20 @@ import { URLSearchParams } from 'url'; import axios, { Axios, AxiosError } from 'axios'; import { Mutex } from 'async-mutex'; +import { API, HapStatusError, Logger } from 'homebridge'; +import { AirConParams, Appliance, Device, LIGHTState } from './types'; -import { - Appliance, - ApplianceCache, - Device, - DeviceCache, - SimpleAirConState, - SimpleLightState, - SimpleSensorValue, -} from './types'; +interface Cache { + updated: number; +} + +interface ApplianceCache extends Cache { + appliances: Appliance[] | null; +} + +interface DeviceCache extends Cache { + devices: Device[] | null; +} const API_URL = 'https://api.nature.global/1'; const CACHE_THRESHOLD = 10 * 1000; @@ -23,7 +27,10 @@ export class NatureRemoApi { private applianceCache: ApplianceCache = { updated: 0, appliances: null }; private deviceCache: DeviceCache = { updated: 0, devices: null }; - constructor(accessToken: string) { + constructor( + private readonly logger: Logger, + private readonly api: API, + accessToken: string) { this.client = axios.create({ baseURL: API_URL, headers: { 'Authorization': `Bearer ${accessToken}` }, @@ -52,47 +59,31 @@ export class NatureRemoApi { }); } - async getAirConState(id: string): Promise { + async getAirConState(id: string): Promise { const appliances = await this.getAllAppliances(); const appliance = appliances.find(val => val.type === 'AC' && val.id === id); - if (appliance === undefined) { - throw new Error(`Cannnot find appliance -> ${id}`); + if (appliance?.settings === undefined) { + throw new this.api.hap.HapStatusError(this.api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST); } - return { - on: appliance.settings?.button !== 'power-off', - mode: appliance.settings?.mode || '', - temp: appliance.settings?.temp || '', - }; + return appliance.settings; } - async getLightState(id: string): Promise { + async getLightState(id: string): Promise { const appliances = await this.getAllAppliances(); const appliance = appliances.find(val => val.type === 'LIGHT' && val.id === id); - if (appliance === undefined) { - throw new Error(`Cannnot find appliance -> ${id}`); + if (appliance?.light === undefined) { + throw new this.api.hap.HapStatusError(this.api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST); } - return { - on: appliance.light?.state.power === 'on', - }; + return appliance.light.state; } - async getSensorValue(id: string): Promise { + async getSensorValue(id: string): Promise { const devices = await this.getAllDevices(); const device = devices.find(val => val.id === id); if (device === undefined) { - throw new Error(`Cannnot find device -> ${id}`); - } - const val = {}; - if (device.newest_events.te) { - val['te'] = device.newest_events.te.val; - } - if (device.newest_events.hu) { - val['hu'] = device.newest_events.hu.val; - } - if (device.newest_events.il) { - val['il'] = device.newest_events.il.val >= 0.0001 ? device.newest_events.il.val : 0.0001; + throw new this.api.hap.HapStatusError(this.api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST); } - return val; + return device; } async setLight(applianceId: string, power: boolean): Promise { @@ -126,8 +117,8 @@ export class NatureRemoApi { try { const res = await this.client.get(url); return res.data; - } catch (error) { - throw new Error(this.getHttpErrorMessage(error as AxiosError)); + } catch (err) { + throw this.convertToHapStatusError(err as AxiosError); } } @@ -135,21 +126,24 @@ export class NatureRemoApi { try { const data = new URLSearchParams(params); await this.client.post(url, data.toString()); - } catch (error) { - throw new Error(this.getHttpErrorMessage(error as AxiosError)); + } catch (err) { + throw this.convertToHapStatusError(err as AxiosError); } } - private getHttpErrorMessage(error: AxiosError): string { + private convertToHapStatusError(error: AxiosError): HapStatusError { if (error.response?.status === 401) { - return 'Authorization error. Access token is wrong.'; + this.logger.error('Authorization error. Access token is wrong.'); + return new this.api.hap.HapStatusError(this.api.hap.HAPStatus.INSUFFICIENT_AUTHORIZATION); } else if (error.response?.status === 429) { const rateLimitLimit = error.response?.headers['x-rate-limit-limit']; const rateLimitReset = error.response?.headers['x-rate-limit-reset']; const rateLimitRemaining = error.response?.headers['x-rate-limit-remaining']; - return `Too Many Requests error. ${rateLimitLimit}, ${rateLimitReset}, ${rateLimitRemaining}`; + this.logger.error(`Too Many Requests error. ${rateLimitLimit}, ${rateLimitReset}, ${rateLimitRemaining}`); + return new this.api.hap.HapStatusError(this.api.hap.HAPStatus.RESOURCE_BUSY); } else { - return `HTTP error. status code -> ${error.response?.status}`; + this.logger.error(`HTTP error. status code -> ${error.response?.status}`); + return new this.api.hap.HapStatusError(this.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); } } } diff --git a/src/platform.ts b/src/platform.ts index 9d8a375..91812f4 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -28,17 +28,14 @@ export class NatureRemoPlatform implements DynamicPlatformPlugin { public readonly api: API, ) { this.logger.debug('accessToken ->', this.config.accessToken); - this.natureRemoApi = new NatureRemoApi(this.config.accessToken as string); + this.natureRemoApi = new NatureRemoApi(this.logger, this.api, this.config.accessToken as string); this.logger.debug('Finished initializing platform:', this.config.name); - this.api.on(APIEvent.DID_FINISH_LAUNCHING, () => { + this.api.on(APIEvent.DID_FINISH_LAUNCHING, async () => { logger.debug('Executed didFinishLaunching callback'); - this.discoverDevices().then(() => { - logger.info('Completed discover accessories'); - }).catch((err) => { - logger.error(err.message); - throw err; - }); + logger.info('Starting discover accessories'); + await this.discoverDevices(); + logger.info('Completed discover accessories'); }); } diff --git a/src/sensorAccessory.ts b/src/sensorAccessory.ts index e2964db..12def16 100644 --- a/src/sensorAccessory.ts +++ b/src/sensorAccessory.ts @@ -14,6 +14,9 @@ export class NatureNemoSensorAccessory { private readonly platform: NatureRemoPlatform, private readonly accessory: PlatformAccessory, ) { + this.name = this.accessory.context.device.name; + this.id = this.accessory.context.device.id; + this.accessory.category = this.platform.api.hap.Categories.SENSOR; const [model, version] = this.accessory.context.device.firmware_version.split('/'); @@ -22,7 +25,7 @@ export class NatureNemoSensorAccessory { .setCharacteristic(this.platform.Characteristic.Model, model || '') .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.device.serial_number) .setCharacteristic(this.platform.Characteristic.FirmwareRevision, version || '') - .setCharacteristic(this.platform.Characteristic.Name, this.accessory.context.device.name); + .setCharacteristic(this.platform.Characteristic.Name, this.name); if (this.accessory.context.device.newest_events.te) { this.tempertureSensorservice @@ -48,85 +51,60 @@ export class NatureNemoSensorAccessory { .onGet(this.getCurrentLightLevel.bind(this)); } - this.platform.logger.debug('[%s] id -> %s', this.accessory.context.device.name, this.accessory.context.device.id); - this.name = this.accessory.context.device.name; - this.id = this.accessory.context.device.id; + this.platform.logger.debug('[%s] id -> %s', this.name, this.id); setInterval(async () => { this.platform.logger.info('[%s] Update sensor values', this.name); - try { - const sensorValue = await this.platform.natureRemoApi.getSensorValue(this.id); - if (sensorValue.te) { - this.platform.logger.info('[%s] Current Temperature -> %s', this.name, sensorValue.te); - this.tempertureSensorservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, sensorValue.te); - } - if (sensorValue.hu) { - this.platform.logger.info('[%s] Current Humidity -> %s', this.name, sensorValue.hu); - this.humiditySensorservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, sensorValue.hu); - } - if (sensorValue.il) { - this.platform.logger.info('[%s] Current Light Level -> %s', this.name, sensorValue.il); - this.lightSensorservice?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, sensorValue.il); - } - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } + const device = await this.platform.natureRemoApi.getSensorValue(this.id); + if (device.newest_events.te) { + const teVal = device.newest_events.te.val; + this.platform.logger.info('[%s] Current Temperature -> %s', this.name, teVal); + this.tempertureSensorservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, teVal); + } + if (device.newest_events.hu) { + const huVal = device.newest_events.hu.val; + this.platform.logger.info('[%s] Current Humidity -> %s', this.name, huVal); + this.humiditySensorservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, huVal); + } + if (device.newest_events.il) { + const ilVal = device.newest_events.il.val >= 0.0001 ? device.newest_events.il.val : 0.0001; + this.platform.logger.info('[%s] Current Light Level -> %s', this.name, ilVal); + this.lightSensorservice?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, ilVal); } }, UPDATE_INTERVAL); } async getCurrentTemperature(): Promise { this.platform.logger.debug('getCurrentTemperature called'); - try { - const sensorValue = await this.platform.natureRemoApi.getSensorValue(this.id); - this.platform.logger.info('[%s] Current Temperature -> %s', this.name, sensorValue.te); - if (sensorValue.te) { - return sensorValue.te; - } else { - throw new Error('cannnot get sensor value'); - } - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; + const device = await this.platform.natureRemoApi.getSensorValue(this.id); + if (device.newest_events.te) { + this.platform.logger.info('[%s] Current Temperature -> %s', this.name, device.newest_events.te.val); + return device.newest_events.te.val; + } else { + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST); } } async getCurrentHumidity(): Promise { this.platform.logger.debug('getCurrentHumidity called'); - try { - const sensorValue = await this.platform.natureRemoApi.getSensorValue(this.id); - this.platform.logger.info('[%s] Current Humidity -> %s', this.name, sensorValue.hu); - if (sensorValue.hu) { - return sensorValue.hu; - } else { - throw new Error('cannnot get sensor value'); - } - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; + const device = await this.platform.natureRemoApi.getSensorValue(this.id); + if (device.newest_events.hu) { + this.platform.logger.info('[%s] Current Humidity -> %s', this.name, device.newest_events.hu.val); + return device.newest_events.hu.val; + } else { + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST); } } async getCurrentLightLevel(): Promise { this.platform.logger.debug('getCurrentLightLevel called'); - try { - const sensorValue = await this.platform.natureRemoApi.getSensorValue(this.id); - this.platform.logger.info('[%s] Current Light Level -> %s', this.name, sensorValue.il); - if (sensorValue.il) { - return sensorValue.il; - } else { - throw new Error('cannnot get sensor value'); - } - } catch (err) { - if (err instanceof Error) { - this.platform.logger.error(err.message); - } - throw err; + const device = await this.platform.natureRemoApi.getSensorValue(this.id); + if (device.newest_events.il) { + const ilVal = device.newest_events.il.val >= 0.0001 ? device.newest_events.il.val : 0.0001; + this.platform.logger.info('[%s] Current Light Level -> %s', this.name, ilVal); + return ilVal; + } else { + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST); } } } diff --git a/src/tvAccessory.ts b/src/tvAccessory.ts index 626e62e..8ba6b96 100644 --- a/src/tvAccessory.ts +++ b/src/tvAccessory.ts @@ -17,17 +17,20 @@ export class NatureNemoTvAccessory { private readonly platform: NatureRemoPlatform, private readonly accessory: PlatformAccessory, ) { + this.name = this.accessory.context.appliance.nickname; + this.id = this.accessory.context.appliance.id; + this.accessory.category = this.platform.api.hap.Categories.TELEVISION; this.accessory.getService(this.platform.Service.AccessoryInformation)! .setCharacteristic(this.platform.Characteristic.Manufacturer, this.accessory.context.appliance.model.manufacturer) .setCharacteristic(this.platform.Characteristic.Model, this.accessory.context.appliance.model.name) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.appliance.id); + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.id) + .setCharacteristic(this.platform.Characteristic.Name, this.name); this.service = this.accessory.getService(this.platform.Service.Television) || this.accessory.addService(this.platform.Service.Television); - this.service.setCharacteristic(this.platform.Characteristic.Name, this.accessory.context.appliance.nickname); - this.service.setCharacteristic(this.platform.Characteristic.ConfiguredName, this.accessory.context.appliance.nickname); + this.service.setCharacteristic(this.platform.Characteristic.ConfiguredName, this.name); this.service.setCharacteristic(this.platform.Characteristic.SleepDiscoveryMode, this.platform.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE); this.service.getCharacteristic(this.platform.Characteristic.Active) @@ -50,9 +53,7 @@ export class NatureNemoTvAccessory { this.televisionSpeakerService.getCharacteristic(this.platform.Characteristic.VolumeSelector) .onSet(this.setVolumeSelector.bind(this)); - this.platform.logger.debug('[%s] id -> %s', this.accessory.context.appliance.nickname, this.accessory.context.appliance.id); - this.name = this.accessory.context.appliance.nickname; - this.id = this.accessory.context.appliance.id; + this.platform.logger.debug('[%s] id -> %s', this.name, this.id); } async getActive(): Promise { @@ -63,20 +64,15 @@ export class NatureNemoTvAccessory { async setActive(value: CharacteristicValue): Promise { this.platform.logger.debug('setActive called ->', value); if (typeof value !== 'number') { - throw new Error('value must be a number'); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } if (value === this.state.active) { this.platform.logger.debug('[%s] Same state. skip sending', this.name); return; } - try { - await this.platform.natureRemoApi.setTvButton(this.id, 'power'); - this.platform.logger.info('[%s] Active <- %s', this.name, value); - this.state.active = value; - } catch (err) { - this.platform.logger.error((err as Error).message); - throw err; - } + await this.platform.natureRemoApi.setTvButton(this.id, 'power'); + this.platform.logger.info('[%s] Active <- %s', this.name, value); + this.state.active = value; } async getActiveIdentifier(): Promise { @@ -87,7 +83,7 @@ export class NatureNemoTvAccessory { async setActiveIdentifier(value: CharacteristicValue): Promise { this.platform.logger.debug('setActiveIdentifier called ->', value); if (typeof value !== 'number') { - throw new Error('value must be a number'); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } if (value === this.state.activeIdentifier) { this.platform.logger.debug('[%s] Same state. skip sending', this.name); @@ -98,13 +94,8 @@ export class NatureNemoTvAccessory { async setRemoteKey(value: CharacteristicValue): Promise { this.platform.logger.debug('setRemoteKey called ->', value); - try { - await this.platform.natureRemoApi.setTvButton(this.id, this.convertRemoteKey(value)); - this.platform.logger.info('[%s] Remote Key <- %s', this.name, value); - } catch (err) { - this.platform.logger.error((err as Error).message); - throw err; - } + await this.platform.natureRemoApi.setTvButton(this.id, this.convertRemoteKey(value)); + this.platform.logger.info('[%s] Remote Key <- %s', this.name, value); } async getMute(): Promise { @@ -115,31 +106,21 @@ export class NatureNemoTvAccessory { async setMute(value: CharacteristicValue): Promise { this.platform.logger.debug('setMute called ->', value); if (typeof value !== 'boolean') { - throw new Error('value must be a boolean'); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } if (value === this.state.mute) { this.platform.logger.debug('[%s] Same state. skip sending', this.name); return; } - try { - await this.platform.natureRemoApi.setTvButton(this.id, 'mute'); - this.platform.logger.info('[%s] Mute <- %s', this.name, value); - this.state.mute = value; - } catch (err) { - this.platform.logger.error((err as Error).message); - throw err; - } + await this.platform.natureRemoApi.setTvButton(this.id, 'mute'); + this.platform.logger.info('[%s] Mute <- %s', this.name, value); + this.state.mute = value; } async setVolumeSelector(value: CharacteristicValue): Promise { this.platform.logger.debug('setVolumeSelector called ->', value); - try { - await this.platform.natureRemoApi.setTvButton(this.id, this.convertVolumeSelector(value)); - this.platform.logger.info('[%s] VolumeSelector <- %s', this.name, value); - } catch (err) { - this.platform.logger.error((err as Error).message); - throw err; - } + await this.platform.natureRemoApi.setTvButton(this.id, this.convertVolumeSelector(value)); + this.platform.logger.info('[%s] VolumeSelector <- %s', this.name, value); } private convertRemoteKey(value: CharacteristicValue): string { @@ -171,7 +152,7 @@ export class NatureNemoTvAccessory { case this.platform.Characteristic.RemoteKey.INFORMATION: return 'display'; default: - throw new Error(`This plugin does not support ${value}`); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } } @@ -181,7 +162,7 @@ export class NatureNemoTvAccessory { } else if (value === this.platform.Characteristic.VolumeSelector.DECREMENT) { return 'vol-down'; } else { - throw new Error(`This plugin does not support ${value}`); + throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST); } } } diff --git a/src/types.ts b/src/types.ts index 7ef1fb2..a08fd8a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -146,31 +146,3 @@ export interface EchonetLiteProperty { val: string; updated_at: DateTime; } - -export interface SimpleAirConState { - on: boolean; - mode: string; - temp: string; -} - -export interface SimpleLightState { - on: boolean; -} - -export interface SimpleSensorValue { - te?: number; - hu?: number; - il?: number; -} - -export interface Cache { - updated: number; -} - -export interface ApplianceCache extends Cache { - appliances: Appliance[] | null; -} - -export interface DeviceCache extends Cache { - devices: Device[] | null; -}