-
Notifications
You must be signed in to change notification settings - Fork 51
Feature/improved heating cooling #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
898d719
1ab5ce0
b998195
3897bfb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1241,18 +1241,21 @@ private pageRoomTemperature() { | |
| input "maintainRoomTemp", "enum", title: "Maintain room temperature?", required: false, multiple: false, defaultValue: 4, | ||
| options: [[1:"Cool"], [2:"Heat"], [3:"Both"], [4:"Neither"]], submitOnChange: true | ||
| if (['1', '2', '3'].contains(maintainRoomTemp)) { | ||
| if (personsPresence) | ||
| input "checkPresence", "bool", title: "Check presence before maintaining temperature?", required: true, multiple: false, defaultValue: false | ||
| else | ||
| paragraph "Check presence before maintaining temperature?\nselect presence sensor(s) to set" | ||
| input "useThermostat", "bool", title: "Use thermostat? (otherwise uses room ac and/or heater)", required: true, multiple: false, defaultValue: false, submitOnChange: true | ||
| if (useThermostat) { | ||
| //input "outTempSensor", "capability.temperatureMeasurement", title: "Which outdoor temperature sensor?", required: false, multiple: false | ||
| if (useThermostat) { | ||
| input "roomThermostat", "capability.thermostat", title: "Which thermostat?", required: true, multiple: false | ||
| input "thermoToTempSensor", "number", title: "Delta (room temperature - thermostat temperature)?", | ||
| description: "if room sensor reads 2° lower than thermostat set this to -2 and so on.", | ||
| required: false, multiple: false, defaultValue: 0, range: "-15..15" | ||
| input "thermoToTempSensor", "number", title: "Room sensor temperature - thermostat temperature = ? (typical delta)", | ||
| description: "Use to compensate for differences ", | ||
| required: true, multiple: false, defaultValue: 0, range: "-15..15" | ||
| input "thermostatOffset", "number", title: "Temporary extra thermostat offset to force HVAC activation?", | ||
| description: "Adds/subtracts to/from thermostat setting", | ||
| required: true, multiple: false, range: "0..9", defaultValue: 3 | ||
| } | ||
| if (personsPresence) | ||
| input "checkPresence", "bool", title: "Check presence before maintaining temperature?", required: true, multiple: false, defaultValue: false | ||
| else | ||
| paragraph "Check presence before maintaining temperature?\nselect presence sensor(s) to set" | ||
| input "outTempSensor", "capability.temperatureMeasurement", title: "Which outdoor temperature sensor?", required: false, multiple: false | ||
| } | ||
| if (!useThermostat && ['1', '3'].contains(maintainRoomTemp)) { | ||
| input "roomCoolSwitch", "capability.switch", title: "Which switch to turn on AC?", required: true, multiple: false, range: "32..99" | ||
|
|
@@ -2410,27 +2413,165 @@ def processCoolHeat() { | |
|
|
||
| def processCoolHeat() { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Split into three methods, setTemp() which does the actual heating/cooling, processCoolHeat for the logic and getActiveTemperatureRule() which is unchanged otherwise. |
||
| ifDebug("processCoolHeat") | ||
| def temp = -1 | ||
| def child = getChildDevice(getRoom()) | ||
| //Need to stop things even if nobody's in | ||
| def isHere = (personsPresence ? personsPresence.currentValue("presence").contains('present') : false) | ||
| if ((checkPresence && !isHere) || maintainRoomTemp == '4') { | ||
| if (checkPresence && !isHere) { | ||
| if (['1', '3'].contains(maintainRoomTemp)) | ||
| (useThermostat ? roomThermostat.off() : roomCoolSwitch.off()) | ||
| if (['2', '3'].contains(maintainRoomTemp)) | ||
| (useThermostat ? roomThermostat.off() : roomHeatSwitch.off()) | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. turning off cool/heat with non-presence is required - no? thanks.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. It still does that, just had to move it down since we don't just turn the thermostat off and do more processing.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like it only does that now when it finds a rule? if yes, that wont work. or am i misreading?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lines 2476-2479 take care of it when no rules match
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i might be missing something but why process the rules if checkpresence is on and the person is not present? thanks.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, no reason. I just wanted to get to the full "off" routine and overlooked that. Will fix, along with making the offset constant. |
||
| updateMaintainIndP(temp) | ||
| updateThermostatIndP(isHere) | ||
| return | ||
| } | ||
| def roomState = child?.currentValue('occupancy') | ||
| if (maintainRoomTemp == '4') | ||
| return | ||
|
|
||
| def temperature = getAvgTemperature() | ||
| def updateMaintainIndicator = true | ||
| def turnOn = null | ||
| def thisRule = [:] | ||
| if (state.rules) { | ||
|
|
||
| def turnOn = getActiveTemperatureRule() | ||
| ifDebug("processCoolHeat: rule: $turnOn") | ||
| if (turnOn) { | ||
| def thisRule = getRule(turnOn, 't') | ||
| def tempRange = thisRule.tempRange | ||
| def setpoint | ||
| //reduced the off commands and fan commands to prevent spamming the likes of Nest, which have an API rate limiter | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. state variables added to prevent messing with the thermostat if not needed
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why the state variable for heating active or cooling active instead of checking the current state of the thermostat to determine if heating or cooling is active? thanks.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For activating, the state variables are redundant since we quit if HVAC is running (and I should remove the checks really), but for cooling it stops us from turning off activations from other rooms and from the thermostat itself.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will make the response to this a part of the centralized model discussion above. |
||
|
|
||
| if (['1', '3'].contains(maintainRoomTemp)) { | ||
| def coolHigh = thisRule.coolTemp + tempRange | ||
| def coolLow = thisRule.coolTemp - tempRange | ||
| ifDebug ("Temperature: ${temperature} Lower limit: ${coolLow}, Upper Limit: ${coolHigh}, cooling: ${state.coolingActive}") | ||
| if (temperature >= coolHigh && (!checkPresence || (checkPresence && isHere)) && (!state.coolingActive)) | ||
| setTemp("coolon", coolLow) | ||
| else | ||
| if (temperature <= coolLow && state.coolingActive) | ||
| setTemp("cooloff") | ||
| } | ||
|
|
||
| if (['2', '3'].contains(maintainRoomTemp)) { | ||
| def heatHigh = thisRule.heatTemp + tempRange | ||
| def heatLow = thisRule.heatTemp - tempRange | ||
| ifDebug ("Temperature: ${temperature} Lower limit: ${heatLow}, Upper Limit: ${heatHigh}, heating: ${state.heatingActive}") | ||
| if (temperature >= heatHigh && state.heatingActive) | ||
| setTemp("heatoff") | ||
| else | ||
| if (temperature <= heatLow && (!checkPresence || (checkPresence && isHere)) && !(state.heatingActive)) | ||
| setTemp("heaton", heatHigh) | ||
| } | ||
|
|
||
| updateThermostatIndP(isHere) | ||
| if (updateMaintainIndicator) { | ||
| if (maintainRoomTemp == '1') | ||
| updateMaintainIndP(thisRule.coolTemp) | ||
| else if (maintainRoomTemp == '2') | ||
| updateMaintainIndP(thisRule.heatTemp) | ||
| else if (maintainRoomTemp == '3') { | ||
| def x = Math.abs(temperature - thisRule.coolTemp) | ||
| def y = Math.abs(temperature - thisRule.heatTemp) | ||
| if (x >= y) | ||
| updateMaintainIndP(thisRule.heatTemp) | ||
| else | ||
| updateMaintainIndP(thisRule.coolTemp) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| else if (state.heatingActive) | ||
| setTemp("heatoff") | ||
| else if (state.coolingActive) | ||
| setTemp("cooloff") | ||
| } | ||
|
|
||
| private setTemp(command, temp = null) | ||
| { | ||
| def setpoint | ||
| switch (command) | ||
| { | ||
| case "heaton": | ||
| if (useThermostat) { | ||
| if (thermostatActive()) | ||
| break | ||
| setpoint = temp - thermoToTempSensor + thermostatOffset | ||
| ifDebug("On - New setpoint: ${setpoint}") | ||
| roomThermostat.setHeatingSetpoint(setpoint) | ||
| //roomThermostat.fanAuto() | ||
| roomThermostat.heat() | ||
| } | ||
| else | ||
| if (roomHeatSwitch.currentValue("switch") == 'off') { | ||
| roomHeatSwitch.on() | ||
| updateMaintainIndP(roomHeatTemp) | ||
| updateMaintainIndicator = false | ||
| } | ||
| state.heatingActive = true | ||
| break | ||
|
|
||
| case "heatoff": | ||
| state.heatingActive = false | ||
| if (useThermostat) { | ||
| currentTemp = roomThermostat.currentTemperature | ||
| if (roomThermostat.currentHeatingSetpoint <= currentTemp) | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prevents changing setpoint if thermostat is manually turned off |
||
| break | ||
| roomThermostat.setHeatingSetpoint(currentTemp) | ||
| ifDebug("Off - New setpoint: $currentTemp") | ||
| } | ||
| else | ||
| roomHeatSwitch.off() | ||
| break | ||
|
|
||
| case "coolon": | ||
| ifDebug("Cooling on") | ||
| if (useThermostat) { | ||
| if (thermostatActive()) | ||
| break | ||
| setpoint = temp - thermoToTempSensor - thermostatOffset | ||
| roomThermostat.setCoolingSetpoint(setpoint) | ||
| //roomThermostat.setThermostatFanMode() | ||
| roomThermostat.cool() | ||
| } | ||
| else | ||
| if (roomCoolSwitch.currentValue("switch") == 'off') { | ||
| roomCoolSwitch.on() | ||
| updateMaintainIndP(roomCoolTemp) | ||
| updateMaintainIndicator = false | ||
| } | ||
| state.coolingActive = true | ||
| break | ||
|
|
||
| case "cooloff": | ||
| ifDebug("Cooling off") | ||
| state.coolingActive = false | ||
| if (useThermostat) { | ||
| currentTemp = roomThermostat.currentTemperature | ||
| if (roomThermostat.currentCoolingSetpoint >= currentTemp) | ||
| break | ||
| roomThermostat.setCoolingSetpoint(currentTemp) | ||
| } | ||
| else | ||
| roomCoolSwitch.off() | ||
| break | ||
| } | ||
| } | ||
|
|
||
| private thermostatActive() | ||
| { | ||
| def status = roomThermostat.currentThermostatOperatingState | ||
| ifDebug("Thermostat status: $status") | ||
| if (status == "heating" || status == "cooling") | ||
| return true | ||
| return false | ||
| } | ||
|
|
||
| def getStateValue(key) { | ||
| return state[key] | ||
| } | ||
|
|
||
| private getActiveTemperatureRule() | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No logic changes |
||
| { | ||
| if (state.rules) | ||
| { | ||
| def thisRule = [:] | ||
| def result = null | ||
| def currentMode = String.valueOf(location.currentMode) | ||
| def child = getChildDevice(getRoom()) | ||
| def roomState = child?.currentValue('occupancy') | ||
| def nowTime = now() + 1000 | ||
| def nowDate = new Date(nowTime) | ||
| def sunriseAndSunset = getSunriseAndSunset() | ||
|
|
@@ -2439,7 +2580,9 @@ def processCoolHeat() { | |
| def timedRulesOnly = false | ||
| def sunriseTimeWithOff, sunsetTimeWithOff | ||
| def i = 1 | ||
| for (; i < 11; i++) { | ||
|
|
||
| for (; i < 11; i++) | ||
| { | ||
| def ruleHasTime = false | ||
| def ruleNo = String.valueOf(i) | ||
| thisRule = getNextRule(ruleNo, 't', true) | ||
|
|
@@ -2448,23 +2591,24 @@ def processCoolHeat() { | |
| if (thisRule.mode && !thisRule.mode.contains(currentMode)) continue; | ||
| if (thisRule.state && !thisRule.state.contains(roomState)) continue; | ||
| if (thisRule.dayOfWeek && !(checkRunDay(thisRule.dayOfWeek))) continue; | ||
| // saved old time comparison while adding offset to sunrise / sunset | ||
| /* if ((thisRule.fromTimeType && (thisRule.fromTimeType != timeTime() || thisRule.fromTime)) && | ||
| (thisRule.toTimeType && (thisRule.toTimeType != timeTime() || thisRule.toTime))) { | ||
| def fTime = ( thisRule.fromTimeType == timeSunrise() ? sunriseTime : ( thisRule.fromTimeType == timeSunset() ? sunsetTime : timeToday(thisRule.fromTime, location.timeZone))) | ||
| def tTime = ( thisRule.toTimeType == timeSunrise() ? sunriseTime : ( thisRule.toTimeType == timeSunset() ? sunsetTime : timeToday(thisRule.toTime, location.timeZone))) | ||
| // ifDebug("ruleNo: $ruleNo | fTime: $fTime | tTime: $tTime | nowDate: $nowDate | timeOfDayIsBetween: ${timeOfDayIsBetween(fTime, tTime, nowDate, location.timeZone)}") | ||
| if (!(timeOfDayIsBetween(fTime, tTime, nowDate, location.timeZone))) continue; | ||
| if (!timedRulesOnly) { | ||
| turnOn = null | ||
| timedRulesOnly = true | ||
| i = 0 | ||
| continue | ||
| } | ||
| ruleHasTime = true | ||
| // saved old time comparison while adding offset to sunrise / sunset | ||
| /* if ((thisRule.fromTimeType && (thisRule.fromTimeType != timeTime() || thisRule.fromTime)) && | ||
| (thisRule.toTimeType && (thisRule.toTimeType != timeTime() || thisRule.toTime))) { | ||
| def fTime = ( thisRule.fromTimeType == timeSunrise() ? sunriseTime : ( thisRule.fromTimeType == timeSunset() ? sunsetTime : timeToday(thisRule.fromTime, location.timeZone))) | ||
| def tTime = ( thisRule.toTimeType == timeSunrise() ? sunriseTime : ( thisRule.toTimeType == timeSunset() ? sunsetTime : timeToday(thisRule.toTime, location.timeZone))) | ||
| // ifDebug("ruleNo: $ruleNo | fTime: $fTime | tTime: $tTime | nowDate: $nowDate | timeOfDayIsBetween: ${timeOfDayIsBetween(fTime, tTime, nowDate, location.timeZone)}") | ||
| if (!(timeOfDayIsBetween(fTime, tTime, nowDate, location.timeZone))) continue; | ||
| if (!timedRulesOnly) { | ||
| turnOn = null | ||
| timedRulesOnly = true | ||
| i = 0 | ||
| continue | ||
| } | ||
| ruleHasTime = true | ||
| }*/ | ||
| if ((thisRule.fromTimeType && (thisRule.fromTimeType != timeTime() || thisRule.fromTime)) && | ||
| (thisRule.toTimeType && (thisRule.toTimeType != timeTime() || thisRule.toTime))) { | ||
| (thisRule.toTimeType && (thisRule.toTimeType != timeTime() || thisRule.toTime))) | ||
| { | ||
| if (thisRule.fromTimeType == timeSunrise()) | ||
| sunriseTimeWithOff = (thisRule.fromTimeOffset ? new Date(sunriseTime.getTime() + (thisRule.fromTimeOffset * 60000L)) : sunriseTime) | ||
| else if (thisRule.fromTimeType == timeSunset()) | ||
|
|
@@ -2475,83 +2619,24 @@ def processCoolHeat() { | |
| else if (thisRule.toTimeType == timeSunset()) | ||
| sunsetTimeWithOff = (thisRule.toTimeOffset ? new Date(sunsetTime.getTime() + (thisRule.toTimeOffset * 60000L)) : sunsetTime) | ||
| def tTime = ( thisRule.toTimeType == timeSunrise() ? sunriseTimeWithOff : ( thisRule.toTimeType == timeSunset() ? sunsetTimeWithOff : timeToday(thisRule.toTime, location.timeZone))) | ||
| // ifDebug("ruleNo: $ruleNo | fTime: $fTime | tTime: $tTime | nowDate: $nowDate | timeOfDayIsBetween: ${timeOfDayIsBetween(fTime, tTime, nowDate, location.timeZone)}") | ||
| // ifDebug("ruleNo: $ruleNo | fTime: $fTime | tTime: $tTime | nowDate: $nowDate | timeOfDayIsBetween: ${timeOfDayIsBetween(fTime, tTime, nowDate, location.timeZone)}") | ||
| if (!(timeOfDayIsBetween(fTime, tTime, nowDate, location.timeZone))) continue; | ||
| if (!timedRulesOnly) { | ||
| turnOn = null | ||
| result = null | ||
| timedRulesOnly = true | ||
| i = 0 | ||
| continue | ||
| } | ||
| ruleHasTime = true | ||
| } | ||
| // ifDebug("ruleNo: $thisRule.ruleNo | thisRule.luxThreshold: $thisRule.luxThreshold | turnOn: $turnOn | previousRuleLux: $previousRuleLux") | ||
| // ifDebug("timedRulesOnly: $timedRulesOnly | ruleHasTime: $ruleHasTime") | ||
| ifDebug("${i} ${thisRule.ruleNo}") | ||
| // ifDebug("ruleNo: $thisRule.ruleNo | thisRule.luxThreshold: $thisRule.luxThreshold | turnOn: $turnOn | previousRuleLux: $previousRuleLux") | ||
| // ifDebug("timedRulesOnly: $timedRulesOnly | ruleHasTime: $ruleHasTime") | ||
| if (timedRulesOnly && !ruleHasTime) continue; | ||
| turnOn = thisRule.ruleNo | ||
| } | ||
| } | ||
| ifDebug("processCoolHeat: rule: $turnOn") | ||
| if (turnOn) { | ||
| thisRule = getRule(turnOn, 't') | ||
| def tempRange = thisRule.tempRange | ||
| if (['1', '3'].contains(maintainRoomTemp)) { | ||
| def coolHigh = thisRule.coolTemp + (tempRange / 2f).round(1) | ||
| def coolLow = thisRule.coolTemp - (tempRange / 2f).round(1) | ||
| if (temperature >= coolHigh) { | ||
| if (useThermostat) { | ||
| roomThermostat.setCoolingSetpoint(thisRule.coolTemp - thermoToTempSensor) | ||
| roomThermostat.setThermostatFanMode() | ||
| roomThermostat.cool() | ||
| } | ||
| else { | ||
| if (roomCoolSwitch.currentValue("switch") == 'off') { | ||
| roomCoolSwitch.on() | ||
| updateMaintainIndP(roomCoolTemp) | ||
| updateMaintainIndicator = false | ||
| } | ||
| } | ||
| } | ||
| else if (temperature <= coolLow) | ||
| (useThermostat ? roomThermostat.off() : roomCoolSwitch.off()) | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Explicitly not turning thermostats off to prevent unintentional freezes
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. saw your notes up top on turning off thermostat being possibly catastrophic. could you expand on that? thanks.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we turn the thermostat off, and ST gets disconnected or something else happens, the thermostat could stay off - if unnoticed that could lead to pipes freezing etc. Turning off via lowering the setpoint instead is safe and works well.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahh. you can probably tell i live in california 😄
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. haha! Ahh, I wish I could say the same... |
||
| } | ||
| if (['2', '3'].contains(maintainRoomTemp)) { | ||
| def heatHigh = thisRule.heatTemp + (tempRange / 2f).round(1) | ||
| def heatLow = thisRule.heatTemp - (tempRange / 2f).round(1) | ||
| if (temperature >= heatHigh) | ||
| (useThermostat ? roomThermostat.off() : roomHeatSwitch.off()) | ||
| else { | ||
| if (temperature <= heatLow) { | ||
| if (useThermostat) { | ||
| roomThermostat.setHeatingSetpoint(thisRule.heatTemp - thermoToTempSensor) | ||
| roomThermostat.fanAuto() | ||
| roomThermostat.heat() | ||
| } | ||
| else { | ||
| if (roomHeatSwitch.currentValue("switch") == 'off') { | ||
| roomHeatSwitch.on() | ||
| updateMaintainIndP(roomHeatTemp) | ||
| updateMaintainIndicator = false | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| updateThermostatIndP(isHere) | ||
| if (updateMaintainIndicator) { | ||
| if (maintainRoomTemp == '1') | ||
| updateMaintainIndP(thisRule.coolTemp) | ||
| else if (maintainRoomTemp == '2') | ||
| updateMaintainIndP(thisRule.heatTemp) | ||
| else if (maintainRoomTemp == '3') { | ||
| def x = Math.abs(temperature - thisRule.coolTemp) | ||
| def y = Math.abs(temperature - thisRule.heatTemp) | ||
| if (x >= y) | ||
| updateMaintainIndP(thisRule.heatTemp) | ||
| else | ||
| updateMaintainIndP(thisRule.coolTemp) | ||
| } | ||
| ifDebug("${i} ${thisRule.ruleNo}") | ||
| result = thisRule.ruleNo | ||
| } | ||
| return result | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just housekeeping and thermostatOffset, which is the "ensure trigger" offset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trying to understand thermostatOffset since i dont use a central thermostat. does setting the thermostat to a lower or higher value then issuing a thermostat.cool() or thermostat.heat() not turn on the thermostat?
thanks.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To simplify, let's assume all sensors read the same at the same actual temperature or it's not possible to discern a delta as they seem to be all over the place, that's a separate concern. Let's also assume we want the area around the thermostat and the room at the same temperature, 70F. Finally, the overall assumption is, when we are heating this is the minimum temperature we want, we don't mind being a little warmer but certainly not colder.
Here's the scenario: The thermostat is at 71F and not heating but the room is at 68F. We want to heat the room +2F. With no sensor offset and no thermostat offset, we would make the setpoint 71F (with a swing of +-1), but that would do nothing.
With a thermostat offset of, say, 4F, we would make it 75F instead which would ensure triggering the thermostat. When the room reaches 71F, we turn off the thermostat by setting it to the current thermostat temperature (which would presumably be ~73F but could be whatever) unless it's already off (manually etc)
BTW, I just realized we turn off at the swing temperature (that's how most thermostats behave, although not all) but setting the thermostat to desired temperature, which is inconsistent. Committing a fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. do we really need the thermostat offset then? more settings usually tend to confuse users. why dont we use a static offset of 5 degrees and update the text on that page to note that.
also, we keep talking about this as a central thermostat but the handling seems to be on a per room basis. heres an example:
room A maintain: 68F room sensor: 65F temperature offset: 2F
room B maintain: 68F room sensor: 72F temperature offset: 5F
room C maintain: 68F room sensor: 70F temperature offset: 3F
this will keep toggling the thermostat ... that wont be good.
thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, it doesn't really make sense on a per room basis. Sure, hard coded 5F sounds fine, since we don't really have a suitable spot for it in the parent app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Busy with moving :) The plan sounds good. I'll probably get started on Sunday. I may submit a couple of other PRs first actually (adjacency & auto level) they are already running on my system but gotta clean up the commits...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh ... that can be consuming but hopefully not stressful. :-)
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guessing you are still tied up with the after move effects? :-) do you want me to get the thermostat related changes started? i also have an user asking for fan support for some time now so want to get that done as well for him. but was holding off till the thermostat changes were final.
sorry this is not meant to bug you.
thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. let me start with #1 for thermostat.
one thing i am debating about this is whether i should transparently override user intent ... generally i am not for doing that. instead of finding all the rules and averaging them out would it better if and when they add the same thermostat more than one time to different rooms warn them that
this may have undesirable effect ... blah ... blah ...and not transparently override it in the background.any thoughts on this?
thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A notification is needed but it can be subtle: "Shared thermostat, temperature averaging enabled"
They can figure it out from there.