# RadioThermostat Binding
+
+
This binding connects RadioThermostat/3M Filtrete models CT30, CT50/3M50, CT80, etc. with built-in Wi-Fi module to openHAB.
The binding retrieves and periodically updates all basic system information from the thermostat.
The thermostat information that is retrieved is available as these channels:
-| Channel ID | Item Type | Description |
-|------------------------|----------------------|---------------------------------------------------------------------------|
-| temperature | Number:Temperature | The current temperature reading of the thermostat |
-| humidity | Number:Dimensionless | The current humidity reading of the thermostat (CT80 only) |
-| mode | Number | The current operating mode of the HVAC system |
-| fan_mode | Number | The current operating mode of the fan |
-| program_mode | Number | The program schedule that the thermostat is running (CT80 Rev B only) |
-| set_point | Number:Temperature | The current temperature set point of the thermostat |
-| status | Number | Indicates the current running status of the HVAC system |
-| fan_status | Number | Indicates the current fan status of the HVAC system |
-| override | Number | Indicates if the normal program set-point has been manually overridden |
-| hold | Switch | Indicates if the current set point temperature is to be held indefinitely |
-| day | Number | The current day of the week reported by the thermostat (0 = Monday) |
-| hour | Number | The current hour of the day reported by the thermostat (24 hr) |
-| minute | Number | The current minute past the hour reported by the thermostat |
-| dt_stamp | String | The current day of the week and time reported by the thermostat (E HH:mm) |
-| today_heat_runtime | Number:Time | The total number of minutes of heating run-time today |
-| today_cool_runtime | Number:Time | The total number of minutes of cooling run-time today |
-| yesterday_heat_runtime | Number:Time | The total number of minutes of heating run-time yesterday |
-| yesterday_cool_runtime | Number:Time | The total number of minutes of cooling run-time yesterday |
+| Channel ID | Item Type | Description |
+|------------------------|----------------------|------------------------------------------------------------------------------------------------------------------------------------|
+| temperature | Number:Temperature | The current temperature reading of the thermostat |
+| humidity | Number:Dimensionless | The current humidity reading of the thermostat (CT80 only) |
+| mode | Number | The current operating mode of the HVAC system |
+| fan_mode | Number | The current operating mode of the fan |
+| program_mode | Number | The program schedule that the thermostat is running (CT80 Rev B only) |
+| set_point | Number:Temperature | The current temperature set point of the thermostat |
+| status | Number | Indicates the current running status of the HVAC system |
+| fan_status | Number | Indicates the current fan status of the HVAC system |
+| override | Number | Indicates if the normal program set-point has been manually overridden |
+| hold | Switch | Indicates if the current set point temperature is to be held indefinitely |
+| remote_temp | Number:Temperature | Override the internal temperature as read by the thermostat's temperature sensor; Set to -1 to return to internal temperature mode |
+| day | Number | The current day of the week reported by the thermostat (0 = Monday) |
+| hour | Number | The current hour of the day reported by the thermostat (24 hr) |
+| minute | Number | The current minute past the hour reported by the thermostat |
+| dt_stamp | String | The current day of the week and time reported by the thermostat (E HH:mm) |
+| today_heat_runtime | Number:Time | The total number of minutes of heating run-time today |
+| today_cool_runtime | Number:Time | The total number of minutes of cooling run-time today |
+| yesterday_heat_runtime | Number:Time | The total number of minutes of heating run-time yesterday |
+| yesterday_cool_runtime | Number:Time | The total number of minutes of cooling run-time yesterday |
## Full Example
Number:Time Therm_yesterdayheat "Yesterday's Heating Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:yesterday_heat_runtime" }
Number:Time Therm_yesterdaycool "Yesterday's Cooling Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:yesterday_cool_runtime" }
+// Override the thermostat's temperature reading with a value from an external sensor, set to -1 to revert to internal temperature mode
+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"
```
Text item=Therm_Override icon="smoke"
Switch item=Therm_Hold icon="smoke"
+ // 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"]
+
// Virtual switch/button to trigger a rule to send a custom command
// The ON value displays in the button
- Switch item=Therm_mysetting mappings=[ON="Heat, 58, hold"]
+ Switch item=Therm_mysetting mappings=[ON="Heat, 68, hold"]
Text item=Therm_Day
Text item=Therm_Hour
}
// JSON to send directly to the thermostat's '/tstat' endpoint
// See RadioThermostat_CT50_Honeywell_Wifi_API_V1.3.pdf for more detail
- actions.sendRawCommand('{"hold":1, "t_heat":' + "58" + ', "tmode":1}')
+ actions.sendRawCommand('{"hold":1, "t_heat":' + "68" + ', "tmode":1}')
end
```
public static final String DEFAULT_RESOURCE = "tstat";
public static final String RUNTIME_RESOURCE = "tstat/datalog";
public static final String HUMIDITY_RESOURCE = "tstat/humidity";
+ public static final String REMOTE_TEMP_RESOURCE = "tstat/remote_temp";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_RTHERM = new ThingTypeUID(BINDING_ID, "rtherm");
public static final String TODAY_COOL_RUNTIME = "today_cool_runtime";
public static final String YESTERDAY_HEAT_RUNTIME = "yesterday_heat_runtime";
public static final String YESTERDAY_COOL_RUNTIME = "yesterday_cool_runtime";
+ public static final String REMOTE_TEMP = "remote_temp";
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_RTHERM);
public static final Set<String> SUPPORTED_CHANNEL_IDS = Stream.of(TEMPERATURE, HUMIDITY, MODE, FAN_MODE,
PROGRAM_MODE, SET_POINT, OVERRIDE, HOLD, STATUS, FAN_STATUS, DAY, HOUR, MINUTE, DATE_STAMP,
- TODAY_HEAT_RUNTIME, TODAY_COOL_RUNTIME, YESTERDAY_HEAT_RUNTIME, YESTERDAY_COOL_RUNTIME)
+ TODAY_HEAT_RUNTIME, TODAY_COOL_RUNTIME, YESTERDAY_HEAT_RUNTIME, YESTERDAY_COOL_RUNTIME, REMOTE_TEMP)
.collect(Collectors.toSet());
// Units of measurement of the data delivered by the API
*
* @param the JSON attribute key for the value to be updated
* @param the value to be updated in the thermostat
+ * @param the end point URI to use for the command
* @return the JSON response string from the thermostat
*/
- public String sendCommand(String cmdKey, @Nullable String cmdVal) {
- return sendCommand(cmdKey, cmdVal, null);
+ public String sendCommand(String cmdKey, @Nullable String cmdVal, String resource) {
+ return sendCommand(cmdKey, cmdVal, null, resource);
}
/**
* @param the JSON attribute key for the value to be updated
* @param the value to be updated in the thermostat
* @param JSON string to send directly to the thermostat instead of a key/value pair
+ * @param the end point URI to use for the command
* @return the JSON response string from the thermostat
*/
- public String sendCommand(@Nullable String cmdKey, @Nullable String cmdVal, @Nullable String cmdJson) {
+ public String sendCommand(@Nullable String cmdKey, @Nullable String cmdVal, @Nullable String cmdJson,
+ String resource) {
// if we got a cmdJson string send that, otherwise build the json from the key and val params
String postJson = cmdJson != null ? cmdJson : "{\"" + cmdKey + "\":" + cmdVal + "}";
- String urlStr = buildRequestURL(DEFAULT_RESOURCE);
+ String urlStr = buildRequestURL(resource);
String output = "";
TimeUnit.SECONDS);
}
- @SuppressWarnings("null")
@Override
protected void stopBackgroundDiscovery() {
- if (scheduledFuture != null && !scheduledFuture.isCancelled()) {
+ ScheduledFuture<?> scheduledFuture = this.scheduledFuture;
+ if (scheduledFuture != null) {
scheduledFuture.cancel(true);
+ this.scheduledFuture = null;
}
}
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
}
public void handleRawCommand(@Nullable String rawCommand) {
- connector.sendCommand(null, null, rawCommand);
+ connector.sendCommand(null, null, rawCommand, DEFAULT_RESOURCE);
}
@Override
} else {
Integer cmdInt = -1;
String cmdStr = command.toString();
- if (cmdStr != null) {
- try {
- // parse out an Integer from the string
- // ie '70.5 F' becomes 70, also handles negative numbers
- cmdInt = NumberFormat.getInstance().parse(cmdStr).intValue();
- } catch (ParseException e) {
- logger.debug("Command: {} -> Not an integer", cmdStr);
- }
+ try {
+ // parse out an Integer from the string
+ // ie '70.5 F' becomes 70, also handles negative numbers
+ cmdInt = NumberFormat.getInstance().parse(cmdStr).intValue();
+ } catch (ParseException e) {
+ logger.debug("Command: {} -> Not an integer", cmdStr);
}
switch (channelUID.getId()) {
case MODE:
// only do if commanded mode is different than current mode
if (!cmdInt.equals(rthermData.getThermostatData().getMode())) {
- connector.sendCommand("tmode", cmdStr);
+ connector.sendCommand("tmode", cmdStr, DEFAULT_RESOURCE);
// set the new operating mode, reset everything else,
// because refreshing the tstat data below is really slow.
rthermData.getThermostatData().setProgramMode(-1);
updateChannel(PROGRAM_MODE, rthermData);
- // now just trigger a refresh of the thermost to get the new active setpoint
+ // now just trigger a refresh of the thermostat to get the new active setpoint
// this takes a while for the JSON request to complete (async).
connector.getAsyncThermostatData(DEFAULT_RESOURCE);
}
break;
case FAN_MODE:
rthermData.getThermostatData().setFanMode(cmdInt);
- connector.sendCommand("fmode", cmdStr);
+ connector.sendCommand("fmode", cmdStr, DEFAULT_RESOURCE);
break;
case PROGRAM_MODE:
rthermData.getThermostatData().setProgramMode(cmdInt);
- connector.sendCommand("program_mode", cmdStr);
+ connector.sendCommand("program_mode", cmdStr, DEFAULT_RESOURCE);
break;
case HOLD:
if (command instanceof OnOffType && command == OnOffType.ON) {
rthermData.getThermostatData().setHold(1);
- connector.sendCommand("hold", "1");
+ connector.sendCommand("hold", "1", DEFAULT_RESOURCE);
} else if (command instanceof OnOffType && command == OnOffType.OFF) {
rthermData.getThermostatData().setHold(0);
- connector.sendCommand("hold", "0");
+ connector.sendCommand("hold", "0", DEFAULT_RESOURCE);
}
break;
case SET_POINT:
// don't do anything if we are not in heat or cool mode
break;
}
- connector.sendCommand(cmdKey, cmdInt.toString());
+ connector.sendCommand(cmdKey, cmdInt.toString(), DEFAULT_RESOURCE);
+ break;
+ case REMOTE_TEMP:
+ if (cmdInt != -1) {
+ QuantityType<?> remoteTemp = ((QuantityType<Temperature>) command)
+ .toUnit(ImperialUnits.FAHRENHEIT);
+ connector.sendCommand("rem_temp", String.valueOf(remoteTemp.intValue()), REMOTE_TEMP_RESOURCE);
+ } else {
+ connector.sendCommand("rem_mode", "0", REMOTE_TEMP_RESOURCE);
+ }
break;
default:
logger.warn("Unsupported command: {}", command.toString());
updateAllChannels();
break;
case HUMIDITY_RESOURCE:
- rthermData.setHumidity(gson.fromJson(evtVal, RadioThermostatHumidityDTO.class).getHumidity());
+ RadioThermostatHumidityDTO dto = gson.fromJson(evtVal, RadioThermostatHumidityDTO.class);
+ if (dto != null) {
+ rthermData.setHumidity(dto.getHumidity());
+ }
updateChannel(HUMIDITY, rthermData);
break;
case RUNTIME_RESOURCE:
/**
* Update a given channelId from the thermostat data
- *
+ *
* @param the channel id to be updated
* @param data the RadioThermostat dto
* @return the value to be set in the state
/**
* Build a list of fan modes based on what model thermostat is used
- *
+ *
* @return list of state options for thermostat fan modes
*/
private List<StateOption> getFanModeOptions() {
<channel id="hold" typeId="hold"/>
<channel id="status" typeId="status"/>
<channel id="fan_status" typeId="fan_status"/>
+ <channel id="remote_temp" typeId="remote_temp"/>
<channel id="day" typeId="t_day"/>
<channel id="hour" typeId="t_hour"/>
<channel id="minute" typeId="t_minute"/>
<state min="0" max="2" pattern="%d"/>
</channel-type>
+ <channel-type id="remote_temp" advanced="true">
+ <item-type>Number:Temperature</item-type>
+ <label>Remote Temperature</label>
+ <description>The remote temperature takes the place of the ambient temperature as read by the local thermostat
+ temperature sensor</description>
+ <state pattern="%d %unit%"/>
+ </channel-type>
+
<channel-type id="t_day" advanced="true">
<item-type>Number</item-type>
<label>Day</label>