mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[radiothermostat] Add next scheduled set point channels (#17743)
Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
parent
772026e25e
commit
c4bce9a768
@ -1,6 +1,6 @@
|
||||
# RadioThermostat Binding
|
||||
# Radio Thermostat Binding
|
||||
|
||||
This binding connects RadioThermostat/3M Filtrete models CT30, CT50/3M50, CT80, etc. with built-in Wi-Fi module to openHAB.
|
||||
This binding connects Radio Thermostat/3M Filtrete models CT30, CT50/3M50, CT80, etc. with built-in Wi-Fi module to openHAB.
|
||||
Thermostats using a Z-Wave module are not supported but can be used via the openHAB ZWave binding.
|
||||
|
||||
The binding retrieves and periodically updates all basic system information from the thermostat.
|
||||
@ -96,6 +96,8 @@ The thermostat information that is retrieved is available as these channels:
|
||||
| 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 |
|
||||
| message | String (Write Only) | Used to display a number in the upper left 'price message' area of the thermostat's screen where the time is normally displayed |
|
||||
| next_temp | Number:Temperature | Displays the next scheduled thermostat set point temperature in the heating or cooling schedule |
|
||||
| next_time | DateTime | Displays the next scheduled thermostat set point time in the heating or cooling schedule |
|
||||
|
||||
## Full Example
|
||||
|
||||
@ -152,33 +154,35 @@ radiothermostat:rtherm:mytherm2 "My 2nd floor thermostat" [ hostName="mythermhos
|
||||
radiotherm.items:
|
||||
|
||||
```java
|
||||
Number:Temperature Therm_Temp "Current Temperature [%.1f °F] " <temperature> { channel="radiothermostat:rtherm:mytherm1:temperature" }
|
||||
Number:Temperature Therm_Temp "Current Temperature [%.1f °F]" <temperature> { channel="radiothermostat:rtherm:mytherm1:temperature" }
|
||||
// Humidity only supported on CT80
|
||||
Number Therm_Hum "Current Humidity [%d %%]" <humidity> { channel="radiothermostat:rtherm:mytherm1:humidity" }
|
||||
Number Therm_Mode "Thermostat Mode [MAP(radiotherm.map):%s_mode]" { channel="radiothermostat:rtherm:mytherm1:mode" }
|
||||
Number Therm_Hum "Current Humidity [%d %%]" <humidity> { channel="radiothermostat:rtherm:mytherm1:humidity" }
|
||||
Number Therm_Mode "Thermostat Mode [MAP(radiotherm.map):%s_mode]" { channel="radiothermostat:rtherm:mytherm1:mode" }
|
||||
// The Auto/Circulate option will only appear for CT80
|
||||
Number Therm_Fmode "Fan Mode [MAP(radiotherm.map):%s_fan]" { channel="radiothermostat:rtherm:mytherm1:fan_mode" }
|
||||
Number Therm_Fmode "Fan Mode [MAP(radiotherm.map):%s_fan]" { channel="radiothermostat:rtherm:mytherm1:fan_mode" }
|
||||
// Program Mode only supported on CT80 Rev B
|
||||
Number Therm_Pmode "Program Mode [MAP(radiotherm.map):%s_pgm]" { channel="radiothermostat:rtherm:mytherm1:program_mode" }
|
||||
Number:Temperature Therm_Setpt "Set Point [%d]" <temperature> { channel="radiothermostat:rtherm:mytherm1:set_point" }
|
||||
Number Therm_Status "Status [MAP(radiotherm.map):%s_stus]" { channel="radiothermostat:rtherm:mytherm1:status" }
|
||||
Number Therm_FanStatus "Fan Status [MAP(radiotherm.map):%s_fstus]" { channel="radiothermostat:rtherm:mytherm1:fan_status" }
|
||||
Number Therm_Override "Override [MAP(radiotherm.map):%s_over]" { channel="radiothermostat:rtherm:mytherm1:override" }
|
||||
Switch Therm_Hold "Hold" { channel="radiothermostat:rtherm:mytherm1:hold" }
|
||||
Number Therm_Pmode "Program Mode [MAP(radiotherm.map):%s_pgm]" { channel="radiothermostat:rtherm:mytherm1:program_mode" }
|
||||
Number:Temperature Therm_Setpt "Set Point [%d]" <temperature> { channel="radiothermostat:rtherm:mytherm1:set_point" }
|
||||
Number Therm_Status "Status [MAP(radiotherm.map):%s_stus]" { channel="radiothermostat:rtherm:mytherm1:status" }
|
||||
Number Therm_FanStatus "Fan Status [MAP(radiotherm.map):%s_fstus]" { channel="radiothermostat:rtherm:mytherm1:fan_status" }
|
||||
Number Therm_Override "Override [MAP(radiotherm.map):%s_over]" { channel="radiothermostat:rtherm:mytherm1:override" }
|
||||
Switch Therm_Hold "Hold" { channel="radiothermostat:rtherm:mytherm1:hold" }
|
||||
Number:Temperature Therm_NextTemp "Next Set Temp [%d %unit%]" <temperature> { channel="radiothermostat:rtherm:mytherm1:next_temp" }
|
||||
DateTime Therm_NextTime "Next Set Time [%1$tl:%1$tM %1$tp]" <time> { channel="radiothermostat:rtherm:mytherm1:next_time" }
|
||||
|
||||
Number Therm_Day "Thermostat Day [%d]" { channel="radiothermostat:rtherm:mytherm1:day" }
|
||||
Number Therm_Hour "Thermostat Hour [%d]" { channel="radiothermostat:rtherm:mytherm1:hour" }
|
||||
Number Therm_Minute "Thermostat Minute [%d]" { channel="radiothermostat:rtherm:mytherm1:minute" }
|
||||
String Therm_Dstmp "Thermostat DateStamp [%s]" <time> { channel="radiothermostat:rtherm:mytherm1:dt_stamp" }
|
||||
Number Therm_Day "Thermostat Day [%d]" { channel="radiothermostat:rtherm:mytherm1:day" }
|
||||
Number Therm_Hour "Thermostat Hour [%d]" { channel="radiothermostat:rtherm:mytherm1:hour" }
|
||||
Number Therm_Minute "Thermostat Minute [%d]" { channel="radiothermostat:rtherm:mytherm1:minute" }
|
||||
String Therm_Dstmp "Thermostat DateStamp [%s]" <time> { channel="radiothermostat:rtherm:mytherm1:dt_stamp" }
|
||||
|
||||
Number:Time Therm_todayheat "Today's Heating Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:today_heat_runtime", unit="min" }
|
||||
Number:Time Therm_todaycool "Today's Cooling Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:today_cool_runtime", unit="min" }
|
||||
Number:Time Therm_yesterdayheat "Yesterday's Heating Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:yesterday_heat_runtime", unit="min" }
|
||||
Number:Time Therm_yesterdaycool "Yesterday's Cooling Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:yesterday_cool_runtime", unit="min" }
|
||||
String Therm_Message "Message: [%s]" { channel="radiothermostat:rtherm:mytherm1:message" }
|
||||
Number:Time Therm_todayheat "Today's Heating Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:today_heat_runtime", unit="min" }
|
||||
Number:Time Therm_todaycool "Today's Cooling Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:today_cool_runtime", unit="min" }
|
||||
Number:Time Therm_yesterdayheat "Yesterday's Heating Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:yesterday_heat_runtime", unit="min" }
|
||||
Number:Time Therm_yesterdaycool "Yesterday's Cooling Runtime [%d %unit%]" { channel="radiothermostat:rtherm:mytherm1:yesterday_cool_runtime", unit="min" }
|
||||
String Therm_Message "Message: [%s]" { channel="radiothermostat:rtherm:mytherm1:message" }
|
||||
|
||||
// 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" }
|
||||
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"
|
||||
@ -201,6 +205,8 @@ sitemap radiotherm label="My Thermostat" {
|
||||
Text item=Therm_FanStatus icon="flow"
|
||||
Text item=Therm_Override icon="smoke"
|
||||
Switch item=Therm_Hold icon="smoke"
|
||||
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"]
|
||||
|
@ -12,6 +12,6 @@
|
||||
|
||||
<artifactId>org.openhab.binding.radiothermostat</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: RadioThermostat Binding</name>
|
||||
<name>openHAB Add-ons :: Bundles :: Radio Thermostat Binding</name>
|
||||
|
||||
</project>
|
||||
|
@ -77,12 +77,15 @@ public class RadioThermostatBindingConstants {
|
||||
public static final String YESTERDAY_COOL_RUNTIME = "yesterday_cool_runtime";
|
||||
public static final String REMOTE_TEMP = "remote_temp";
|
||||
public static final String MESSAGE = "message";
|
||||
public static final String NEXT_TEMP = "next_temp";
|
||||
public static final String NEXT_TIME = "next_time";
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_RTHERM);
|
||||
|
||||
public static final Set<String> SUPPORTED_CHANNEL_IDS = Set.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, REMOTE_TEMP, MESSAGE);
|
||||
TODAY_COOL_RUNTIME, YESTERDAY_HEAT_RUNTIME, YESTERDAY_COOL_RUNTIME, REMOTE_TEMP, MESSAGE, NEXT_TEMP,
|
||||
NEXT_TIME);
|
||||
|
||||
public static final Set<String> NO_UPDATE_CHANNEL_IDS = Set.of(REMOTE_TEMP, MESSAGE);
|
||||
|
||||
|
@ -39,7 +39,7 @@ import org.openhab.binding.radiothermostat.internal.dto.RadioThermostatDTO;
|
||||
import org.openhab.binding.radiothermostat.internal.dto.RadioThermostatHumidityDTO;
|
||||
import org.openhab.binding.radiothermostat.internal.dto.RadioThermostatRuntimeDTO;
|
||||
import org.openhab.binding.radiothermostat.internal.dto.RadioThermostatTstatDTO;
|
||||
import org.openhab.binding.radiothermostat.internal.util.RadioThermostatScheduleJson;
|
||||
import org.openhab.binding.radiothermostat.internal.util.RadioThermostatSchedule;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
@ -83,6 +83,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
private final Gson gson;
|
||||
private final RadioThermostatConnector connector;
|
||||
private final RadioThermostatDTO rthermData = new RadioThermostatDTO();
|
||||
private @Nullable RadioThermostatSchedule thermostatSchedule;
|
||||
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
private @Nullable ScheduledFuture<?> logRefreshJob;
|
||||
@ -151,10 +152,10 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
updateThing(editThing().withChannels(channels).build());
|
||||
}
|
||||
|
||||
final RadioThermostatScheduleJson thermostatSchedule = new RadioThermostatScheduleJson(config);
|
||||
final RadioThermostatSchedule localSchedule = thermostatSchedule = new RadioThermostatSchedule(config);
|
||||
|
||||
try {
|
||||
heatProgramJson = thermostatSchedule.getHeatProgramJson();
|
||||
heatProgramJson = localSchedule.getHeatProgramJson();
|
||||
} catch (IllegalStateException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.configuration-error-heating-program");
|
||||
@ -162,7 +163,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
}
|
||||
|
||||
try {
|
||||
coolProgramJson = thermostatSchedule.getCoolProgramJson();
|
||||
coolProgramJson = localSchedule.getCoolProgramJson();
|
||||
} catch (IllegalStateException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.configuration-error-cooling-program");
|
||||
@ -359,6 +360,8 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
updateChannel(SET_POINT, rthermData);
|
||||
rthermData.getThermostatData().setHold(0);
|
||||
updateChannel(HOLD, rthermData);
|
||||
updateChannel(NEXT_TEMP, rthermData);
|
||||
updateChannel(NEXT_TIME, rthermData);
|
||||
rthermData.getThermostatData().setProgramMode(-1);
|
||||
updateChannel(PROGRAM_MODE, rthermData);
|
||||
|
||||
@ -383,6 +386,8 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
rthermData.getThermostatData().setHold(0);
|
||||
connector.sendCommand("hold", "0", DEFAULT_RESOURCE);
|
||||
}
|
||||
updateChannel(NEXT_TEMP, rthermData);
|
||||
updateChannel(NEXT_TIME, rthermData);
|
||||
break;
|
||||
case SET_POINT:
|
||||
String cmdKey;
|
||||
@ -404,7 +409,10 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
if (cmdInt != -1) {
|
||||
QuantityType<?> remoteTemp = ((QuantityType<Temperature>) command)
|
||||
.toUnit(ImperialUnits.FAHRENHEIT);
|
||||
connector.sendCommand("rem_temp", String.valueOf(remoteTemp.intValue()), REMOTE_TEMP_RESOURCE);
|
||||
if (remoteTemp != null) {
|
||||
connector.sendCommand("rem_temp", String.valueOf(remoteTemp.intValue()),
|
||||
REMOTE_TEMP_RESOURCE);
|
||||
}
|
||||
} else {
|
||||
connector.sendCommand("rem_mode", "0", REMOTE_TEMP_RESOURCE);
|
||||
}
|
||||
@ -477,7 +485,7 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
if (isLinked(channelId)) {
|
||||
Object value;
|
||||
try {
|
||||
value = getValue(channelId, rthermData);
|
||||
value = getValue(channelId, rthermData, thermostatSchedule);
|
||||
} catch (Exception e) {
|
||||
logger.debug("Error setting {} value", channelId.toUpperCase());
|
||||
return;
|
||||
@ -519,7 +527,8 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
* @param data the RadioThermostat dto
|
||||
* @return the value to be set in the state
|
||||
*/
|
||||
public static @Nullable Object getValue(String channelId, RadioThermostatDTO data) {
|
||||
public static @Nullable Object getValue(String channelId, RadioThermostatDTO data,
|
||||
@Nullable RadioThermostatSchedule thermostatSchedule) {
|
||||
switch (channelId) {
|
||||
case TEMPERATURE:
|
||||
if (data.getThermostatData().getTemperature() != null) {
|
||||
@ -576,6 +585,22 @@ public class RadioThermostatHandler extends BaseThingHandler implements RadioThe
|
||||
case YESTERDAY_COOL_RUNTIME:
|
||||
return new QuantityType<>(data.getRuntime().getYesterday().getCoolTime().getRuntime(),
|
||||
API_MINUTES_UNIT);
|
||||
case NEXT_TEMP:
|
||||
if (thermostatSchedule != null) {
|
||||
final Integer nextTemp = thermostatSchedule.getNextTemp(data.getThermostatData());
|
||||
if (nextTemp != null) {
|
||||
return new QuantityType<>(nextTemp, API_TEMPERATURE_UNIT);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
case NEXT_TIME:
|
||||
if (thermostatSchedule != null) {
|
||||
final ZonedDateTime nextTime = thermostatSchedule.getNextTime(data.getThermostatData());
|
||||
if (nextTime != null) {
|
||||
return nextTime;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,296 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.radiothermostat.internal.util;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.radiothermostat.internal.RadioThermostatConfiguration;
|
||||
import org.openhab.binding.radiothermostat.internal.dto.RadioThermostatTstatDTO;
|
||||
|
||||
/**
|
||||
* The {@link RadioThermostatSchedule} is the class used to convert the heating and cooling schedules from user
|
||||
* configuration into the json that is sent to the thermostat
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RadioThermostatSchedule {
|
||||
|
||||
private final ArrayList<DaySchedule> heatSchedule = new ArrayList<DaySchedule>();
|
||||
private final ArrayList<DaySchedule> coolSchedule = new ArrayList<DaySchedule>();
|
||||
|
||||
public RadioThermostatSchedule(RadioThermostatConfiguration config) {
|
||||
heatSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.monMorningHeatTime, config.monMorningHeatTemp),
|
||||
new SetPeriod(config.monDayHeatTime, config.monDayHeatTemp),
|
||||
new SetPeriod(config.monEveningHeatTime, config.monEveningHeatTemp),
|
||||
new SetPeriod(config.monNightHeatTime, config.monNightHeatTemp)))));
|
||||
|
||||
heatSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.tueMorningHeatTime, config.tueMorningHeatTemp),
|
||||
new SetPeriod(config.tueDayHeatTime, config.tueDayHeatTemp),
|
||||
new SetPeriod(config.tueEveningHeatTime, config.tueEveningHeatTemp),
|
||||
new SetPeriod(config.tueNightHeatTime, config.tueNightHeatTemp)))));
|
||||
|
||||
heatSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.wedMorningHeatTime, config.wedMorningHeatTemp),
|
||||
new SetPeriod(config.wedDayHeatTime, config.wedDayHeatTemp),
|
||||
new SetPeriod(config.wedEveningHeatTime, config.wedEveningHeatTemp),
|
||||
new SetPeriod(config.wedNightHeatTime, config.wedNightHeatTemp)))));
|
||||
|
||||
heatSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.thuMorningHeatTime, config.thuMorningHeatTemp),
|
||||
new SetPeriod(config.thuDayHeatTime, config.thuDayHeatTemp),
|
||||
new SetPeriod(config.thuEveningHeatTime, config.thuEveningHeatTemp),
|
||||
new SetPeriod(config.thuNightHeatTime, config.thuNightHeatTemp)))));
|
||||
|
||||
heatSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.friMorningHeatTime, config.friMorningHeatTemp),
|
||||
new SetPeriod(config.friDayHeatTime, config.friDayHeatTemp),
|
||||
new SetPeriod(config.friEveningHeatTime, config.friEveningHeatTemp),
|
||||
new SetPeriod(config.friNightHeatTime, config.friNightHeatTemp)))));
|
||||
|
||||
heatSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.satMorningHeatTime, config.satMorningHeatTemp),
|
||||
new SetPeriod(config.satDayHeatTime, config.satDayHeatTemp),
|
||||
new SetPeriod(config.satEveningHeatTime, config.satEveningHeatTemp),
|
||||
new SetPeriod(config.satNightHeatTime, config.satNightHeatTemp)))));
|
||||
|
||||
heatSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.sunMorningHeatTime, config.sunMorningHeatTemp),
|
||||
new SetPeriod(config.sunDayHeatTime, config.sunDayHeatTemp),
|
||||
new SetPeriod(config.sunEveningHeatTime, config.sunEveningHeatTemp),
|
||||
new SetPeriod(config.sunNightHeatTime, config.sunNightHeatTemp)))));
|
||||
|
||||
coolSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.monMorningCoolTime, config.monMorningCoolTemp),
|
||||
new SetPeriod(config.monDayCoolTime, config.monDayCoolTemp),
|
||||
new SetPeriod(config.monEveningCoolTime, config.monEveningCoolTemp),
|
||||
new SetPeriod(config.monNightCoolTime, config.monNightCoolTemp)))));
|
||||
|
||||
coolSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.tueMorningCoolTime, config.tueMorningCoolTemp),
|
||||
new SetPeriod(config.tueDayCoolTime, config.tueDayCoolTemp),
|
||||
new SetPeriod(config.tueEveningCoolTime, config.tueEveningCoolTemp),
|
||||
new SetPeriod(config.tueNightCoolTime, config.tueNightCoolTemp)))));
|
||||
|
||||
coolSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.wedMorningCoolTime, config.wedMorningCoolTemp),
|
||||
new SetPeriod(config.wedDayCoolTime, config.wedDayCoolTemp),
|
||||
new SetPeriod(config.wedEveningCoolTime, config.wedEveningCoolTemp),
|
||||
new SetPeriod(config.wedNightCoolTime, config.wedNightCoolTemp)))));
|
||||
|
||||
coolSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.thuMorningCoolTime, config.thuMorningCoolTemp),
|
||||
new SetPeriod(config.thuDayCoolTime, config.thuDayCoolTemp),
|
||||
new SetPeriod(config.thuEveningCoolTime, config.thuEveningCoolTemp),
|
||||
new SetPeriod(config.thuNightCoolTime, config.thuNightCoolTemp)))));
|
||||
|
||||
coolSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.friMorningCoolTime, config.friMorningCoolTemp),
|
||||
new SetPeriod(config.friDayCoolTime, config.friDayCoolTemp),
|
||||
new SetPeriod(config.friEveningCoolTime, config.friEveningCoolTemp),
|
||||
new SetPeriod(config.friNightCoolTime, config.friNightCoolTemp)))));
|
||||
|
||||
coolSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.satMorningCoolTime, config.satMorningCoolTemp),
|
||||
new SetPeriod(config.satDayCoolTime, config.satDayCoolTemp),
|
||||
new SetPeriod(config.satEveningCoolTime, config.satEveningCoolTemp),
|
||||
new SetPeriod(config.satNightCoolTime, config.satNightCoolTemp)))));
|
||||
|
||||
coolSchedule.add(new DaySchedule(
|
||||
new ArrayList<SetPeriod>(List.of(new SetPeriod(config.sunMorningCoolTime, config.sunMorningCoolTemp),
|
||||
new SetPeriod(config.sunDayCoolTime, config.sunDayCoolTemp),
|
||||
new SetPeriod(config.sunEveningCoolTime, config.sunEveningCoolTemp),
|
||||
new SetPeriod(config.sunNightCoolTime, config.sunNightCoolTemp)))));
|
||||
}
|
||||
|
||||
public String getHeatProgramJson() throws IllegalStateException {
|
||||
return getProgramJson(heatSchedule);
|
||||
}
|
||||
|
||||
public String getCoolProgramJson() throws IllegalStateException {
|
||||
return getProgramJson(coolSchedule);
|
||||
}
|
||||
|
||||
private String getProgramJson(ArrayList<DaySchedule> schedule) throws IllegalStateException {
|
||||
// all were null, bypass
|
||||
if (schedule.stream().allMatch(day -> day.isAnyNull())) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// some were null, the schedule is invalid
|
||||
if (schedule.stream().anyMatch(day -> day.isAnyNull())) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
final StringBuilder json = new StringBuilder("{");
|
||||
IntStream.range(0, 7).forEach(i -> {
|
||||
json.append("\"" + i + "\":" + getDaySchedule(schedule.get(i)));
|
||||
if (i < 6) {
|
||||
json.append(",");
|
||||
}
|
||||
});
|
||||
json.append("}");
|
||||
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
private @Nullable String getDaySchedule(DaySchedule day) {
|
||||
// if any of the time or temp fields are null, this day schedule is not valid
|
||||
if (day.isAnyNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ArrayList<SetPeriod> setPeriods = day.getSchedule();
|
||||
|
||||
final int morningMin;
|
||||
final int dayMin;
|
||||
final int eveningMin;
|
||||
final int nightMin;
|
||||
|
||||
try {
|
||||
morningMin = setPeriods.get(0).getMinutes();
|
||||
dayMin = setPeriods.get(1).getMinutes();
|
||||
eveningMin = setPeriods.get(2).getMinutes();
|
||||
nightMin = setPeriods.get(3).getMinutes();
|
||||
} catch (NumberFormatException nfe) {
|
||||
// if any of the times could not be parsed into minutes, the schedule is invalid
|
||||
return null;
|
||||
}
|
||||
|
||||
// the minute value for each period must be greater than the previous period otherwise the schedule is invalid
|
||||
if (morningMin >= dayMin || dayMin >= eveningMin || eveningMin >= nightMin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "[" + morningMin + "," + setPeriods.get(0).getTemp() + "," + dayMin + "," + setPeriods.get(1).getTemp()
|
||||
+ "," + eveningMin + "," + setPeriods.get(2).getTemp() + "," + nightMin + ","
|
||||
+ setPeriods.get(3).getTemp() + "]";
|
||||
}
|
||||
|
||||
public class DaySchedule {
|
||||
private final ArrayList<SetPeriod> schedule;
|
||||
|
||||
public DaySchedule(ArrayList<SetPeriod> schedule) {
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
public ArrayList<SetPeriod> getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
public boolean isAnyNull() {
|
||||
return schedule.stream().anyMatch(itm -> itm.getTime() == null || itm.getTemp() == null);
|
||||
}
|
||||
}
|
||||
|
||||
public class SetPeriod {
|
||||
private final @Nullable String time;
|
||||
private final @Nullable Integer temp;
|
||||
|
||||
public SetPeriod(@Nullable String time, @Nullable Integer temp) {
|
||||
this.time = time;
|
||||
this.temp = temp;
|
||||
}
|
||||
|
||||
public @Nullable ZonedDateTime getTime() {
|
||||
final String timeLocal = time;
|
||||
|
||||
if (timeLocal != null) {
|
||||
final String[] hourMin = timeLocal.split(":");
|
||||
|
||||
// get a zdt with the hour and minute of the next set point
|
||||
final ZonedDateTime nextSetTime = ZonedDateTime.now().withHour(Integer.parseInt(hourMin[0]))
|
||||
.withMinute(Integer.parseInt(hourMin[1])).withSecond(0).withNano(0);
|
||||
|
||||
// if the next set point occurs tomorrow, add one day to the zdt
|
||||
if (nextSetTime.isBefore(ZonedDateTime.now())) {
|
||||
return nextSetTime.plusDays(1);
|
||||
}
|
||||
return nextSetTime;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable Integer getTemp() {
|
||||
return temp;
|
||||
}
|
||||
|
||||
public int getMinutes() {
|
||||
final String timeLocal = time;
|
||||
final String[] hourMin = timeLocal != null ? timeLocal.split(":") : new String[] { "" };
|
||||
return Integer.parseInt(hourMin[0]) * 60 + Integer.parseInt(hourMin[1]);
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Integer getNextTemp(RadioThermostatTstatDTO thermostatData) {
|
||||
final SetPeriod nextPeriod = getNextSetpoint(thermostatData);
|
||||
return nextPeriod != null ? nextPeriod.getTemp() : null;
|
||||
}
|
||||
|
||||
public @Nullable ZonedDateTime getNextTime(RadioThermostatTstatDTO thermostatData) {
|
||||
final SetPeriod nextPeriod = getNextSetpoint(thermostatData);
|
||||
return nextPeriod != null ? nextPeriod.getTime() : null;
|
||||
}
|
||||
|
||||
private @Nullable SetPeriod getNextSetpoint(RadioThermostatTstatDTO thermostatData) {
|
||||
if (thermostatData.getHold().equals(1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ArrayList<DaySchedule> schedule;
|
||||
|
||||
if (thermostatData.getMode().equals(1)) {
|
||||
schedule = heatSchedule;
|
||||
} else if (thermostatData.getMode().equals(2)) {
|
||||
schedule = coolSchedule;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
final DaySchedule daySched = schedule.get(thermostatData.getTime().getDayOfWeek().intValue());
|
||||
int nextPeriod = 0;
|
||||
|
||||
try {
|
||||
for (int i = 0; i <= 3; i++) {
|
||||
if (thermostatData.getTime().getRuntime().intValue() >= daySched.getSchedule().get(i).getMinutes()) {
|
||||
if (i == 3) {
|
||||
nextPeriod = -1;
|
||||
break;
|
||||
} else {
|
||||
nextPeriod = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (nextPeriod == -1) {
|
||||
int nextDay = thermostatData.getTime().getDayOfWeek().intValue() + 1;
|
||||
if (nextDay == 7) {
|
||||
nextDay = 0;
|
||||
}
|
||||
|
||||
return schedule.get(nextDay).getSchedule().get(0);
|
||||
}
|
||||
return daySched.getSchedule().get(nextPeriod);
|
||||
}
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.radiothermostat.internal.util;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.radiothermostat.internal.RadioThermostatConfiguration;
|
||||
|
||||
/**
|
||||
* The {@link RadioThermostatScheduleJson} is the class used to convert the heating and cooling schedules from user
|
||||
* configuration into the json that is sent to the thermostat
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RadioThermostatScheduleJson {
|
||||
private final @Nullable String monHeat;
|
||||
private final @Nullable String tueHeat;
|
||||
private final @Nullable String wedHeat;
|
||||
private final @Nullable String thuHeat;
|
||||
private final @Nullable String friHeat;
|
||||
private final @Nullable String satHeat;
|
||||
private final @Nullable String sunHeat;
|
||||
|
||||
private final @Nullable String monCool;
|
||||
private final @Nullable String tueCool;
|
||||
private final @Nullable String wedCool;
|
||||
private final @Nullable String thuCool;
|
||||
private final @Nullable String friCool;
|
||||
private final @Nullable String satCool;
|
||||
private final @Nullable String sunCool;
|
||||
|
||||
public RadioThermostatScheduleJson(RadioThermostatConfiguration config) {
|
||||
monHeat = getDaySchedule(config.monMorningHeatTime, config.monDayHeatTime, config.monEveningHeatTime,
|
||||
config.monNightHeatTime, config.monMorningHeatTemp, config.monDayHeatTemp, config.monEveningHeatTemp,
|
||||
config.monNightHeatTemp);
|
||||
tueHeat = getDaySchedule(config.tueMorningHeatTime, config.tueDayHeatTime, config.tueEveningHeatTime,
|
||||
config.tueNightHeatTime, config.tueMorningHeatTemp, config.tueDayHeatTemp, config.tueEveningHeatTemp,
|
||||
config.tueNightHeatTemp);
|
||||
wedHeat = getDaySchedule(config.wedMorningHeatTime, config.wedDayHeatTime, config.wedEveningHeatTime,
|
||||
config.wedNightHeatTime, config.wedMorningHeatTemp, config.wedDayHeatTemp, config.wedEveningHeatTemp,
|
||||
config.wedNightHeatTemp);
|
||||
thuHeat = getDaySchedule(config.thuMorningHeatTime, config.thuDayHeatTime, config.thuEveningHeatTime,
|
||||
config.thuNightHeatTime, config.thuMorningHeatTemp, config.thuDayHeatTemp, config.thuEveningHeatTemp,
|
||||
config.thuNightHeatTemp);
|
||||
friHeat = getDaySchedule(config.friMorningHeatTime, config.friDayHeatTime, config.friEveningHeatTime,
|
||||
config.friNightHeatTime, config.friMorningHeatTemp, config.friDayHeatTemp, config.friEveningHeatTemp,
|
||||
config.friNightHeatTemp);
|
||||
satHeat = getDaySchedule(config.satMorningHeatTime, config.satDayHeatTime, config.satEveningHeatTime,
|
||||
config.satNightHeatTime, config.satMorningHeatTemp, config.satDayHeatTemp, config.satEveningHeatTemp,
|
||||
config.satNightHeatTemp);
|
||||
sunHeat = getDaySchedule(config.sunMorningHeatTime, config.sunDayHeatTime, config.sunEveningHeatTime,
|
||||
config.sunNightHeatTime, config.sunMorningHeatTemp, config.sunDayHeatTemp, config.sunEveningHeatTemp,
|
||||
config.sunNightHeatTemp);
|
||||
|
||||
monCool = getDaySchedule(config.monMorningCoolTime, config.monDayCoolTime, config.monEveningCoolTime,
|
||||
config.monNightCoolTime, config.monMorningCoolTemp, config.monDayCoolTemp, config.monEveningCoolTemp,
|
||||
config.monNightCoolTemp);
|
||||
tueCool = getDaySchedule(config.tueMorningCoolTime, config.tueDayCoolTime, config.tueEveningCoolTime,
|
||||
config.tueNightCoolTime, config.tueMorningCoolTemp, config.tueDayCoolTemp, config.tueEveningCoolTemp,
|
||||
config.tueNightCoolTemp);
|
||||
wedCool = getDaySchedule(config.wedMorningCoolTime, config.wedDayCoolTime, config.wedEveningCoolTime,
|
||||
config.wedNightCoolTime, config.wedMorningCoolTemp, config.wedDayCoolTemp, config.wedEveningCoolTemp,
|
||||
config.wedNightCoolTemp);
|
||||
thuCool = getDaySchedule(config.thuMorningCoolTime, config.thuDayCoolTime, config.thuEveningCoolTime,
|
||||
config.thuNightCoolTime, config.thuMorningCoolTemp, config.thuDayCoolTemp, config.thuEveningCoolTemp,
|
||||
config.thuNightCoolTemp);
|
||||
friCool = getDaySchedule(config.friMorningCoolTime, config.friDayCoolTime, config.friEveningCoolTime,
|
||||
config.friNightCoolTime, config.friMorningCoolTemp, config.friDayCoolTemp, config.friEveningCoolTemp,
|
||||
config.friNightCoolTemp);
|
||||
satCool = getDaySchedule(config.satMorningCoolTime, config.satDayCoolTime, config.satEveningCoolTime,
|
||||
config.satNightCoolTime, config.satMorningCoolTemp, config.satDayCoolTemp, config.satEveningCoolTemp,
|
||||
config.satNightCoolTemp);
|
||||
sunCool = getDaySchedule(config.sunMorningCoolTime, config.sunDayCoolTime, config.sunEveningCoolTime,
|
||||
config.sunNightCoolTime, config.sunMorningCoolTemp, config.sunDayCoolTemp, config.sunEveningCoolTemp,
|
||||
config.sunNightCoolTemp);
|
||||
}
|
||||
|
||||
public String getHeatProgramJson() throws IllegalStateException {
|
||||
return getProgramJson(monHeat, tueHeat, wedHeat, thuHeat, friHeat, satHeat, sunHeat);
|
||||
}
|
||||
|
||||
public String getCoolProgramJson() throws IllegalStateException {
|
||||
return getProgramJson(monCool, tueCool, wedCool, thuCool, friCool, satCool, sunCool);
|
||||
}
|
||||
|
||||
private String getProgramJson(@Nullable String mon, @Nullable String tue, @Nullable String wed,
|
||||
@Nullable String thu, @Nullable String fri, @Nullable String sat, @Nullable String sun)
|
||||
throws IllegalStateException {
|
||||
// all were null, bypass
|
||||
if (mon == null && tue == null && wed == null && thu == null && fri == null && sat == null && sun == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// some were null, the schedule is invalid
|
||||
if (mon == null || tue == null || wed == null || thu == null || fri == null || sat == null || sun == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
return "{\"0\":" + mon + ",\"1\":" + tue + ",\"2\":" + wed + ",\"3\":" + thu + ",\"4\":" + fri + ",\"5\":" + sat
|
||||
+ ",\"6\":" + sun + "}";
|
||||
}
|
||||
|
||||
private @Nullable String getDaySchedule(@Nullable String morningTime, @Nullable String dayTime,
|
||||
@Nullable String eveningTime, @Nullable String nightTime, @Nullable Integer morningTemp,
|
||||
@Nullable Integer dayTemp, @Nullable Integer eveningTemp, @Nullable Integer nightTemp) {
|
||||
// if any null, this day schedule is not valid
|
||||
if (morningTime == null || dayTime == null || eveningTime == null || nightTime == null || morningTemp == null
|
||||
|| dayTemp == null || eveningTemp == null || nightTemp == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int morningMin;
|
||||
final int dayMin;
|
||||
final int eveningMin;
|
||||
final int nightMin;
|
||||
|
||||
try {
|
||||
morningMin = parseMinutes(morningTime);
|
||||
dayMin = parseMinutes(dayTime);
|
||||
eveningMin = parseMinutes(eveningTime);
|
||||
nightMin = parseMinutes(nightTime);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// if any of the times could not be parsed into minutes, the schedule is invalid
|
||||
return null;
|
||||
}
|
||||
|
||||
// the minute value for each period must be greater than the previous period otherwise the schedule is invalid
|
||||
if (morningMin >= dayMin || dayMin >= eveningMin || eveningMin >= nightMin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "[" + morningMin + "," + morningTemp + "," + dayMin + "," + dayTemp + "," + eveningMin + ","
|
||||
+ eveningTemp + "," + nightMin + "," + nightTemp + "]";
|
||||
}
|
||||
|
||||
private int parseMinutes(String timeStr) {
|
||||
final String[] hourMin = timeStr.split(":");
|
||||
|
||||
return Integer.parseInt(hourMin[0]) * 60 + Integer.parseInt(hourMin[1]);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
|
||||
<type>binding</type>
|
||||
<name>Radio Thermostat Binding</name>
|
||||
<description>Controls the RadioThermostat model CT30, CT50 or CT80 via the built-in WIFI module</description>
|
||||
<description>Controls the Radio Thermostat model CT30, CT50 or CT80 via the built-in WIFI module</description>
|
||||
<connection>local</connection>
|
||||
|
||||
<discovery-methods>
|
||||
|
@ -1,7 +1,7 @@
|
||||
# add-on
|
||||
|
||||
addon.radiothermostat.name = Radio Thermostat Binding
|
||||
addon.radiothermostat.description = Controls the RadioThermostat model CT30, CT50 or CT80 via the built-in WIFI module
|
||||
addon.radiothermostat.description = Controls the Radio Thermostat model CT30, CT50 or CT80 via the built-in WIFI module
|
||||
|
||||
# thing types
|
||||
|
||||
@ -275,6 +275,10 @@ channel-type.radiothermostat.mode.state.option.0 = Off
|
||||
channel-type.radiothermostat.mode.state.option.1 = Heat
|
||||
channel-type.radiothermostat.mode.state.option.2 = Cool
|
||||
channel-type.radiothermostat.mode.state.option.3 = Auto
|
||||
channel-type.radiothermostat.next_temp.label = Next Set Temp
|
||||
channel-type.radiothermostat.next_temp.description = Displays the next scheduled thermostat set point temperature
|
||||
channel-type.radiothermostat.next_time.label = Next Set Time
|
||||
channel-type.radiothermostat.next_time.description = Displays the next scheduled thermostat set point time
|
||||
channel-type.radiothermostat.override.label = Override
|
||||
channel-type.radiothermostat.override.description = Indicates If the Normal Program Setpoint Has Been Manually Overriden
|
||||
channel-type.radiothermostat.program_mode.label = Program Mode
|
||||
|
@ -32,10 +32,12 @@
|
||||
<channel id="yesterday_heat_runtime" typeId="yesterday_heat_runtime"/>
|
||||
<channel id="yesterday_cool_runtime" typeId="yesterday_cool_runtime"/>
|
||||
<channel id="message" typeId="message"/>
|
||||
<channel id="next_temp" typeId="next_temp"/>
|
||||
<channel id="next_time" typeId="next_time"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
<property name="thingTypeVersion">2</property>
|
||||
</properties>
|
||||
|
||||
<config-description-ref uri="thing-type:radiothermostat:thermostatconfig"/>
|
||||
@ -197,4 +199,28 @@
|
||||
<description>Use this channel to display a number in the price message area</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="next_temp" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Next Set Temp</label>
|
||||
<description>Displays the next scheduled thermostat set point temperature</description>
|
||||
<category>Temperature</category>
|
||||
<tags>
|
||||
<tag>Setpoint</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="next_time" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Next Set Time</label>
|
||||
<description>Displays the next scheduled thermostat set point time</description>
|
||||
<category>Time</category>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>Timestamp</tag>
|
||||
</tags>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
@ -11,4 +11,15 @@
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
<thing-type uid="radiothermostat:rtherm">
|
||||
<instruction-set targetVersion="2">
|
||||
<add-channel id="next_temp">
|
||||
<type>radiothermostat:next_temp</type>
|
||||
</add-channel>
|
||||
<add-channel id="next_time">
|
||||
<type>radiothermostat:next_time</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
</update:update-descriptions>
|
||||
|
Loading…
Reference in New Issue
Block a user