From 99bd9361cd821180860d0243206123c86c53488c Mon Sep 17 00:00:00 2001 From: JDierkse Date: Mon, 3 Mar 2025 14:38:41 +0100 Subject: [PATCH] Add ThermostatAccessory --- httpServer.ts | 5 + platform.ts | 20 +++- thermostatAccessory.ts | 208 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 thermostatAccessory.ts diff --git a/httpServer.ts b/httpServer.ts index 7f72dab..dcb3e64 100644 --- a/httpServer.ts +++ b/httpServer.ts @@ -59,6 +59,11 @@ export class httpServer { } } + if (type == "Toon") { + type = "Thermostat" + } + + var foundAccessory for (const accessory of this.platform.accessories) { diff --git a/platform.ts b/platform.ts index a11d93c..1bbfe27 100644 --- a/platform.ts +++ b/platform.ts @@ -11,7 +11,7 @@ import { environmentSensorAccessory } from './environmentSensorAccessory' import { doorWindowSensorAccessory } from './doorWindowSensorAccessory' import { motionSensorAccessory } from './motionSensorAccessory' import { cameraAccessory } from './cameraAccessory' -import { activityAccessory } from './activityAccessory' +import { thermostatAccessory } from './thermostatAccessory' import { httpServer } from './httpServer' export class domoticaPlatform implements DynamicPlatformPlugin { @@ -122,6 +122,16 @@ export class domoticaPlatform implements DynamicPlatformPlugin { .catch((error) => { this.log.debug('activityAccessory.discoverDevices Error ->' + error) }) + + this.discoveryStarted('thermostatAccessory') + thermostatAccessory.discoverDevices(this) + .then((devices) => { + this.processDiscoveredDevices(devices) + this.discoveryCompleted('thermostatAccessory') + }) + .catch((error) => { + this.log.debug('thermostatAccessory.discoverDevices Error ->' + error) + }) } processDiscoveredDevices(domoticaDevices: device[]) { @@ -154,8 +164,8 @@ export class domoticaPlatform implements DynamicPlatformPlugin { this.accessories.push(new motionSensorAccessory(this, existingAccessory)) } else if (existingAccessory.context.device.type == 'Camera') { this.accessories.push(new cameraAccessory(this, existingAccessory)) - } else if (existingAccessory.context.device.type == 'Activity') { - this.accessories.push(new activityAccessory(this, existingAccessory)) + } else if (existingAccessory.context.device.type == 'Thermostat') { + this.accessories.push(new thermostatAccessory(this, existingAccessory)) } } else { this.log.info('+ ' + device.type + ': ' + device.deviceName) @@ -177,8 +187,8 @@ export class domoticaPlatform implements DynamicPlatformPlugin { this.accessories.push(new motionSensorAccessory(this, accessory)) } else if (accessory.context.device.type == 'Camera') { this.accessories.push(new cameraAccessory(this, accessory)) - } else if (accessory.context.device.type == 'Activity') { - this.accessories.push(new activityAccessory(this, accessory)) + } else if (accessory.context.device.type == 'Thermostat') { + this.accessories.push(new thermostatAccessory(this, accessory)) } this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]) diff --git a/thermostatAccessory.ts b/thermostatAccessory.ts new file mode 100644 index 0000000..8df37ae --- /dev/null +++ b/thermostatAccessory.ts @@ -0,0 +1,208 @@ +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge' + +import { accessory } from './accessory' +import { device } from './device' +import { httpRequest, response } from './httpPromise' +import { domoticaPlatform } from './platform' + +export class thermostatAccessory extends accessory { + private service: Service + + private state = { + CurrentHeatingCoolingState: 0, + TargetHeatingCoolingState: 0, + CurrentTemperature: 0.0, + TargetTemperature: 10.0, + TemperatureDisplayUnits: 0 + } + + static async discoverDevices(platform: domoticaPlatform): Promise { + return new Promise((resolve, reject) => { + let thermostatDevices: device[] = [] + + httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/toonthermostat') + .then((response) => { + let devices = JSON.parse(response.body) + + for (const device of devices) { + if (device.enabled == "true") { + let object: device = { + type: "Thermostat", + typeId: device.id, + uniqueId: "Thermostat." + device.id, + deviceName: device.name, + switchable: "false", + dimmable: "false", + detailedType: "" + } + thermostatDevices.push(object) + } + } + + resolve(thermostatDevices) + }) + .catch((error) => { + reject('thermostatAccessory::discoverDevices Error ->' + error) + }) + }) + } + + constructor( + private readonly platform: domoticaPlatform, + private readonly accessory: PlatformAccessory, + ) { + // https://developers.homebridge.io/#/service/Thermostat + super("Thermostat", accessory.context.device.typeId) + + this.accessory.getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Thermostat') + .setCharacteristic(this.platform.Characteristic.Model, 'Domotica-Thermostat') + .setCharacteristic(this.platform.Characteristic.SerialNumber, '09000' + accessory.context.device.typeId) + + this.service = this.accessory.getService(this.platform.Service.Thermostat) || this.accessory.addService(this.platform.Service.Thermostat) + + this.service.getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState) + .onGet(this.getCurrentHeatingCoolingState.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState) + .onGet(this.getTargetHeatingCoolingState.bind(this)); + //.onSet(this.setTargetHeatingCoolingState.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature) + .onGet(this.getCurrentTemperature.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.TargetTemperature) + .onGet(this.getTargetTemperature.bind(this)) + .onSet(this.setTargetTemperature.bind(this)); + + this.service.getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits) + .onGet(this.getTemperatureDisplayUnits.bind(this)); + //.onSet(this.setTemperatureDisplayUnits.bind(this)); + + this.setName(accessory.context.device.deviceName) + this.update() + } + + setName(name) { + this.service.setCharacteristic(this.platform.Characteristic.Name, name) + } + + setValue(key, value) { + if (key == "burnerState" && value != "") { + if (value == 'Off') { + this.state.CurrentHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.OFF + this.state.TargetHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.OFF + + } else { + this.state.CurrentHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.HEAT + this.state.TargetHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.HEAT + } + this.service.updateCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState, this.state.CurrentHeatingCoolingState) + this.service.updateCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState, this.state.TargetHeatingCoolingState) + } else if (key == "currentSetPoint" && value != "") { + this.state.TargetTemperature = value / 100 + this.service.updateCharacteristic(this.platform.Characteristic.TargetTemperature, this.state.TargetTemperature) + } else if (key == "currentTemperature" && value != "") { + this.state.CurrentTemperature = value / 100 + this.service.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.state.CurrentTemperature) + } + } + + update() { + // curl -X POST "http://192.168.101.100:8080/API/Status" -d '[{"type": "toonthermostat", "query": "state"}]' + // {"toonthermostat": + // [{"burnerlevel":"low", + // "burnerstate":"heater", + // "currentsetpoint":"1800", + // "currenttemperature":"1805", + // "device_id":"113", + // "id":"1", + // "name":"Toon", + // "programstate":"off", + // "temperaturestate":"Home"}]} + + const data = JSON.stringify([{ + type: 'toonthermostat', + query: 'state' + }]) + + httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data) + .then((response) => { + const devices = JSON.parse(response.body) + + if (devices.hasOwnProperty('toonthermostat')) { + for (const device of devices.toonthermostat) { + if (device.id == this.accessory.context.device.typeId) { + if (device.burnerstate == 'off') { + this.state.CurrentHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.OFF + this.state.TargetHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.OFF + } else { + this.state.CurrentHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.HEAT + this.state.TargetHeatingCoolingState = this.platform.Characteristic.CurrentHeatingCoolingState.HEAT + } + + this.state.CurrentTemperature = device.currenttemperature / 100 + this.state.TargetTemperature = device.currentsetpoint / 100 + + this.state.TemperatureDisplayUnits = this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS + + this.service.updateCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState, this.state.CurrentHeatingCoolingState) + this.service.updateCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState, this.state.TargetHeatingCoolingState) + this.service.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.state.CurrentTemperature) + this.service.updateCharacteristic(this.platform.Characteristic.TargetTemperature, this.state.TargetTemperature) + this.service.updateCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits, this.state.TemperatureDisplayUnits) + + break + } + } + } + }) + .catch((error) => { + this.platform.log.debug('thermostatAccessory::update Error ->' + error) + }) + } + + getCurrentHeatingCoolingState(): CharacteristicValue { + // https://developers.homebridge.io/#/characteristic/CurrentHeatingCoolingState + return this.state.CurrentHeatingCoolingState + } + + getTargetHeatingCoolingState(): CharacteristicValue { + // https://developers.homebridge.io/#/characteristic/TargetHeatingCoolingState + return this.state.TargetHeatingCoolingState + } + + setTargetHeatingCoolingState(value) { + // https://developers.homebridge.io/#/characteristic/TargetHeatingCoolingState + } + + getCurrentTemperature(): CharacteristicValue { + // https://developers.homebridge.io/#/characteristic/CurrentTemperature + return this.state.CurrentTemperature + } + + getTargetTemperature(): CharacteristicValue { + // https://developers.homebridge.io/#/characteristic/TargetTemperature + return this.state.TargetTemperature + } + + async setTargetTemperature(value) { + // https://developers.homebridge.io/#/characteristic/TargetTemperature + this.state.TargetTemperature = value + + // http://$domoticaIP/external/WebUpdate.php?data=Toon/$id/ + httpRequest("http://" + 'administration-container:8080' + '/external/WebUpdate.php?data=Toon/' + this.accessory.context.device.typeId + '///' + (this.state.TargetTemperature * 100)) + .catch((error) => { + this.platform.log.debug('thermostatAccessory::setOn Error ->' + error) + }) + } + + getTemperatureDisplayUnits(): CharacteristicValue { + // https://developers.homebridge.io/#/characteristic/TemperatureDisplayUnits + return this.state.TemperatureDisplayUnits + } + + setTemperatureDisplayUnits(value) { + // https://developers.homebridge.io/#/characteristic/TemperatureDisplayUnits + } +}