diff --git a/bundles/org.openhab.binding.radiothermostat/README.md b/bundles/org.openhab.binding.radiothermostat/README.md index 74987466aae42..e5244c0de7aca 100644 --- a/bundles/org.openhab.binding.radiothermostat/README.md +++ b/bundles/org.openhab.binding.radiothermostat/README.md @@ -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. @@ -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 @@ -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]" { 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" ``` @@ -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 @@ -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 ``` diff --git a/bundles/org.openhab.binding.radiothermostat/doc/index.jpg b/bundles/org.openhab.binding.radiothermostat/doc/index.jpg new file mode 100644 index 0000000000000..963f5295621ef Binary files /dev/null and b/bundles/org.openhab.binding.radiothermostat/doc/index.jpg differ diff --git a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatBindingConstants.java b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatBindingConstants.java index 64bed1aed37bb..daf02d99e749f 100644 --- a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatBindingConstants.java +++ b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/RadioThermostatBindingConstants.java @@ -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"); @@ -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 SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_RTHERM); public static final Set 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 diff --git a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/communication/RadioThermostatConnector.java b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/communication/RadioThermostatConnector.java index fb25804f435fc..78646d41416b3 100644 --- a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/communication/RadioThermostatConnector.java +++ b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/communication/RadioThermostatConnector.java @@ -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); } /** @@ -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 = ""; diff --git a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/discovery/RadioThermostatDiscoveryService.java b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/discovery/RadioThermostatDiscoveryService.java index 4c276eb6f867e..250f9251cea1c 100644 --- a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/discovery/RadioThermostatDiscoveryService.java +++ b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/discovery/RadioThermostatDiscoveryService.java @@ -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; } } diff --git a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/handler/RadioThermostatHandler.java b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/handler/RadioThermostatHandler.java index b9b2bbaee80c3..6ab451829938d 100644 --- a/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/handler/RadioThermostatHandler.java +++ b/bundles/org.openhab.binding.radiothermostat/src/main/java/org/openhab/binding/radiothermostat/internal/handler/RadioThermostatHandler.java @@ -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; @@ -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 @@ -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. @@ -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: @@ -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) 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()); @@ -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: @@ -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 @@ -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 getFanModeOptions() { diff --git a/bundles/org.openhab.binding.radiothermostat/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.radiothermostat/src/main/resources/OH-INF/thing/thing-types.xml index 557d36c560bbe..a33622dbdc557 100644 --- a/bundles/org.openhab.binding.radiothermostat/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.radiothermostat/src/main/resources/OH-INF/thing/thing-types.xml @@ -22,6 +22,7 @@ + @@ -157,6 +158,14 @@ + + Number:Temperature + + The remote temperature takes the place of the ambient temperature as read by the local thermostat + temperature sensor + + + Number