UNPKG

homebridge-freeathome-local-api

Version:

Control your free@home setup using the local API provided by your System Access Point

730 lines (691 loc) 27.6 kB
import { Channel, Device } from "freeathome-local-api-client"; import { Characteristic, CharacteristicValue, PlatformAccessory, Service, WithUUID, } from "homebridge"; import { FreeAtHomeContext } from "../src/freeAtHomeContext.js"; import { RoomTemperatureControllerAccessory } from "../src/roomTemperatureControllerAccessory.js"; import { EmptyGuid } from "../src/util.js"; import { createPlatformAccessory, MockFreeAtHomeHomebridgePlatform, } from "./platform.mock.js"; describe("Room Temperature Controller Accessory", () => { let channel: Channel; let device: Device; let platform: MockFreeAtHomeHomebridgePlatform; let platformAccessory: PlatformAccessory<FreeAtHomeContext>; beforeEach(() => { channel = { outputs: { odp0000: { pairingID: 48, }, odp0001: { pairingID: 50, }, odp0006: { pairingID: 51, }, odp0008: { pairingID: 56, }, odp0010: { pairingID: 304, }, }, inputs: { idp0012: { pairingID: 66 }, idp0016: { pairingID: 320 }, }, }; device = {}; platform = new MockFreeAtHomeHomebridgePlatform(); platformAccessory = createPlatformAccessory( "Room Temperature Controller Accessory" ); platformAccessory.context = { channel: channel, channelId: "ch1234", device: device, deviceSerial: "ABB7xxxxxxxx", }; }); afterEach(() => { platform.resetLoggerCalls(); }); it("should throw if expected data points are missing on channel", () => { channel.inputs = undefined; expect( () => new RoomTemperatureControllerAccessory(platform, platformAccessory) ).toThrowError("Channel lacks expected input or output data points."); }); it("should be created with default state", async () => { const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); const characteristicTdu = instance.service.getCharacteristic( accessory.platform.Characteristic.TemperatureDisplayUnits ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.OFF ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.OFF ); expect(await characteristicCt.handleGetRequest()).toBe(0); expect(await characteristicTt.handleGetRequest()).toBe(7); expect(await characteristicTdu.handleGetRequest()).toBe( accessory.platform.Characteristic.TemperatureDisplayUnits.CELSIUS ); }); it("should be created with heating state", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.HEAT ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(20); expect(await characteristicTt.handleGetRequest()).toBe(21.5); }); it("should be created with cooling state", async () => { channel.outputs!.odp0000.value = "0"; channel.outputs!.odp0001.value = "1"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "23"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.COOL ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(23); expect(await characteristicTt.handleGetRequest()).toBe(21.5); }); it("should be created with controller active but not heating or cooling", async () => { channel.outputs!.odp0000.value = "0"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "23"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.OFF ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(23); expect(await characteristicTt.handleGetRequest()).toBe(21.5); }); it("should be created with invalid CurrentHeatingCooling state", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "1"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "23"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.OFF ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(23); expect(await characteristicTt.handleGetRequest()).toBe(21.5); // eslint-disable-next-line @typescript-eslint/unbound-method expect(accessory.platform.log.error).toHaveBeenCalledWith( "Invalid State: Heating and cooling cannot be on simultaneously!" ); }); it("should not handle a request to set TargetTemperature characteristic if the value has not changed", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristic = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristic.handleGetRequest()).toBe(21.5); const spy = spyOn(accessory.platform.sysap, "setDatapoint"); await characteristic.handleSetRequest(21.5); expect(spy).not.toHaveBeenCalled(); }); it("should handle request to set TargetTemperature characteristic if the value has changed", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristic = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristic.handleGetRequest()).toBe(21.5); const spy = spyOn(accessory.platform.sysap, "setDatapoint"); await characteristic.handleSetRequest(22); expect(await characteristic.handleGetRequest()).toBe(22); expect(spy).toHaveBeenCalledWith( EmptyGuid, "ABB7xxxxxxxx", "ch1234", "idp0016", "22.0" ); // eslint-disable-next-line @typescript-eslint/unbound-method expect(platform.log.info).toHaveBeenCalledWith( "Room Temperature Controller Accessory (Room Temperature Controller ABB7xxxxxxxx (ch1234)) set characteristic TargetTemperature -> 22" ); }); it("should not handle a request to set TargetHeatingCoolingState characteristic if the value has not changed", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristic = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); expect(await characteristic.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); const spy = spyOn(accessory.platform.sysap, "setDatapoint"); await characteristic.handleSetRequest( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(spy).not.toHaveBeenCalled(); }); it("should handle request to set TargetHeatingCoolingState characteristic from auto to off", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristic = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); expect(await characteristic.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); const spy = spyOn(accessory.platform.sysap, "setDatapoint"); await characteristic.handleSetRequest( accessory.platform.Characteristic.TargetHeatingCoolingState.OFF ); expect(await characteristic.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.OFF ); expect(spy).toHaveBeenCalledWith( EmptyGuid, "ABB7xxxxxxxx", "ch1234", "idp0012", "0" ); // eslint-disable-next-line @typescript-eslint/unbound-method expect(platform.log.info).toHaveBeenCalledWith( // eslint-disable-next-line max-len "Room Temperature Controller Accessory (Room Temperature Controller ABB7xxxxxxxx (ch1234)) set characteristic TargetHeatingCoolingState -> 0" ); }); it("should handle request to set TargetHeatingCoolingState characteristic from off to auto", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "0"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); expect(accessory).toBeTruthy(); const instance = accessory as unknown as { service: Service; }; const characteristic = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); expect(await characteristic.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.OFF ); const spy = spyOn(accessory.platform.sysap, "setDatapoint"); await characteristic.handleSetRequest( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristic.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(spy).toHaveBeenCalledWith( EmptyGuid, "ABB7xxxxxxxx", "ch1234", "idp0012", "1" ); // eslint-disable-next-line @typescript-eslint/unbound-method expect(platform.log.info).toHaveBeenCalledWith( // eslint-disable-next-line max-len "Room Temperature Controller Accessory (Room Temperature Controller ABB7xxxxxxxx (ch1234)) set characteristic TargetHeatingCoolingState -> 3" ); }); it("should ignore update if datapoint is unknown", async () => { const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); const instance = accessory as unknown as { service: Service; doUpdateDatapoint: ( acccessoryDisplayType: string, service: Service, characteristic: WithUUID<new () => Characteristic>, characteristicValue: CharacteristicValue ) => void; }; const spy = spyOn(instance, "doUpdateDatapoint"); const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.OFF ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.OFF ); expect(await characteristicCt.handleGetRequest()).toBe(0); expect(await characteristicTt.handleGetRequest()).toBe(7); accessory.updateDatapoint("test", "0"); expect(spy).not.toHaveBeenCalled(); }); it("should process update to heating mode datapoint", async () => { channel.outputs!.odp0000.value = "0"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); const instance = accessory as unknown as { service: Service; doUpdateDatapoint: ( acccessoryDisplayType: string, service: Service, characteristic: WithUUID<new () => Characteristic>, characteristicValue: CharacteristicValue ) => void; }; const spy = spyOn(instance, "doUpdateDatapoint"); const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.OFF ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(20); expect(await characteristicTt.handleGetRequest()).toBe(21.5); accessory.updateDatapoint("odp0000", "1"); expect(spy).toHaveBeenCalledWith( "Room Temperature Controller", instance.service, accessory.platform.Characteristic.CurrentHeatingCoolingState, accessory.platform.Characteristic.CurrentHeatingCoolingState.HEAT ); }); it("should process update to cooling mode datapoint", async () => { channel.outputs!.odp0000.value = "0"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "23"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); const instance = accessory as unknown as { service: Service; doUpdateDatapoint: ( acccessoryDisplayType: string, service: Service, characteristic: WithUUID<new () => Characteristic>, characteristicValue: CharacteristicValue ) => void; }; const spy = spyOn(instance, "doUpdateDatapoint"); const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.OFF ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(23); expect(await characteristicTt.handleGetRequest()).toBe(21.5); accessory.updateDatapoint("odp0001", "1"); expect(spy).toHaveBeenCalledWith( "Room Temperature Controller", instance.service, accessory.platform.Characteristic.CurrentHeatingCoolingState, accessory.platform.Characteristic.CurrentHeatingCoolingState.COOL ); }); it("should process update to controller on/off datapoint", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "0"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); const instance = accessory as unknown as { service: Service; doUpdateDatapoint: ( acccessoryDisplayType: string, service: Service, characteristic: WithUUID<new () => Characteristic>, characteristicValue: CharacteristicValue ) => void; }; const spy = spyOn(instance, "doUpdateDatapoint"); const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.OFF ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.OFF ); expect(await characteristicCt.handleGetRequest()).toBe(20); expect(await characteristicTt.handleGetRequest()).toBe(21.5); accessory.updateDatapoint("odp0008", "1"); expect(spy).toHaveBeenCalledWith( "Room Temperature Controller", instance.service, accessory.platform.Characteristic.CurrentHeatingCoolingState, accessory.platform.Characteristic.CurrentHeatingCoolingState.HEAT ); expect(spy).toHaveBeenCalledWith( "Room Temperature Controller", instance.service, accessory.platform.Characteristic.TargetHeatingCoolingState, accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); }); it("should process update to target temperature datapoint", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); const instance = accessory as unknown as { service: Service; doUpdateDatapoint: ( acccessoryDisplayType: string, service: Service, characteristic: WithUUID<new () => Characteristic>, characteristicValue: CharacteristicValue ) => void; }; const spy = spyOn(instance, "doUpdateDatapoint"); const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.HEAT ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(20); expect(await characteristicTt.handleGetRequest()).toBe(21.5); accessory.updateDatapoint("odp0006", "23"); expect(spy).toHaveBeenCalledWith( "Room Temperature Controller", instance.service, accessory.platform.Characteristic.TargetTemperature, 23 ); }); it("should process update to current temperature datapoint", async () => { channel.outputs!.odp0000.value = "1"; channel.outputs!.odp0001.value = "0"; channel.outputs!.odp0006.value = "21.5"; channel.outputs!.odp0008.value = "1"; channel.outputs!.odp0010.value = "20"; const accessory = new RoomTemperatureControllerAccessory( platform, platformAccessory ); const instance = accessory as unknown as { service: Service; doUpdateDatapoint: ( acccessoryDisplayType: string, service: Service, characteristic: WithUUID<new () => Characteristic>, characteristicValue: CharacteristicValue ) => void; }; const spy = spyOn(instance, "doUpdateDatapoint"); const characteristicChcs = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentHeatingCoolingState ); const characteristicThcs = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetHeatingCoolingState ); const characteristicCt = instance.service.getCharacteristic( accessory.platform.Characteristic.CurrentTemperature ); const characteristicTt = instance.service.getCharacteristic( accessory.platform.Characteristic.TargetTemperature ); expect(await characteristicChcs.handleGetRequest()).toBe( accessory.platform.Characteristic.CurrentHeatingCoolingState.HEAT ); expect(await characteristicThcs.handleGetRequest()).toBe( accessory.platform.Characteristic.TargetHeatingCoolingState.AUTO ); expect(await characteristicCt.handleGetRequest()).toBe(20); expect(await characteristicTt.handleGetRequest()).toBe(21.5); accessory.updateDatapoint("odp0010", "20.46"); expect(spy).toHaveBeenCalledWith( "Room Temperature Controller", instance.service, accessory.platform.Characteristic.CurrentTemperature, 20.46 ); }); });