mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[radiothermostat] Implement remote temperature deadband (#20207)
* Implement Remote Temperature Deadband Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
@@ -23,15 +23,16 @@ Otherwise the Thing must be manually added.
|
||||
|
||||
The Thing has a few configuration parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| hostName | The host name or IP address of the thermostat. Mandatory. |
|
||||
| refresh | Overrides the refresh interval of the thermostat data. Optional, the default is 2 minutes. |
|
||||
| logRefresh | Overrides the refresh interval of the run-time logs & humidity data. Optional, the default is 10 minutes. |
|
||||
| isCT80 | Flag to enable additional features only available on the CT80 thermostat. Optional, the default is false. |
|
||||
| disableLogs | Disable retrieval of run-time logs from the thermostat. Optional, the default is false. |
|
||||
| setpointMode | Controls temporary or absolute setpoint mode. In "temporary" mode the thermostat will temporarily maintain the given setpoint until the next scheduled setpoint time period. In "absolute" mode the thermostat will ignore its program and maintain the given setpoint indefinitely. Optional, the default is "temporary". |
|
||||
| clockSync | Flag to enable the binding to sync the internal clock on the thermostat to match the openHAB host's system clock. Sync occurs at binding startup and every hour thereafter. Optional, the default is **true**. |
|
||||
| Parameter | Description |
|
||||
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| hostName | The host name or IP address of the thermostat. Mandatory. |
|
||||
| refresh | Overrides the refresh interval of the thermostat data. Optional, the default is 2 minutes. |
|
||||
| logRefresh | Overrides the refresh interval of the run-time logs & humidity data. Optional, the default is 10 minutes. |
|
||||
| isCT80 | Flag to enable additional features only available on the CT80 thermostat. Optional, the default is **false**. |
|
||||
| disableLogs | Disable retrieval of run-time logs from the thermostat. Optional, the default is **false**. |
|
||||
| setpointMode | Controls temporary or absolute setpoint mode. In "temporary" mode the thermostat will temporarily maintain the given setpoint until the next scheduled setpoint time period. In "absolute" mode the thermostat will ignore its program and maintain the given setpoint indefinitely. Optional, the default is "temporary". |
|
||||
| clockSync | Flag to enable the binding to sync the internal clock on the thermostat to match the openHAB host's system clock. Sync occurs at binding startup and every hour thereafter. Optional, the default is **true**. |
|
||||
| remoteTempDeadband | Flag to enable rounding of the value received by the `remote_temp` channel, see notes and rule example below. Optional, the default is **false**. |
|
||||
|
||||
### Schedule Configuration
|
||||
|
||||
@@ -68,6 +69,12 @@ curl http://$THERMOSTAT_IP/cloud -d '{"authkey":""}'
|
||||
- The `override` flag is not reported correctly on older thermostat versions (i.e. /tstat/model reports v1.09)
|
||||
- The Program Mode command is untested and according to the published API is only available on a CT80 Rev B.
|
||||
- Humidity information is available only when using a CT80 thermostat.
|
||||
- The `remote_temp` channel can be used to send a temperature reading from a remote sensor to override the internal temperature reading of the thermostat in 1 degree increments.
|
||||
- An example use case for a thermostat that is located in a hallway would be to place a sensor in a bedroom to provide more accurate temperature regulation in the bedroom.
|
||||
- The temperature value from the sensor should be sent to the thermostat once per minute and with care taken to reset the thermostat to internal temperature mode in the case that the sensor stops updating.
|
||||
- If the `remoteTempDeadband` configuration parameter is set to **true** the value received by the `remote_temp` channel will be rounded up or down depending on the thermostat's current state.
|
||||
- This rounding allows the deadband/temperature swing to work properly for any remote temperature value (including those with decimal places) sent.
|
||||
- For example, if the heat setpoint is 70°F, the heat will not energize until the temperature drops to 69.0°F and should stay on until the temperature reaches 70.0°F again. The reverse is true for cooling.
|
||||
- If `remote_temp` or `message` channels are used, their values in the thermostat will be cleared during binding shutdown.
|
||||
|
||||
## Channels
|
||||
@@ -185,7 +192,7 @@ String Therm_Message "Message: [%s]"
|
||||
Number:Temperature Therm_Rtemp "Remote Temperature [%d]" <temperature> { channel="radiothermostat:rtherm:mytherm1:remote_temp" }
|
||||
|
||||
// A virtual switch used to trigger a rule to send a json command to the thermostat
|
||||
Switch Therm_mysetting "Send my preferred setting"
|
||||
Switch Therm_mysetting "Send my preferred setting"
|
||||
```
|
||||
|
||||
### `radiotherm.sitemap` Example
|
||||
@@ -208,8 +215,8 @@ sitemap radiotherm label="My Thermostat" {
|
||||
Text item=Therm_NextTemp icon="temperature"
|
||||
Text item=Therm_NextTime icon="time"
|
||||
|
||||
// Example of overriding the thermostat's temperature reading
|
||||
Switch item=Therm_Rtemp label="Remote Temp" icon="temperature" mappings=[60="60", 75="75", 80="80", -1="Reset"]
|
||||
// Example of overriding the thermostat's temperature reading - TEST USE ONLY!
|
||||
// Switch item=Therm_Rtemp label="Remote Temp" icon="temperature" mappings=[60="60", 75="75", 80="80", -1="Reset"]
|
||||
|
||||
// Virtual switch/button to trigger a rule to send a custom command
|
||||
// The ON value displays in the button
|
||||
@@ -236,7 +243,7 @@ when
|
||||
Item Therm_mysetting received command
|
||||
then
|
||||
val actions = getActions("radiothermostat","radiothermostat:rtherm:mytherm1")
|
||||
if(null === actions) {
|
||||
if (null === actions) {
|
||||
logInfo("actions", "Actions not found, check thing ID")
|
||||
return
|
||||
}
|
||||
@@ -258,6 +265,20 @@ then
|
||||
|
||||
end
|
||||
|
||||
rule "Send remote temperature sensor readings to the thermostat"
|
||||
when
|
||||
// An item containing the temperature from a remote sensor
|
||||
// The item should be made to expire (e.g. expire="10m" in the item definition) in case the sensor should stop updating
|
||||
// Must set the `remoteTempDeadband` configuration parameter to true and the `refresh` configuration parameter to 1
|
||||
Item RemoteSensorTemp received update
|
||||
then
|
||||
if (RemoteSensorTemp.state != NULL && RemoteSensorTemp.state != UNDEF) {
|
||||
Therm_Rtemp.sendCommand(RemoteSensorTemp.state)
|
||||
} else {
|
||||
Therm_Rtemp.sendCommand(-1)
|
||||
}
|
||||
end
|
||||
|
||||
rule "Display outside temp in thermostat message area"
|
||||
when
|
||||
// An item containing the current outside temperature
|
||||
@@ -268,7 +289,7 @@ then
|
||||
// Sends empty string to clear the number and restore the time display if OutsideTemp is undefined
|
||||
var temp = ""
|
||||
|
||||
if (newState != null && newState != UNDEF) {
|
||||
if (newState != NULL && newState != UNDEF) {
|
||||
temp = Math.round((newState as DecimalType).doubleValue).intValue.toString
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ public class RadioThermostatConfiguration {
|
||||
public boolean disableLogs = false;
|
||||
public boolean clockSync = false;
|
||||
public String setpointMode = "temporary";
|
||||
public boolean remoteTempDeadband = false;
|
||||
|
||||
public @Nullable String monMorningHeatTime;
|
||||
public @Nullable String monDayHeatTime;
|
||||
|
||||
+38
-9
@@ -94,6 +94,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
private boolean isCT80 = false;
|
||||
private boolean disableLogs = false;
|
||||
private boolean clockSync = false;
|
||||
private boolean remoteTempDeadband = false;
|
||||
private String setpointCmdKeyPrefix = "t_";
|
||||
private String heatProgramJson = BLANK;
|
||||
private String coolProgramJson = BLANK;
|
||||
@@ -117,6 +118,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
this.isCT80 = config.isCT80;
|
||||
this.disableLogs = config.disableLogs;
|
||||
this.clockSync = config.clockSync;
|
||||
this.remoteTempDeadband = config.remoteTempDeadband;
|
||||
|
||||
if (hostName == null || hostName.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
@@ -339,7 +341,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
updateChannel(channelUID.getId(), rthermData);
|
||||
} else {
|
||||
Integer cmdInt = -1;
|
||||
String cmdStr = command.toString();
|
||||
final String cmdStr = command.toString();
|
||||
try {
|
||||
// parse out an Integer from the string
|
||||
// ie '70.5 F' becomes 70, also handles negative numbers
|
||||
@@ -392,7 +394,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
updateChannel(NEXT_TIME, rthermData);
|
||||
break;
|
||||
case SET_POINT:
|
||||
String cmdKey;
|
||||
final String cmdKey;
|
||||
if (rthermData.getThermostatData().getMode() == 1) {
|
||||
cmdKey = this.setpointCmdKeyPrefix + "heat";
|
||||
rthermData.getThermostatData().setHeatTarget(Double.valueOf(cmdInt));
|
||||
@@ -409,10 +411,10 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
break;
|
||||
case REMOTE_TEMP:
|
||||
if (cmdInt != -1) {
|
||||
QuantityType<?> remoteTemp = ((QuantityType<Temperature>) command)
|
||||
final QuantityType<Temperature> remoteTemp = ((QuantityType<Temperature>) command)
|
||||
.toUnit(ImperialUnits.FAHRENHEIT);
|
||||
if (remoteTemp != null) {
|
||||
connector.sendCommand("rem_temp", String.valueOf(remoteTemp.intValue()),
|
||||
connector.sendCommand("rem_temp", handleRemoteTempDeadband(remoteTemp),
|
||||
REMOTE_TEMP_RESOURCE);
|
||||
}
|
||||
} else {
|
||||
@@ -441,8 +443,8 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
public void onNewMessageEvent(RadioThermostatEvent event) {
|
||||
logger.debug("onNewMessageEvent: key {} = {}", event.getKey(), event.getValue());
|
||||
|
||||
String evtKey = event.getKey();
|
||||
String evtVal = event.getValue();
|
||||
final String evtKey = event.getKey();
|
||||
final String evtVal = event.getValue();
|
||||
|
||||
if (KEY_ERROR.equals(evtKey)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
@@ -460,7 +462,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
}
|
||||
break;
|
||||
case HUMIDITY_RESOURCE:
|
||||
RadioThermostatHumidityDTO dto = gson.fromJson(evtVal, RadioThermostatHumidityDTO.class);
|
||||
final RadioThermostatHumidityDTO dto = gson.fromJson(evtVal, RadioThermostatHumidityDTO.class);
|
||||
// if thermostat returned -1 for humidity, skip this update
|
||||
if (dto != null && dto.getHumidity() >= 0) {
|
||||
rthermData.setHumidity(dto.getHumidity());
|
||||
@@ -485,7 +487,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
*/
|
||||
private void updateChannel(String channelId, RadioThermostatDTO rthermData) {
|
||||
if (isLinked(channelId)) {
|
||||
Object value;
|
||||
final Object value;
|
||||
try {
|
||||
value = getValue(channelId, rthermData, thermostatSchedule);
|
||||
} catch (Exception e) {
|
||||
@@ -625,7 +627,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
* @return list of state options for thermostat fan modes
|
||||
*/
|
||||
private List<StateOption> getFanModeOptions() {
|
||||
List<StateOption> fanModeOptions = new ArrayList<>();
|
||||
final List<StateOption> fanModeOptions = new ArrayList<>(this.isCT80 ? 3 : 2);
|
||||
|
||||
fanModeOptions.add(new StateOption("0", "@text/options.fan-option-auto"));
|
||||
if (this.isCT80) {
|
||||
@@ -635,4 +637,31 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
|
||||
return fanModeOptions;
|
||||
}
|
||||
|
||||
private String handleRemoteTempDeadband(QuantityType<Temperature> remoteTemp) {
|
||||
if (!this.remoteTempDeadband) {
|
||||
return String.valueOf(remoteTemp.intValue());
|
||||
}
|
||||
|
||||
final int remoteTempRounded;
|
||||
final boolean isHeatModeOff = Integer.valueOf(1).equals(rthermData.getThermostatData().getMode())
|
||||
&& Integer.valueOf(0).equals(rthermData.getThermostatData().getStatus());
|
||||
final boolean isCoolModeOff = Integer.valueOf(2).equals(rthermData.getThermostatData().getMode())
|
||||
&& Integer.valueOf(0).equals(rthermData.getThermostatData().getStatus());
|
||||
|
||||
// hvac heating on OR hvac off in cooling mode, round temperature down, e.g. 69.5 = 69
|
||||
if (Integer.valueOf(1).equals(rthermData.getThermostatData().getStatus()) || isCoolModeOff) {
|
||||
remoteTempRounded = (int) Math.floor(remoteTemp.doubleValue());
|
||||
logger.debug("remote temp: {}, rounding down to {}", remoteTemp, remoteTempRounded);
|
||||
} else if (Integer.valueOf(2).equals(rthermData.getThermostatData().getStatus()) || isHeatModeOff) {
|
||||
// hvac cooling on OR hvac off in heat mode, round temperature up, e.g. 69.1 = 70
|
||||
remoteTempRounded = (int) Math.ceil(remoteTemp.doubleValue());
|
||||
logger.debug("remote temp: {}, rounding up to {}", remoteTemp, remoteTempRounded);
|
||||
} else {
|
||||
// hvac mode off (or unknown), round temperature, e.g. 69.5 = 70
|
||||
remoteTempRounded = (int) Math.round(remoteTemp.doubleValue());
|
||||
logger.debug("remote temp: {}, rounding to {}", remoteTemp, remoteTempRounded);
|
||||
}
|
||||
return String.valueOf(remoteTempRounded);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -51,10 +51,16 @@
|
||||
</parameter>
|
||||
<parameter name="clockSync" type="boolean">
|
||||
<label>Enable Thermostat Clock Sync</label>
|
||||
<description>Optional Flag to sync the thermostat's clock with the host system clock</description>
|
||||
<description>Optional flag to sync the thermostat's clock with the host system clock</description>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="remoteTempDeadband" type="boolean">
|
||||
<label>Enable Remote Temperature Deadband</label>
|
||||
<description>Optional flag to round the value received by the Remote Temperature channel</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="monMorningHeatTime" type="text" pattern="([01]?[0-9]|2[0-3]):[0-5][0-9]"
|
||||
groupName="heating">
|
||||
<label>Monday Morning</label>
|
||||
|
||||
+3
-1
@@ -11,7 +11,7 @@ thing-type.radiothermostat.rtherm.description = CT30, CT50/3M50 or CT80 Wi-Fi Th
|
||||
# thing types config
|
||||
|
||||
thing-type.config.radiothermostat.thermostatconfig.clockSync.label = Enable Thermostat Clock Sync
|
||||
thing-type.config.radiothermostat.thermostatconfig.clockSync.description = Optional Flag to sync the thermostat's clock with the host system clock
|
||||
thing-type.config.radiothermostat.thermostatconfig.clockSync.description = Optional flag to sync the thermostat's clock with the host system clock
|
||||
thing-type.config.radiothermostat.thermostatconfig.disableLogs.label = Disable Retrieval of Run-time Data
|
||||
thing-type.config.radiothermostat.thermostatconfig.disableLogs.description = Optional Flag to Disable the Retrieval of Run-time Data from the Thermostat
|
||||
thing-type.config.radiothermostat.thermostatconfig.friDayCoolTemp.label = Friday Day Cool Setpoint
|
||||
@@ -90,6 +90,8 @@ thing-type.config.radiothermostat.thermostatconfig.monNightHeatTime.label = Mond
|
||||
thing-type.config.radiothermostat.thermostatconfig.monNightHeatTime.description = Monday Night setpoint start time (HH:mm)
|
||||
thing-type.config.radiothermostat.thermostatconfig.refresh.label = Refresh Interval
|
||||
thing-type.config.radiothermostat.thermostatconfig.refresh.description = Specifies the Refresh Interval in Minutes
|
||||
thing-type.config.radiothermostat.thermostatconfig.remoteTempDeadband.label = Enable Remote Temperature Deadband
|
||||
thing-type.config.radiothermostat.thermostatconfig.remoteTempDeadband.description = Optional flag to round the value received by the Remote Temperature channel
|
||||
thing-type.config.radiothermostat.thermostatconfig.satDayCoolTemp.label = Saturday Day Cool Setpoint
|
||||
thing-type.config.radiothermostat.thermostatconfig.satDayCoolTemp.description = The setpoint in Degrees Fahrenheit
|
||||
thing-type.config.radiothermostat.thermostatconfig.satDayCoolTime.label = Saturday Day
|
||||
|
||||
Reference in New Issue
Block a user