Skip to content
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

[radiothermostat] Add Remote Temperature channel #10194

Merged
merged 7 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 31 additions & 22 deletions bundles/org.openhab.binding.radiothermostat/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# RadioThermostat Binding

![RadioThermostat logo](doc/index.jpg)

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.
Expand Down Expand Up @@ -45,26 +47,27 @@ The thing has a few configuration parameters:

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

Expand Down Expand Up @@ -145,6 +148,9 @@ Number:Time Therm_todaycool "Today's Cooling Runtime [%d %unit%]" { channe
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"
```
Expand All @@ -167,9 +173,12 @@ sitemap radiotherm label="My Thermostat" {
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
Expand Down Expand Up @@ -198,6 +207,6 @@ then
}
// 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
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class RadioThermostatBindingConstants {
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");
Expand All @@ -70,11 +71,12 @@ public class RadioThermostatBindingConstants {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,11 @@ public void onComplete(@Nullable Result result) {
*
* @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);
}

/**
Expand All @@ -117,12 +118,14 @@ public String sendCommand(String cmdKey, @Nullable String cmdVal) {
* @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 = "";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@
/**
* The {@link RadioThermostatDiscoveryService} is responsible for discovery of
* RadioThermostats on the local network
*
*
* @author William Welliver - Initial contribution
* @author Dan Cunningham - Refactoring and Improvements
* @author Bill Forsyth - Modified for the RadioThermostat's peculiar discovery mode
* @author Michael Lobstein - Cleanup for RadioThermostat
*
*
*/

@NonNullByDefault
Expand All @@ -81,11 +81,12 @@ protected void startBackgroundDiscovery() {
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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
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;
Expand Down Expand Up @@ -216,7 +217,7 @@ public void dispose() {
}

public void handleRawCommand(@Nullable String rawCommand) {
connector.sendCommand(null, null, rawCommand);
connector.sendCommand(null, null, rawCommand, DEFAULT_RESOURCE);
}

@Override
Expand All @@ -226,21 +227,19 @@ public void handleCommand(ChannelUID channelUID, Command command) {
} 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.
Expand All @@ -253,26 +252,26 @@ public void handleCommand(ChannelUID channelUID, Command command) {
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:
Expand All @@ -287,7 +286,16 @@ public void handleCommand(ChannelUID channelUID, Command command) {
// 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());
Expand Down Expand Up @@ -320,7 +328,10 @@ public void onNewMessageEvent(RadioThermostatEvent event) {
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:
Expand Down Expand Up @@ -382,7 +393,7 @@ private void updateChannel(String channelId, RadioThermostatDTO rthermData) {

/**
* 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
Expand Down Expand Up @@ -456,7 +467,7 @@ private void updateAllChannels() {

/**
* 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<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"/>
Expand Down Expand Up @@ -157,6 +158,14 @@
<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>
Expand Down