@zswuwing/homebridge-wyze-smart-home
Version:
Wyze Smart Home plugin for Homebridge
277 lines (214 loc) • 12.7 kB
JavaScript
const { Service, Characteristic } = require('../types')
const WyzeAccessory = require('./WyzeAccessory')
// want to add a temperature/humidity sensor to display how long it will take for system to complete current job - prop time2temp_val
// add temp humidity for real humidity as well
// add switch maybe to control fan mode - switch on is fan auto and switch off is fan off
// want to add temp sensors for all that are connected to thermostat as well
const noResponse = new Error('No Response')
noResponse.toString = () => { return noResponse.message }
module.exports = class WyzeThermostat extends WyzeAccessory {
constructor (plugin, homeKitAccessory) {
super(plugin, homeKitAccessory)
// set accessory information
this.homeKitAccessory.getService(Service.Thermostat.AccessoryInformation)
.setCharacteristic(Characteristic.Manufacturer, 'Wyze')
.setCharacteristic(Characteristic.Model, 'Thermostat')
.setCharacteristic(Characteristic.SerialNumber, 'WYZE-THERMOSTAT');
this.service = this.getThermostatService()
// GET current heat/cool/off state
this.service.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
.onGet(this.handleCurrentHeatingCoolingStateGet.bind(this));
// GET current temperature
this.service.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(this.handleCurrentTemperatureGet.bind(this));
// Target heat/cool/off state
this.service.getCharacteristic(Characteristic.TargetHeatingCoolingState)
.onGet(this.handleTargetHeatingCoolingStateGet.bind(this))
.onSet(this.handleTargetHeatingCoolingStateSet.bind(this));
// Target temperature handlers - needed given that the system is not in auto
this.service.getCharacteristic(Characteristic.TargetTemperature)
.onGet(this.handleTargetTemperatureGet.bind(this))
.onSet(this.handleTargetTemperatureSet.bind(this));
// Cooling setpoint handlers - needed for system in Auto
this.service.getCharacteristic(Characteristic.CoolingThresholdTemperature)
.onGet(this.handleCoolingThresholdTemperatureGet.bind(this))
.onSet(this.handleCoolingThresholdTemperatureSet.bind(this));
// Heating setpoint handlers - needed for system in Auto
this.service.getCharacteristic(Characteristic.HeatingThresholdTemperature)
.onGet(this.handleHeatingThresholdTemperatureGet.bind(this))
.onSet(this.handleHeatingThresholdTemperatureSet.bind(this));
// Target display unit handlers
this.service.getCharacteristic(Characteristic.TemperatureDisplayUnits)
.onGet(this.handleTemperatureDisplayUnitsGet.bind(this))
.onSet(this.handleTemperatureDisplayUnitsSet.bind(this));
this.updateCharacteristics();
}
setDefaultValues() {
this.thermostatTemperature = 69.0
this.thermostatCoolSetpoint = 65.0
this.thermostatHeatSetpoint = 72.0
this.thermostatModeSys = "auto"
this.thermostatWorkingState = "idle"
this.thermostatTempUnit = "F"
}
// GET Methods for returning device data to homekit when homekit asks for example when loading the home app for the first time
async handleCurrentTemperatureGet() {
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleCurrentTemperatureGet status of "${this.display_name}" to ${this.thermostatTemperature}`)
return this.f2c(this.thermostatTemperature)
}
async handleCurrentHeatingCoolingStateGet() {
return this.Wyze2HomekitWorkingStates[this.thermostatWorkingState]
}
async handleTargetHeatingCoolingStateGet() {
return this.Wyze2HomekitStates[this.thermostatModeSys]
}
async handleTargetTemperatureGet() {
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] handleTargetTemperatureGet Target Temp: " + this.c2f(this.getTargetTemperatureForSystemState()))
return this.getTargetTemperatureForSystemState()
}
async handleCoolingThresholdTemperatureGet() {
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] handleCoolingThresholdTemperatureGet Cool Setpoint: " + (this.thermostatCoolSetpoint))
return this.f2c(this.thermostatCoolSetpoint)
}
async handleHeatingThresholdTemperatureGet() {
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] handleHeatingThresholdTemperatureGet Heat Setpoint: " + (this.thermostatHeatSetpoint))
return this.f2c(this.thermostatHeatSetpoint)
}
async handleTemperatureDisplayUnitsGet() {
return this.Wyze2HomekitUnits[this.thermostatTempUnit]
}
// SET Methods for taking data from homekit and applying it to Wyze thermostat
async handleTargetHeatingCoolingStateSet(value) {
let targetState = this.getKey(this.Wyze2HomekitStates, value)
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleTargetHeatingCoolingStateSet status of "${this.display_name}" to ${targetState} (${value})`)
this.setHvacMode(targetState)
this.service.getCharacteristic(Characteristic.TargetHeatingCoolingState).updateValue(value);
this.thermostatModeSys = targetState
this.service.getCharacteristic(Characteristic.TargetTemperature).updateValue(this.getTargetTemperatureForSystemState())
}
async handleTargetTemperatureSet(value) {
let targetTemp = this.c2f(value)
let currentStateNumber = this.Wyze2HomekitStates[this.thermostatModeSys]
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleTargetTemperatureSet status of "${this.display_name}" to ${targetTemp} for mode ${this.thermostatModeSys} which is ${currentStateNumber}`)
// switch on current heating cooling state since we are NOT in auto mode
switch(currentStateNumber) {
case this.Wyze2HomekitStates.auto:
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] WARNING: handleTargetTemperatureSet cannot set value since system is in AUTO`)
break
case this.Wyze2HomekitStates.cool:
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleTargetTemperatureSet for COOLING`)
this.handleCoolingThresholdTemperatureSet(value)
break
case this.Wyze2HomekitStates.heat:
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleTargetTemperatureSet for HEATING`)
this.handleHeatingThresholdTemperatureSet(value)
break
case this.Wyze2HomekitStates.off:
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] WARNING: handleTargetTemperatureSet cannot set value since system is OFF`)
break
default:
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] WARNING: handleTargetTemperatureSet cannot set value since system mode is UNDEFINED`)
break
}
this.service.getCharacteristic(Characteristic.TargetTemperature).updateValue(value)
}
async handleCoolingThresholdTemperatureSet(value) {
let c = this.clamp(value, 10, 35)
let val = Math.round(this.c2f(c))
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleCoolingThresholdTemperatureSet status of "${this.display_name}" to ${val}`)
this.setCoolPoint(val)
this.thermostatCoolSetpoint = val
this.service.getCharacteristic(Characteristic.CoolingThresholdTemperature).updateValue(c)
}
async handleHeatingThresholdTemperatureSet(value) {
let c = this.clamp(value, 0, 25)
let val = Math.round(this.c2f(c))
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleHeatingThresholdTemperatureSet status of "${this.display_name}" to ${val}`)
this.setHeatPoint(val)
this.thermostatHeatSetpoint = val
this.service.getCharacteristic(Characteristic.HeatingThresholdTemperature).updateValue(c)
}
async handleTemperatureDisplayUnitsSet(value) {
// nothing for now, can use internally maybe
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] handleTemperatureDisplayUnitsSet status of "${this.display_name}" to ${value}`)
this.service.getCharacteristic(Characteristic.TemperatureDisplayUnits).updateValue(value)
}
// this is where we do the magic
async updateCharacteristics () {
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] Updating status of "${this.display_name}"`)
// have to wait for this call to finish before updating or we might get null data
this.thermostatGetIotProp().then(this.fillData.bind(this))
}
fillData() {
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] Temp: " + (this.thermostatTemperature))
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] Target Temp: " + this.c2f(this.getTargetTemperatureForSystemState()))
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] Mode Sys: " + this.thermostatModeSys + " for " + this.Wyze2HomekitStates[this.thermostatModeSys])
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] Working State: " + this.thermostatWorkingState + " for " + this.Wyze2HomekitWorkingStates[this.thermostatWorkingState])
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] Cool Setpoint: " + (this.thermostatCoolSetpoint))
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] Heat Setpoint: " + (this.thermostatHeatSetpoint))
if(this.plugin.config.logging == "debug") this.plugin.log("[Thermostat] Temp Units: " + this.Wyze2HomekitUnits[this.thermostatTempUnit])
}
getTargetTemperatureForSystemState() {
// relies on temperature setpoints to tell Homekit what the system is currently doing
// Wyze seems to not actually send the "working state" to user at the moment so we have to decide what the system is doing manually
let s = this.Wyze2HomekitStates[this.thermostatModeSys]
if (s == this.Wyze2HomekitStates.cool) {
return this.f2c(this.thermostatCoolSetpoint)
} else if (s == this.Wyze2HomekitStates.heat) {
return this.f2c(this.thermostatHeatSetpoint)
} else if (s == this.Wyze2HomekitStates.auto) {
if (this.thermostatCoolSetpoint < this.thermostatTemperature) {
return this.f2c(this.thermostatCoolSetpoint)
} else if (this.thermostatHeatSetpoint > this.thermostatTemperature) {
return this.f2c(this.thermostatHeatSetpoint)
} else {
return this.f2c(this.thermostatTemperature)
}
} else {
return this.f2c(this.thermostatTemperature)
}
}
getThermostatService() {
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] Retrieving previous service for "${this.display_name}"`)
let service = this.homeKitAccessory.getService(Service.Thermostat)
if (!service) {
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] Adding service for "${this.display_name}"`)
service = this.homeKitAccessory.addService(Service.Thermostat)
}
return service
}
f2c(fahrenheit) {
let out = ((fahrenheit) - 32.0) / 1.8
return out
}
c2f(celsius) {
return ((celsius) * 1.8) + 32.0
}
getKey(object, value) {
return Object.keys(object)[value]
}
Wyze2HomekitUnits = {
C: 0,
F: 1
}
Wyze2HomekitStates = {
off: 0,
heat: 1,
cool: 2,
auto: 3
}
Wyze2HomekitWorkingStates = {
idle: 0,
heating: 1,
cooling: 2
}
clamp(number, min, max) {
if (number < min) {
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] Clamping value: ${number} to min ${min}`)
}
if (number > max) {
if(this.plugin.config.logging == "debug") this.plugin.log(`[Thermostat] Clamping value: ${number} to max ${max}`)
}
return Math.max(min, Math.min(number, max));
}
}