homebridge-tuya
homebridge-tuya copied to clipboard
SimpleHeater: the set temperature is doubled
First of all I would like to thank you for your work here - great plugin!
I use a smart thermostat (BHT-002-GBLW) als a simpleHeater. In the config I use "temperatureDivisor": 2 to become the right temperatur from my thermostat This works fine so far. If I now set the temperature manually in the Apple Home app (e.g. to 20 degrees) the set tempetature will be doubled and springs to 40 degrees.
Without use "temperatureDivisor" all ist fine (but I habe the wrong room temperature).
Environment (please complete the following information):*
- OS: macOS Catalina
- iOS version: 13.7 & 14 beta
- Homehubs: Hoods 3.2.6
- Node Version: 12.13.1
- Plugin Version: 1.0.5
- Accessory Type simpleHeater
Additional context
{ "platform": "TuyaLan", "devices": [ { "name": "thermostat", "type": "SimpleHeater", "manufacturer": "Branded", "model": "Wifi Thermostat", "id": "xxxx", "key": "xxxx", "temperatureDivisor": 2 } ] }
I have the same problems. Presse bug fixing.
thank you verry much
@iRayanKhan du you have any tips?
Hi there, I'm not sure why your thermostat would do that. I could try to make a custom accessory (no guarantees) to just divide the temperature display in half, and retain the normal temp. I'm backed up so this may take awhile before I can get to it.
I'm also facing exactly the same issue. Would be great, if you could add the custom accessory! Thank you!
Having the same issue for quite some time. Everything is great, "temperatureDivisor" is set to 2 and everything shows up correctly except when i change the set temperature, its not divided when homekit updates the accessory status after sending the command. If i set 25 degrees, my thermostat changes to 25, but homekit jumps to 50 immediately, after some time it shows up correctly again. video
I don't have a device to test this, or a test harness, but this is the only reason I could see for incorrect reporting. Would someone care to add some extra logging and test?
This block of code here sets the target temperature, taking the divisor into account before sending the updated value to the device.
setTargetThresholdTemperature(value, callback) {
this.setState(this.dpDesiredTemperature, value * this.temperatureDivisor, err => {
if (err) return callback(err);
if (this.characteristicHeatingThresholdTemperature) {
this.characteristicHeatingThresholdTemperature.updateValue(value);
}
callback();
});
}
So for your target value of 25, setState
gets given 50 with your divisor of 2.
Just before the call to this line, can someone log what value actually is? Or even better a log line before setState too
this.characteristicHeatingThresholdTemperature.updateValue(value);
@dhutchison how can i do that?
Hello, I can't see from the text exactly what you need to be able to help. It may be that my english is so bad. Should the log be sent that is written to the system.log when the thermostat is switched on? I don't see any changes in the code that I could test with customizations. I had already tried to adapt code in certain places. Unfortunately unsuccessful. Good night
My guess is that when accessory changes (either through HomeKit or via just changing the temp on the thermostat), the plugin doesn't use the temperatureDivisor. So its gotta be somewhere here
if (changes.hasOwnProperty(this.dpDesiredTemperature)) {
if (characteristicHeatingThresholdTemperature.value !== changes[this.dpDesiredTemperature])
characteristicHeatingThresholdTemperature.updateValue(changes[this.dpDesiredTemperature]);
}
if (changes.hasOwnProperty(this.dpCurrentTemperature) && characteristicCurrentTemperature.value !== changes[this.dpCurrentTemperature]) characteristicCurrentTemperature.updateValue(changes[this.dpCurrentTemperature]);
console.log('[TuyaAccessory] SimpleHeater changed: ' + JSON.stringify(state));
});
}
I know nothing about coding, so its up to you to fix it.
Okay, I somehow fixed it, I just put the temperatureDivisor everywhere I could, but it works just fine now
if (changes.hasOwnProperty(this.dpDesiredTemperature)) {
if (characteristicHeatingThresholdTemperature.value !== changes[this.dpDesiredTemperature * this.temperatureDivisor])
characteristicHeatingThresholdTemperature.updateValue(changes[this.dpDesiredTemperature * this.temperatureDivisor]);
}
if (changes.hasOwnProperty(this.dpCurrentTemperature) && characteristicCurrentTemperature.value !== changes[this.dpCurrentTemperature]) characteristicCurrentTemperature.updateValue(changes[this.dpCurrentTemperature * this.temperatureDivisor]);
console.log('[TuyaAccessory] SimpleHeater changed: ' + JSON.stringify(state));
});
}
it works perfect. great
Sorry for the stupid question, but how can I implement the changes of fupzlito? I have a Rasperry Pi 4 running HOOBS. When I log into HOOBS via hoobs.local, I can only change the config file.
Thank you very much!
@marvelousmo the fix should be in the next release of the plugin. If you dont want to wait, you could replace the file homebridge-tuya/lib/SimpleHeaterAccessory.js with the fixed one from github You’ll have to look up where HOOBS keeps its plugins to do that
@fupzlito Thank you very much! So I'll wait for the next update :-)
So I made it to change the code in SimpleHeaterAccessory.js as you proposed in
if (changes.hasOwnProperty(this.dpDesiredTemperature)) { if (characteristicHeatingThresholdTemperature.value !== changes[this.dpDesiredTemperature]) characteristicHeatingThresholdTemperature.updateValue(changes[this.dpDesiredTemperature]); }
if (changes.hasOwnProperty(this.dpCurrentTemperature) && characteristicCurrentTemperature.value !== changes[this.dpCurrentTemperature]) characteristicCurrentTemperature.updateValue(changes[this.dpCurrentTemperature]); console.log('[TuyaAccessory] SimpleHeater changed: ' + JSON.stringify(state)); }); }
... but unfortunately it didn't work for me (restartet homebridge server and even the whole rasperry pi). have to wait for an updated plugin version I guess... Thanks anyway!
Hey, i got the same Problem at "Heat Convector" Can you add the "temperatureDivisor" also for that device type?
best, chris
edit: i done it by myself.
ConvectorAccessory.js
const BaseAccessory = require('./BaseAccessory');
class ConvectorAccessory extends BaseAccessory { static getCategory(Categories) { return Categories.AIR_HEATER; }
constructor(...props) {
super(...props);
}
_registerPlatformAccessory() {
const {Service} = this.hap;
this.accessory.addService(Service.HeaterCooler, this.device.context.name);
super._registerPlatformAccessory();
}
_registerCharacteristics(dps) {
const {Service, Characteristic} = this.hap;
const service = this.accessory.getService(Service.HeaterCooler);
this._checkServiceName(service, this.device.context.name);
this.dpActive = this._getCustomDP(this.device.context.dpActive) || '7';
this.dpDesiredTemperature = this._getCustomDP(this.device.context.dpDesiredTemperature) || '2';
this.dpCurrentTemperature = this._getCustomDP(this.device.context.dpCurrentTemperature) || '3';
this.dpRotationSpeed = this._getCustomDP(this.device.context.dpRotationSpeed) || '4';
this.dpChildLock = this._getCustomDP(this.device.context.dpChildLock) || '6';
this.dpTemperatureDisplayUnits = this._getCustomDP(this.device.context.dpTemperatureDisplayUnits) || '19';
this.temperatureDivisor = parseInt(this.device.context.temperatureDivisor) || 1;
this.cmdLow = 'LOW';
if (this.device.context.cmdLow) {
if (/^l[a-z]+$/i.test(this.device.context.cmdLow)) this.cmdLow = ('' + this.device.context.cmdLow).trim();
else throw new Error('The cmdLow doesn\'t appear to be valid: ' + this.device.context.cmdLow);
}
this.cmdHigh = 'HIGH';
if (this.device.context.cmdHigh) {
if (/^h[a-z]+$/i.test(this.device.context.cmdHigh)) this.cmdHigh = ('' + this.device.context.cmdHigh).trim();
else throw new Error('The cmdHigh doesn\'t appear to be valid: ' + this.device.context.cmdHigh);
}
this.enableFlipSpeedSlider = !!this.device.context.enableFlipSpeedSlider;
const characteristicActive = service.getCharacteristic(Characteristic.Active)
.updateValue(this._getActive(dps[this.dpActive]))
.on('get', this.getActive.bind(this))
.on('set', this.setActive.bind(this));
service.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
.updateValue(this._getCurrentHeaterCoolerState(dps))
.on('get', this.getCurrentHeaterCoolerState.bind(this));
service.getCharacteristic(Characteristic.TargetHeaterCoolerState)
.setProps({
minValue: 1,
maxValue: 1,
validValues: [Characteristic.TargetHeaterCoolerState.HEAT]
})
.updateValue(this._getTargetHeaterCoolerState())
.on('get', this.getTargetHeaterCoolerState.bind(this))
.on('set', this.setTargetHeaterCoolerState.bind(this));
const characteristicCurrentTemperature = service.getCharacteristic(Characteristic.CurrentTemperature)
.updateValue(this._getDividedState(dps[this.dpCurrentTemperature], this.temperatureDivisor))
.on('get', this.getDividedState.bind(this, this.dpCurrentTemperature, this.temperatureDivisor));
const characteristicHeatingThresholdTemperature = service.getCharacteristic(Characteristic.HeatingThresholdTemperature)
.setProps({
minValue: this.device.context.minTemperature || 15,
maxValue: this.device.context.maxTemperature || 35,
minStep: this.device.context.minTemperatureSteps || 1
})
.updateValue(this._getDividedState(dps[this.dpDesiredTemperature], this.temperatureDivisor))
.on('get', this.getDividedState.bind(this, this.dpDesiredTemperature, this.temperatureDivisor))
.on('set', this.setTargetThresholdTemperature.bind(this));
let characteristicTemperatureDisplayUnits;
if (!this.device.context.noTemperatureUnit) {
characteristicTemperatureDisplayUnits = service.getCharacteristic(Characteristic.TemperatureDisplayUnits)
.updateValue(this._getTemperatureDisplayUnits(dps[this.dpTemperatureDisplayUnits * this.temperatureDivisor]))
.on('get', this.getTemperatureDisplayUnits.bind(this))
.on('set', this.setTemperatureDisplayUnits.bind(this));
} else this._removeCharacteristic(service, Characteristic.TemperatureDisplayUnits);
let characteristicLockPhysicalControls;
if (!this.device.context.noChildLock) {
characteristicLockPhysicalControls = service.getCharacteristic(Characteristic.LockPhysicalControls)
.updateValue(this._getLockPhysicalControls(dps[this.dpChildLock]))
.on('get', this.getLockPhysicalControls.bind(this))
.on('set', this.setLockPhysicalControls.bind(this));
} else this._removeCharacteristic(service, Characteristic.LockPhysicalControls);
const characteristicRotationSpeed = service.getCharacteristic(Characteristic.RotationSpeed)
.updateValue(this._getRotationSpeed(dps))
.on('get', this.getRotationSpeed.bind(this))
.on('set', this.setRotationSpeed.bind(this));
this.characteristicActive = characteristicActive;
this.characteristicHeatingThresholdTemperature = characteristicHeatingThresholdTemperature;
this.characteristicRotationSpeed = characteristicRotationSpeed;
this.device.on('change', (changes, state) => {
if (changes.hasOwnProperty(this.dpActive)) {
const newActive = this._getActive(changes[this.dpActive]);
if (characteristicActive.value !== newActive) {
characteristicActive.updateValue(newActive);
if (!changes.hasOwnProperty(this.dpRotationSpeed)) {
characteristicRotationSpeed.updateValue(this._getRotationSpeed(state));
}
}
}
if (characteristicLockPhysicalControls && changes.hasOwnProperty(this.dpChildLock)) {
const newLockPhysicalControls = this._getLockPhysicalControls(changes[this.dpChildLock]);
if (characteristicLockPhysicalControls.value !== newLockPhysicalControls) {
characteristicLockPhysicalControls.updateValue(newLockPhysicalControls);
}
}
if (changes.hasOwnProperty(this.dpDesiredTemperature)) {
if (characteristicHeatingThresholdTemperature.value !== changes[this.dpDesiredTemperature])
characteristicHeatingThresholdTemperature.updateValue(changes[this.dpDesiredTemperature * this.temperatureDivisor]);
}
if (changes.hasOwnProperty(this.dpCurrentTemperature) && characteristicCurrentTemperature.value !== changes[this.dpCurrentTemperature])
characteristicCurrentTemperature.updateValue(changes[this.dpCurrentTemperature * this.temperatureDivisor]);
if (characteristicTemperatureDisplayUnits && changes.hasOwnProperty(this.dpTemperatureDisplayUnits)) {
const newTemperatureDisplayUnits = this._getTemperatureDisplayUnits(changes[this.dpTemperatureDisplayUnits * this.temperatureDivisor]);
if (characteristicTemperatureDisplayUnits.value !== newTemperatureDisplayUnits) characteristicTemperatureDisplayUnits.updateValue(newTemperatureDisplayUnits);
}
if (changes.hasOwnProperty(this.dpRotationSpeed)) {
const newRotationSpeed = this._getRotationSpeed(state);
if (characteristicRotationSpeed.value !== newRotationSpeed) characteristicRotationSpeed.updateValue(newRotationSpeed);
}
});
}
getActive(callback) {
this.getState(this.dpActive, (err, dp) => {
if (err) return callback(err);
callback(null, this._getActive(dp));
});
}
_getActive(dp) {
const {Characteristic} = this.hap;
return dp ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
}
setActive(value, callback) {
const {Characteristic} = this.hap;
switch (value) {
case Characteristic.Active.ACTIVE:
return this.setState(this.dpActive, true, callback);
case Characteristic.Active.INACTIVE:
return this.setState(this.dpActive, false, callback);
}
callback();
}
getLockPhysicalControls(callback) {
this.getState(this.dpChildLock, (err, dp) => {
if (err) return callback(err);
callback(null, this._getLockPhysicalControls(dp));
});
}
_getLockPhysicalControls(dp) {
const {Characteristic} = this.hap;
return dp ? Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED : Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED;
}
setLockPhysicalControls(value, callback) {
const {Characteristic} = this.hap;
switch (value) {
case Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED:
return this.setState(this.dpChildLock, true, callback);
case Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED:
return this.setState(this.dpChildLock, false, callback);
}
callback();
}
getCurrentHeaterCoolerState(callback) {
this.getState([this.dpActive], (err, dps) => {
if (err) return callback(err);
callback(null, this._getCurrentHeaterCoolerState(dps));
});
}
_getCurrentHeaterCoolerState(dps) {
const {Characteristic} = this.hap;
return dps[this.dpActive] ? Characteristic.CurrentHeaterCoolerState.HEATING : Characteristic.CurrentHeaterCoolerState.INACTIVE;
}
getTargetHeaterCoolerState(callback) {
callback(null, this._getTargetHeaterCoolerState());
}
_getTargetHeaterCoolerState() {
const {Characteristic} = this.hap;
return Characteristic.TargetHeaterCoolerState.HEAT;
}
setTargetHeaterCoolerState(value, callback) {
this.setState(this.dpActive, true, callback);
}
setTargetThresholdTemperature(value, callback) {
this.setState(this.dpDesiredTemperature, value * this.temperatureDivisor, err => {
if (err) return callback(err);
if (this.characteristicHeatingThresholdTemperature) {
this.characteristicHeatingThresholdTemperature.updateValue(value);
}
callback();
});
}
getTemperatureDisplayUnits(callback) {
this.getState(this.dpTemperatureDisplayUnits, (err, dp) => {
if (err) return callback(err);
callback(null, this._getTemperatureDisplayUnits(dp));
});
}
_getTemperatureDisplayUnits(dp) {
const {Characteristic} = this.hap;
return dp === 'F' ? Characteristic.TemperatureDisplayUnits.FAHRENHEIT : Characteristic.TemperatureDisplayUnits.CELSIUS;
}
setTemperatureDisplayUnits(value, callback) {
const {Characteristic} = this.hap;
this.setState(this.dpTemperatureDisplayUnits, value === Characteristic.TemperatureDisplayUnits.FAHRENHEIT ? 'F' : 'C', callback);
}
getRotationSpeed(callback) {
this.getState([this.dpActive, this.dpRotationSpeed], (err, dps) => {
if (err) return callback(err);
callback(null, this._getRotationSpeed(dps));
});
}
_getRotationSpeed(dps) {
if (!dps[this.dpActive]) return 0;
if (this._hkRotationSpeed) {
const currntRotationSpeed = this.convertRotationSpeedFromHomeKitToTuya(this._hkRotationSpeed);
return currntRotationSpeed === dps[this.dpRotationSpeed] ? this._hkRotationSpeed : this.convertRotationSpeedFromTuyaToHomeKit(dps[this.dpRotationSpeed]);
}
return this._hkRotationSpeed = this.convertRotationSpeedFromTuyaToHomeKit(dps[this.dpRotationSpeed]);
}
setRotationSpeed(value, callback) {
const {Characteristic} = this.hap;
if (value === 0) {
this.setActive(Characteristic.Active.INACTIVE, callback);
} else {
this._hkRotationSpeed = value;
const newSpeed = this.convertRotationSpeedFromHomeKitToTuya(value);
const currentSpeed = this.convertRotationSpeedFromHomeKitToTuya(this.characteristicRotationSpeed.value);
if (this.enableFlipSpeedSlider) this._hkRotationSpeed = this.convertRotationSpeedFromTuyaToHomeKit(newSpeed);
if (newSpeed !== currentSpeed) {
this.characteristicRotationSpeed.updateValue(this._hkRotationSpeed);
this.setMultiState({[this.dpActive]: true, [this.dpRotationSpeed]: newSpeed}, callback);
} else {
callback();
if (this.enableFlipSpeedSlider)
process.nextTick(() => {
this.characteristicRotationSpeed.updateValue(this._hkRotationSpeed);
});
}
}
}
convertRotationSpeedFromTuyaToHomeKit(value) {
return {[this.cmdLow]: 1, [this.cmdHigh]: 100}[value];
}
convertRotationSpeedFromHomeKitToTuya(value) {
return value < 50 ? this.cmdLow : this.cmdHigh;
}
}
module.exports = ConvectorAccessory;