mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[daikin] Add demand control support for ac_unit (#17087)
Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
parent
b0f9d82c1e
commit
7e73ed83cf
@ -45,50 +45,53 @@ A BRP072C42 adapter requires a registered UUID to authenticate. Upon discovery,
|
||||
The temperature channels have a precision of one half degree Celsius.
|
||||
For the BRP072A42 and BRP072C42:
|
||||
|
||||
| Channel Name | Description |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| power | Turns the power on/off for the air conditioning unit. |
|
||||
| settemp | The temperature set for the air conditioning unit. |
|
||||
| indoortemp | The indoor temperature as measured by the unit. |
|
||||
| outdoortemp | The outdoor temperature as measured by the external part of the air conditioning system. May not be available when unit is off. |
|
||||
| humidity | The indoor humidity as measured by the unit. This is not available on all units. |
|
||||
| mode | The mode set for the unit (AUTO, DEHUMIDIFIER, COLD, HEAT, FAN) |
|
||||
| homekitmode | A mode that is compatible with homekit/alexa/google home (off, auto, heat, cool). Not tested for BRP069B41 |
|
||||
| fanspeed | The fan speed set for the unit (AUTO, SILENCE, LEVEL_1, LEVEL_2, LEVEL_3, LEVEL_4, LEVEL_5) |
|
||||
| fandir | The fan blade direction (STOPPED, VERTICAL, HORIZONTAL, VERTICAL_AND_HORIZONTAL) |
|
||||
| cmpfrequency | The compressor frequency |
|
||||
| specialmode | The special mode set for the unit (NORMAL, ECO, POWERFUL). This is not available on all units. |
|
||||
| streamer | Turns the streamer feature on/off for the air conditioning unit. This is not available on all units. |
|
||||
| energyheatingtoday | The energy consumption when heating for today |
|
||||
| energyheatingthisweek | The energy consumption when heating for this week |
|
||||
| energyheatinglastweek | The energy consumption when heating for last week |
|
||||
| energyheatingcurrentyear-1 | The energy consumption when heating for current year January |
|
||||
| energyheatingcurrentyear-2 | The energy consumption when heating for current year February |
|
||||
| energyheatingcurrentyear-3 | The energy consumption when heating for current year March |
|
||||
| energyheatingcurrentyear-4 | The energy consumption when heating for current year April |
|
||||
| energyheatingcurrentyear-5 | The energy consumption when heating for current year May |
|
||||
| energyheatingcurrentyear-6 | The energy consumption when heating for current year June |
|
||||
| energyheatingcurrentyear-7 | The energy consumption when heating for current year July |
|
||||
| energyheatingcurrentyear-8 | The energy consumption when heating for current year August |
|
||||
| energyheatingcurrentyear-9 | The energy consumption when heating for current year September |
|
||||
| energyheatingcurrentyear-10 | The energy consumption when heating for current year October |
|
||||
| energyheatingcurrentyear-11 | The energy consumption when heating for current year November |
|
||||
| energyheatingcurrentyear-12 | The energy consumption when heating for current year December |
|
||||
| energycoolingtoday | The energy consumption when cooling for today |
|
||||
| energycoolingthisweek | The energy consumption when cooling for this week |
|
||||
| energycoolinglastweek | The energy consumption when cooling for last week |
|
||||
| energycoolingcurrentyear-1 | The energy consumption when cooling for current year January |
|
||||
| energycoolingcurrentyear-2 | The energy consumption when cooling for current year February |
|
||||
| energycoolingcurrentyear-3 | The energy consumption when cooling for current year March |
|
||||
| energycoolingcurrentyear-4 | The energy consumption when cooling for current year April |
|
||||
| energycoolingcurrentyear-5 | The energy consumption when cooling for current year May |
|
||||
| energycoolingcurrentyear-6 | The energy consumption when cooling for current year June |
|
||||
| energycoolingcurrentyear-7 | The energy consumption when cooling for current year July |
|
||||
| energycoolingcurrentyear-8 | The energy consumption when cooling for current year August |
|
||||
| energycoolingcurrentyear-9 | The energy consumption when cooling for current year September |
|
||||
| energycoolingcurrentyear-10 | The energy consumption when cooling for current year October |
|
||||
| energycoolingcurrentyear-11 | The energy consumption when cooling for current year November |
|
||||
| energycoolingcurrentyear-12 | The energy consumption when cooling for current year December |
|
||||
| Channel Name | Description |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| power | Turns the power on/off for the air conditioning unit. |
|
||||
| settemp | The temperature set for the air conditioning unit. |
|
||||
| indoortemp | The indoor temperature as measured by the unit. |
|
||||
| outdoortemp | The outdoor temperature as measured by the external part of the air conditioning system. May not be available when unit is off. |
|
||||
| humidity | The indoor humidity as measured by the unit. This is not available on all units. |
|
||||
| mode | The mode set for the unit (AUTO, DEHUMIDIFIER, COLD, HEAT, FAN) |
|
||||
| homekitmode | A mode that is compatible with homekit/alexa/google home (off, auto, heat, cool). Not tested for BRP069B41 |
|
||||
| fanspeed | The fan speed set for the unit (AUTO, SILENCE, LEVEL_1, LEVEL_2, LEVEL_3, LEVEL_4, LEVEL_5) |
|
||||
| fandir | The fan blade direction (STOPPED, VERTICAL, HORIZONTAL, VERTICAL_AND_HORIZONTAL) |
|
||||
| cmpfrequency | The compressor frequency |
|
||||
| specialmode | The special mode set for the unit (NORMAL, ECO, POWERFUL). This is not available on all units. |
|
||||
| streamer | Turns the streamer feature on/off for the air conditioning unit. This is not available on all units. |
|
||||
| energyheatingtoday | The energy consumption when heating for today |
|
||||
| energyheatingthisweek | The energy consumption when heating for this week |
|
||||
| energyheatinglastweek | The energy consumption when heating for last week |
|
||||
| energyheatingcurrentyear-1 | The energy consumption when heating for current year January |
|
||||
| energyheatingcurrentyear-2 | The energy consumption when heating for current year February |
|
||||
| energyheatingcurrentyear-3 | The energy consumption when heating for current year March |
|
||||
| energyheatingcurrentyear-4 | The energy consumption when heating for current year April |
|
||||
| energyheatingcurrentyear-5 | The energy consumption when heating for current year May |
|
||||
| energyheatingcurrentyear-6 | The energy consumption when heating for current year June |
|
||||
| energyheatingcurrentyear-7 | The energy consumption when heating for current year July |
|
||||
| energyheatingcurrentyear-8 | The energy consumption when heating for current year August |
|
||||
| energyheatingcurrentyear-9 | The energy consumption when heating for current year September |
|
||||
| energyheatingcurrentyear-10 | The energy consumption when heating for current year October |
|
||||
| energyheatingcurrentyear-11 | The energy consumption when heating for current year November |
|
||||
| energyheatingcurrentyear-12 | The energy consumption when heating for current year December |
|
||||
| energycoolingtoday | The energy consumption when cooling for today |
|
||||
| energycoolingthisweek | The energy consumption when cooling for this week |
|
||||
| energycoolinglastweek | The energy consumption when cooling for last week |
|
||||
| energycoolingcurrentyear-1 | The energy consumption when cooling for current year January |
|
||||
| energycoolingcurrentyear-2 | The energy consumption when cooling for current year February |
|
||||
| energycoolingcurrentyear-3 | The energy consumption when cooling for current year March |
|
||||
| energycoolingcurrentyear-4 | The energy consumption when cooling for current year April |
|
||||
| energycoolingcurrentyear-5 | The energy consumption when cooling for current year May |
|
||||
| energycoolingcurrentyear-6 | The energy consumption when cooling for current year June |
|
||||
| energycoolingcurrentyear-7 | The energy consumption when cooling for current year July |
|
||||
| energycoolingcurrentyear-8 | The energy consumption when cooling for current year August |
|
||||
| energycoolingcurrentyear-9 | The energy consumption when cooling for current year September |
|
||||
| energycoolingcurrentyear-10 | The energy consumption when cooling for current year October |
|
||||
| energycoolingcurrentyear-11 | The energy consumption when cooling for current year November |
|
||||
| energycoolingcurrentyear-12 | The energy consumption when cooling for current year December |
|
||||
| demandcontrolmode | The demand control mode (`OFF`, `AUTO`, `MANUAL`, `SCHEDULED`) |
|
||||
| demandcontrolmaxpower | The maximum power when in `MANUAL` mode. Values between 40 and 100 are accepted in an increment of 5. In `SCHEDULED` demand control mode, this channel will be updated with the calculated maximum power based on the current active schedule. |
|
||||
| demandcontrolschedule | A JSON string that contains the scheduled demand control settings. See below. |
|
||||
|
||||
For the BRP15B61:
|
||||
|
||||
@ -110,6 +113,135 @@ For the BRP15B61:
|
||||
| zone7 | Turns zone 7 on/off for the air conditioning unit. |
|
||||
| zone8 | Turns zone 8 on/off for the air conditioning unit. |
|
||||
|
||||
## Demand Control
|
||||
|
||||
Some units have a _demand control_ feature to limit the maximum power usage to a certain percentage.
|
||||
This is set through the `demandcontrolmode` channel which accepts `OFF`, `MANUAL`, `SCHEDULED`, or `AUTO`.
|
||||
|
||||
When changing the mode from `MANUAL` to another mode, the maximum power setting will be saved in the Binding's memory and restored when switching the mode back to `MANUAL`.
|
||||
Equally, when changing the mode from `SCHEDULED` to another mode, the current schedule will be saved in the Binding's memory and restored when switching the mode back to `SCHEDULED`.
|
||||
|
||||
### Manual Demand Control
|
||||
|
||||
Manual demand control requires setting the `demandcontrolmaxpower` channel to the desired limit.
|
||||
The unit accepts values between 40% and 100% in increments of 5.
|
||||
|
||||
Sending a command to the `demandcontrolmaxpower` channel will automatically switch the demand control mode to `MANUAL`.
|
||||
|
||||
### Scheduled Demand Control
|
||||
|
||||
It is possible to set the demand control power limit based on day of the week and time of day schedules.
|
||||
When the unit is in scheduled demand control mode, the binding provides the current schedule through the `demandcontrolschedule` channel.
|
||||
|
||||
In `SCHEDULED`, the `demandcontrolmaxpower` channel will provide the _current_ maximum power in effect, as defined within the schedule.
|
||||
This information is not provided by the unit itself.
|
||||
It is calculated by the binding based on the current schedule.
|
||||
Therefore, it is important to ensure that openHAB's local time is in sync with the unit's date/time.
|
||||
|
||||
The schedule and associated max power settings can be set by sending a command to the `demandcontrolschedule` channel.
|
||||
When doing so, the demand control mode will automatically change to `SCHEDULED`, if it wasn't already in that mode.
|
||||
|
||||
The schedule is specified in a JSON string in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"monday": [
|
||||
{
|
||||
"enabled": true,
|
||||
"time": <minutes from midnight>,
|
||||
"power": <power in percent>
|
||||
}
|
||||
],
|
||||
"tuesday": [
|
||||
// Schedule entries for Tuesday
|
||||
],
|
||||
"wednesday": [
|
||||
|
||||
],
|
||||
// more days up to Sunday
|
||||
"sunday": [
|
||||
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Concrete example:
|
||||
|
||||
The JSON format doesn't actually support comments. They are provided for clarity.
|
||||
|
||||
```json
|
||||
{
|
||||
"monday": [
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 480, // 8 am
|
||||
"power": 80
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 600, // 10 am
|
||||
"power": 100
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 960, // 4pm
|
||||
"power": 50
|
||||
}
|
||||
],
|
||||
"tuesday": [
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 480, // 8 am
|
||||
"power": 80
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 600, // 10 am
|
||||
"power": 100
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 960, // 4pm
|
||||
"power": 50
|
||||
}
|
||||
],
|
||||
"wednesday": [
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 480, // 8 am
|
||||
"power": 80
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 600, // 10 am
|
||||
"power": 100
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 960, // 4pm
|
||||
"power": 50
|
||||
}
|
||||
],
|
||||
"thursday": [
|
||||
{
|
||||
"enabled": true,
|
||||
"time": 480, // 8 am
|
||||
"power": 100
|
||||
}
|
||||
]
|
||||
// omitted days mean that they contain no schedules
|
||||
}
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- Each day can have up to 4 schedule entries
|
||||
- `enabled` means whether this schedule element is enabled.
|
||||
- `time` is the start time of the schedule, expressed in number of minutes from midnight.
|
||||
- `power` a value of zero means demand power is disabled at the time defined by the `time` element.
|
||||
- When there are no schedules defined for the current day/time, it is believed that the settings from the previous schedule will apply, bearing in mind that it is a weekly recurring schedule.
|
||||
This is ultimately determined by the logic in the unit itself, and not controlled by the Binding.
|
||||
|
||||
## Full Example
|
||||
|
||||
daikin.things:
|
||||
@ -135,7 +267,10 @@ String DaikinACUnit_Fan { channel="daikin:ac_unit:living_room_ac:fanspeed" }
|
||||
String DaikinACUnit_Fan_Movement { channel="daikin:ac_unit:living_room_ac:fandir" }
|
||||
Number:Temperature DaikinACUnit_IndoorTemperature { channel="daikin:ac_unit:living_room_ac:indoortemp" }
|
||||
Number:Temperature DaikinACUnit_OutdoorTemperature { channel="daikin:ac_unit:living_room_ac:outdoortemp" }
|
||||
|
||||
// Demand control, when supported by the unit
|
||||
String DaikinACUnit_DemandControl_Mode { channel="daikin:ac_unit:living_room_ac:demandcontrolmode" }
|
||||
Dimmer DaikinACUnit_DemandControl_MaxPower { channel="daikin:ac_unit:living_room_ac:demandcontrolmaxpower" }
|
||||
String DaikinACUnit_DemandControl_Schedule { channel="daikin:ac_unit:living_room_ac:demandcontrolschedule" }
|
||||
|
||||
// for Airbase (BRP15B61)
|
||||
Switch DaikinACUnit_Power { channel="daikin:airbase_ac_unit:living_room_ac:power" }
|
||||
@ -153,7 +288,6 @@ Switch DaikinACUnit_Zone5 { channel="daikin:airbase_ac_unit:living_room_ac:zone5
|
||||
Switch DaikinACUnit_Zone6 { channel="daikin:airbase_ac_unit:living_room_ac:zone6" }
|
||||
Switch DaikinACUnit_Zone7 { channel="daikin:airbase_ac_unit:living_room_ac:zone7" }
|
||||
Switch DaikinACUnit_Zone8 { channel="daikin:airbase_ac_unit:living_room_ac:zone8" }
|
||||
|
||||
```
|
||||
|
||||
daikin.sitemap:
|
||||
@ -183,5 +317,4 @@ Switch item=DaikinACUnit_Zone5 visibility=[DaikinACUnit_Power==ON]
|
||||
Switch item=DaikinACUnit_Zone6 visibility=[DaikinACUnit_Power==ON]
|
||||
Switch item=DaikinACUnit_Zone7 visibility=[DaikinACUnit_Power==ON]
|
||||
Switch item=DaikinACUnit_Zone8 visibility=[DaikinACUnit_Power==ON]
|
||||
|
||||
```
|
||||
|
@ -64,6 +64,10 @@ public class DaikinBindingConstants {
|
||||
public static final String CHANNEL_AC_SPECIALMODE = "specialmode";
|
||||
public static final String CHANNEL_AC_STREAMER = "streamer";
|
||||
|
||||
public static final String CHANNEL_AC_DEMAND_MODE = "demandcontrolmode";
|
||||
public static final String CHANNEL_AC_DEMAND_MAX_POWER = "demandcontrolmaxpower";
|
||||
public static final String CHANNEL_AC_DEMAND_SCHEDULE = "demandcontrolschedule";
|
||||
|
||||
// additional channels for Airbase Controller
|
||||
public static final String CHANNEL_AIRBASE_AC_FAN_SPEED = "airbasefanspeed";
|
||||
public static final String CHANNEL_AIRBASE_AC_ZONE = "zone";
|
||||
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.daikin.internal.api.BasicInfo;
|
||||
import org.openhab.binding.daikin.internal.api.ControlInfo;
|
||||
import org.openhab.binding.daikin.internal.api.DemandControl;
|
||||
import org.openhab.binding.daikin.internal.api.EnergyInfoDayAndWeek;
|
||||
import org.openhab.binding.daikin.internal.api.EnergyInfoYear;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.SpecialMode;
|
||||
@ -62,6 +63,8 @@ public class DaikinWebTargets {
|
||||
private String getEnergyInfoYearUri;
|
||||
private String getEnergyInfoWeekUri;
|
||||
private String setSpecialModeUri;
|
||||
private String setDemandControlUri;
|
||||
private String getDemandControlUri;
|
||||
|
||||
private String setAirbaseControlInfoUri;
|
||||
private String getAirbaseControlInfoUri;
|
||||
@ -90,6 +93,8 @@ public class DaikinWebTargets {
|
||||
getEnergyInfoYearUri = baseUri + "aircon/get_year_power_ex";
|
||||
getEnergyInfoWeekUri = baseUri + "aircon/get_week_power_ex";
|
||||
setSpecialModeUri = baseUri + "aircon/set_special_mode";
|
||||
setDemandControlUri = baseUri + "aircon/set_demand_control";
|
||||
getDemandControlUri = baseUri + "aircon/get_demand_control";
|
||||
|
||||
// Daikin Airbase API
|
||||
getAirbaseBasicInfoUri = baseUri + "skyfi/common/basic_info";
|
||||
@ -169,6 +174,18 @@ public class DaikinWebTargets {
|
||||
}
|
||||
}
|
||||
|
||||
public DemandControl getDemandControl() throws DaikinCommunicationException {
|
||||
String response = invoke(getDemandControlUri);
|
||||
return DemandControl.parse(response);
|
||||
}
|
||||
|
||||
public boolean setDemandControl(DemandControl info) throws DaikinCommunicationException {
|
||||
Map<String, String> queryParams = info.getParamString();
|
||||
String result = invoke(setDemandControlUri, queryParams);
|
||||
Map<String, String> responseMap = InfoParser.parse(result);
|
||||
return Optional.ofNullable(responseMap.get("ret")).orElse("").equals("OK");
|
||||
}
|
||||
|
||||
// Daikin Airbase API
|
||||
public AirbaseControlInfo getAirbaseControlInfo() throws DaikinCommunicationException {
|
||||
String response = invoke(getAirbaseControlInfoUri);
|
||||
|
@ -0,0 +1,187 @@
|
||||
/**
|
||||
* 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.daikin.internal.api;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.DemandControlMode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Class for holding the set of parameters used by set and get demand control info.
|
||||
*
|
||||
* @author Jimmy Tanagra - Initial Contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DemandControl {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DemandControl.class);
|
||||
|
||||
private static final List<String> DAYS = List.of("monday", "tuesday", "wednesday", "thursday", "friday", "saturday",
|
||||
"sunday");
|
||||
// create a map of "monday" -> "mo", "tuesday" -> "tu", etc.
|
||||
private static final Map<String, String> DAYS_ABBREVIATIONS = DAYS.stream()
|
||||
.map(day -> Map.entry(day, day.substring(0, 2)))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
private static Gson GSON = new Gson();
|
||||
|
||||
public String ret = "";
|
||||
|
||||
public DemandControlMode mode = DemandControlMode.AUTO;
|
||||
public int maxPower = 100;
|
||||
private Map<String, List<ScheduleEntry>> scheduleMap = new HashMap<>();
|
||||
|
||||
private DemandControl() {
|
||||
}
|
||||
|
||||
public String getSchedule() {
|
||||
return GSON.toJson(scheduleMap);
|
||||
}
|
||||
|
||||
public void setSchedule(String schedule) throws JsonSyntaxException {
|
||||
Map<String, List<ScheduleEntry>> parsedMap = GSON.fromJson(schedule,
|
||||
new TypeToken<Map<String, List<ScheduleEntry>>>() {
|
||||
}.getType());
|
||||
|
||||
if (DAYS.containsAll(parsedMap.keySet())) {
|
||||
scheduleMap = parsedMap;
|
||||
} else {
|
||||
throw new JsonSyntaxException("Invalid day(s) in JSON data");
|
||||
}
|
||||
}
|
||||
|
||||
public int getScheduledMaxPower() {
|
||||
return getScheduledMaxPower(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// Returns the current max_power setting based on the schedule
|
||||
// If there are no matching schedules for the current time,
|
||||
// it will search the last schedule of the previous non-empty day
|
||||
public int getScheduledMaxPower(LocalDateTime dateTime) {
|
||||
int todayIndex = dateTime.getDayOfWeek().getValue() - 1;
|
||||
String today = DAYS.get(todayIndex);
|
||||
int currentMinsFromMidnight = dateTime.toLocalTime().toSecondOfDay() / 60;
|
||||
|
||||
// search today's schedule for the last applicable schedule
|
||||
Optional<Integer> maxPower = scheduleMap.get(today).stream().filter(entry -> entry.enabled)
|
||||
.sorted((s1, s2) -> Integer.compare(s1.time, s2.time))
|
||||
.takeWhile(scheduleEntry -> scheduleEntry.time <= currentMinsFromMidnight)
|
||||
.reduce((first, second) -> second) // get the last entry that matches the condition
|
||||
.map(scheduleEntry -> scheduleEntry.power).or(() -> {
|
||||
// there are no matching schedules today, so
|
||||
// get the last entry of the previous non-empty schedule day,
|
||||
// wrapping around the DAYS array if necessary
|
||||
|
||||
int currentIndex = todayIndex > 0 ? (todayIndex - 1) : (DAYS.size() - 1);
|
||||
while (currentIndex != todayIndex) {
|
||||
String prevDay = DAYS.get(currentIndex);
|
||||
List<ScheduleEntry> prevDaySchedules = scheduleMap.get(prevDay).stream()
|
||||
.filter(entry -> entry.enabled).sorted((s1, s2) -> Integer.compare(s1.time, s2.time))
|
||||
.toList();
|
||||
if (!prevDaySchedules.isEmpty()) {
|
||||
return Optional.of(prevDaySchedules.get(prevDaySchedules.size() - 1).power);
|
||||
}
|
||||
currentIndex = currentIndex > 0 ? (currentIndex - 1) : (DAYS.size() - 1);
|
||||
}
|
||||
|
||||
// if previous days have no schedules, use today's last schedule if any
|
||||
return scheduleMap.get(today).stream().filter(entry -> entry.enabled)
|
||||
.sorted((s1, s2) -> Integer.compare(s1.time, s2.time)).reduce((first, second) -> second)
|
||||
.map(scheduleEntry -> scheduleEntry.power);
|
||||
});
|
||||
|
||||
return maxPower.map(value -> value == 0 ? 100 : value) // a maxPower of 0 means the demand control is disabled,
|
||||
// so return 100
|
||||
.orElse(100); // return 100 also for no schedules
|
||||
}
|
||||
|
||||
public static DemandControl parse(String response) {
|
||||
LOGGER.trace("Parsing string: \"{}\"", response);
|
||||
|
||||
Map<String, String> responseMap = InfoParser.parse(response);
|
||||
|
||||
DemandControl info = new DemandControl();
|
||||
info.ret = responseMap.getOrDefault("ret", "");
|
||||
boolean enabled = "1".equals(responseMap.get("en_demand"));
|
||||
if (!enabled) {
|
||||
info.mode = DemandControlMode.OFF;
|
||||
} else {
|
||||
info.mode = DemandControlMode.fromValue(responseMap.getOrDefault("mode", "-"));
|
||||
}
|
||||
info.maxPower = Objects.requireNonNull(Optional.ofNullable(responseMap.get("max_pow"))
|
||||
.flatMap(value -> InfoParser.parseInt(value)).orElse(100));
|
||||
|
||||
info.scheduleMap = DAYS_ABBREVIATIONS.entrySet().stream().map(day -> {
|
||||
final String dayName = day.getKey();
|
||||
final String dayPrefix = day.getValue();
|
||||
|
||||
final int dayCount = Objects.requireNonNull(Optional.ofNullable(responseMap.get(dayPrefix + "c"))
|
||||
.flatMap(value -> InfoParser.parseInt(value)).orElse(0));
|
||||
|
||||
// We don't want to sort the entries by time here, to preserve the same order from the response
|
||||
List<ScheduleEntry> schedules = Stream.iterate(1, i -> i <= dayCount, i -> i + 1).map(i -> {
|
||||
String prefix = dayPrefix + i + "_";
|
||||
return new ScheduleEntry("1".equals(responseMap.get(prefix + "en")),
|
||||
Objects.requireNonNull(Optional.ofNullable(responseMap.get(prefix + "t"))
|
||||
.flatMap(value -> InfoParser.parseInt(value)).orElse(0)),
|
||||
Objects.requireNonNull(Optional.ofNullable(responseMap.get(prefix + "p"))
|
||||
.flatMap(value -> InfoParser.parseInt(value)).orElse(0)));
|
||||
}).toList();
|
||||
|
||||
return Map.entry(dayName, schedules);
|
||||
}).collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public Map<String, String> getParamString() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("en_demand", mode == DemandControlMode.OFF ? "0" : "1");
|
||||
if (mode != DemandControlMode.OFF) {
|
||||
params.put("mode", mode.getValue());
|
||||
params.put("max_pow", Integer.toString(maxPower));
|
||||
DAYS.stream().forEach(day -> {
|
||||
String dayPrefix = DAYS_ABBREVIATIONS.get(day);
|
||||
List<ScheduleEntry> schedules = scheduleMap.getOrDefault(day, List.of());
|
||||
params.put(dayPrefix + "c", Integer.toString(schedules.size()));
|
||||
for (int i = 0; i < schedules.size(); i++) {
|
||||
ScheduleEntry schedule = schedules.get(i);
|
||||
String prefix = dayPrefix + (i + 1) + "_";
|
||||
params.put(prefix + "en", schedule.enabled ? "1" : "0");
|
||||
params.put(prefix + "t", Integer.toString(schedule.time));
|
||||
params.put(prefix + "p", Integer.toString(schedule.power));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// package private for testing
|
||||
record ScheduleEntry(boolean enabled, int time, int power) {
|
||||
}
|
||||
}
|
@ -209,4 +209,43 @@ public class Enums {
|
||||
return NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DemandControlMode {
|
||||
OFF("-"),
|
||||
MANUAL("0"),
|
||||
SCHEDULED("1"),
|
||||
AUTO("2");
|
||||
|
||||
private final String value;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DemandControlMode.class);
|
||||
|
||||
DemandControlMode(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static boolean isValidValue(String value) {
|
||||
for (DemandControlMode m : DemandControlMode.values()) {
|
||||
if (m.getValue().equals(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static DemandControlMode fromValue(String value) {
|
||||
for (DemandControlMode m : DemandControlMode.values()) {
|
||||
if (m.getValue().equals(value)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
LOGGER.debug("Unexpected DemandControlMode value of \"{}\"", value);
|
||||
|
||||
// Default to off
|
||||
return OFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,10 @@ import org.openhab.binding.daikin.internal.DaikinBindingConstants;
|
||||
import org.openhab.binding.daikin.internal.DaikinCommunicationException;
|
||||
import org.openhab.binding.daikin.internal.DaikinDynamicStateDescriptionProvider;
|
||||
import org.openhab.binding.daikin.internal.api.ControlInfo;
|
||||
import org.openhab.binding.daikin.internal.api.DemandControl;
|
||||
import org.openhab.binding.daikin.internal.api.EnergyInfoDayAndWeek;
|
||||
import org.openhab.binding.daikin.internal.api.EnergyInfoYear;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.DemandControlMode;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.FanMovement;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.FanSpeed;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.HomekitMode;
|
||||
@ -34,6 +36,7 @@ import org.openhab.binding.daikin.internal.api.Enums.SpecialMode;
|
||||
import org.openhab.binding.daikin.internal.api.SensorInfo;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
@ -45,6 +48,8 @@ import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* Handles communicating with a Daikin air conditioning unit.
|
||||
*
|
||||
@ -59,6 +64,9 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(DaikinAcUnitHandler.class);
|
||||
|
||||
private Optional<Integer> autoModeValue = Optional.empty();
|
||||
private boolean pollDemandControl = true;
|
||||
private Optional<String> savedDemandControlSchedule = Optional.empty();
|
||||
private Optional<Integer> savedDemandControlMaxPower = Optional.empty();
|
||||
|
||||
public DaikinAcUnitHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider,
|
||||
@Nullable HttpClient httpClient) {
|
||||
@ -153,6 +161,29 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
|
||||
// Suppress any error if energy info is not supported.
|
||||
logger.debug("getEnergyInfoDayAndWeek() error: {}", e.getMessage());
|
||||
}
|
||||
|
||||
if (pollDemandControl) {
|
||||
try {
|
||||
DemandControl demandInfo = webTargets.getDemandControl();
|
||||
String schedule = demandInfo.getSchedule();
|
||||
int maxPower = demandInfo.maxPower;
|
||||
|
||||
if (demandInfo.mode == DemandControlMode.SCHEDULED) {
|
||||
savedDemandControlSchedule = Optional.of(schedule);
|
||||
maxPower = demandInfo.getScheduledMaxPower();
|
||||
} else if (demandInfo.mode == DemandControlMode.MANUAL) {
|
||||
savedDemandControlMaxPower = Optional.of(demandInfo.maxPower);
|
||||
}
|
||||
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_MODE, new StringType(demandInfo.mode.name()));
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_MAX_POWER, new PercentType(maxPower));
|
||||
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_SCHEDULE, new StringType(schedule));
|
||||
} catch (DaikinCommunicationException e) {
|
||||
// Suppress any error if demand control is not supported.
|
||||
logger.debug("getDemandControl() error: {}", e.getMessage());
|
||||
pollDemandControl = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -177,6 +208,24 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DaikinBindingConstants.CHANNEL_AC_DEMAND_MODE:
|
||||
if (command instanceof StringType stringCommand) {
|
||||
changeDemandMode(stringCommand.toString());
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DaikinBindingConstants.CHANNEL_AC_DEMAND_MAX_POWER:
|
||||
if (command instanceof PercentType percentCommand) {
|
||||
changeDemandMaxPower(percentCommand.intValue());
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DaikinBindingConstants.CHANNEL_AC_DEMAND_SCHEDULE:
|
||||
if (command instanceof StringType stringCommand) {
|
||||
changeDemandSchedule(stringCommand.toString());
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -265,6 +314,51 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
|
||||
webTargets.setStreamerMode(streamerMode);
|
||||
}
|
||||
|
||||
protected void changeDemandMode(String mode) throws DaikinCommunicationException {
|
||||
DemandControlMode newMode;
|
||||
try {
|
||||
newMode = DemandControlMode.valueOf(mode);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("Invalid demand mode: {}. Valid values: {}", mode, DemandControlMode.values());
|
||||
return;
|
||||
}
|
||||
DemandControl demandInfo = webTargets.getDemandControl();
|
||||
if (demandInfo.mode != newMode) {
|
||||
if (newMode == DemandControlMode.SCHEDULED && savedDemandControlSchedule.isPresent()) {
|
||||
// restore previously saved schedule
|
||||
demandInfo.setSchedule(savedDemandControlSchedule.get());
|
||||
}
|
||||
|
||||
if (newMode == DemandControlMode.MANUAL && savedDemandControlMaxPower.isPresent()) {
|
||||
// restore previously saved maxPower
|
||||
demandInfo.maxPower = savedDemandControlMaxPower.get();
|
||||
}
|
||||
}
|
||||
demandInfo.mode = newMode;
|
||||
webTargets.setDemandControl(demandInfo);
|
||||
}
|
||||
|
||||
protected void changeDemandMaxPower(int maxPower) throws DaikinCommunicationException {
|
||||
DemandControl demandInfo = webTargets.getDemandControl();
|
||||
demandInfo.mode = DemandControlMode.MANUAL;
|
||||
demandInfo.maxPower = maxPower;
|
||||
webTargets.setDemandControl(demandInfo);
|
||||
savedDemandControlMaxPower = Optional.of(maxPower);
|
||||
}
|
||||
|
||||
protected void changeDemandSchedule(String schedule) throws DaikinCommunicationException {
|
||||
DemandControl demandInfo = webTargets.getDemandControl();
|
||||
try {
|
||||
demandInfo.setSchedule(schedule);
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.warn("Invalid schedule: {}. {}", schedule, e.getMessage());
|
||||
return;
|
||||
}
|
||||
demandInfo.mode = DemandControlMode.SCHEDULED;
|
||||
webTargets.setDemandControl(demandInfo);
|
||||
savedDemandControlSchedule = Optional.of(demandInfo.getSchedule());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates energy year channels. Values are provided in hundreds of Watt
|
||||
*
|
||||
|
@ -27,8 +27,24 @@ thing-type.config.daikin.config.uuid.description = A unique UUID for authenticat
|
||||
|
||||
channel-type.daikin.acunit-cmpfrequency.label = Compressor Frequency
|
||||
channel-type.daikin.acunit-cmpfrequency.description = Current compressor frequency
|
||||
channel-type.daikin.acunit-demandcontrolmaxpower.label = Demand Control Max Power
|
||||
channel-type.daikin.acunit-demandcontrolmaxpower.description = The maximum power for demand control in percent. Allowed range is between 40% and 100% in increments of 5%.
|
||||
channel-type.daikin.acunit-demandcontrolmode.label = Demand Control Mode
|
||||
channel-type.daikin.acunit-demandcontrolmode.description = The demand control mode
|
||||
channel-type.daikin.acunit-demandcontrolmode.state.option.OFF = Off
|
||||
channel-type.daikin.acunit-demandcontrolmode.state.option.AUTO = Auto
|
||||
channel-type.daikin.acunit-demandcontrolmode.state.option.SCHEDULED = Scheduled
|
||||
channel-type.daikin.acunit-demandcontrolmode.state.option.MANUAL = Manual
|
||||
channel-type.daikin.acunit-demandcontrolschedule.label = Demand Control Schedule
|
||||
channel-type.daikin.acunit-demandcontrolschedule.description = The demand control schedule in JSON format.
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-1.label = Energy Cooling Current Year January
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-1.description = The energy usage for cooling this year January
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-10.label = Energy Cooling Current Year October
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-10.description = The energy usage for cooling this year October
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-11.label = Energy Cooling Current Year November
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-11.description = The energy usage for cooling this year November
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-12.label = Energy Cooling Current Year December
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-12.description = The energy usage for cooling this year December
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-2.label = Energy Cooling Current Year February
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-2.description = The energy usage for cooling this year February
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-3.label = Energy Cooling Current Year March
|
||||
@ -45,12 +61,6 @@ channel-type.daikin.acunit-energycoolingcurrentyear-8.label = Energy Cooling Cur
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-8.description = The energy usage for cooling this year August
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-9.label = Energy Cooling Current Year September
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-9.description = The energy usage for cooling this year September
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-10.label = Energy Cooling Current Year October
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-10.description = The energy usage for cooling this year October
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-11.label = Energy Cooling Current Year November
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-11.description = The energy usage for cooling this year November
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-12.label = Energy Cooling Current Year December
|
||||
channel-type.daikin.acunit-energycoolingcurrentyear-12.description = The energy usage for cooling this year December
|
||||
channel-type.daikin.acunit-energycoolinglastweek.label = Energy Cooling Last Week
|
||||
channel-type.daikin.acunit-energycoolinglastweek.description = The energy usage for cooling last week
|
||||
channel-type.daikin.acunit-energycoolingthisweek.label = Energy Cooling This Week
|
||||
@ -59,6 +69,12 @@ channel-type.daikin.acunit-energycoolingtoday.label = Energy Cooling Today
|
||||
channel-type.daikin.acunit-energycoolingtoday.description = The energy usage for cooling today
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-1.label = Energy Heating Current Year January
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-1.description = The energy usage for heating this year January
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-10.label = Energy Heating Current Year October
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-10.description = The energy usage for heating this year October
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-11.label = Energy Heating Current Year November
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-11.description = The energy usage for heating this year November
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-12.label = Energy Heating Current Year December
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-12.description = The energy usage for heating this year December
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-2.label = Energy Heating Current Year February
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-2.description = The energy usage for heating this year February
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-3.label = Energy Heating Current Year March
|
||||
@ -75,12 +91,6 @@ channel-type.daikin.acunit-energyheatingcurrentyear-8.label = Energy Heating Cur
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-8.description = The energy usage for heating this year August
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-9.label = Energy Heating Current Year September
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-9.description = The energy usage for heating this year September
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-10.label = Energy Heating Current Year October
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-10.description = The energy usage for heating this year October
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-11.label = Energy Heating Current Year November
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-11.description = The energy usage for heating this year November
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-12.label = Energy Heating Current Year December
|
||||
channel-type.daikin.acunit-energyheatingcurrentyear-12.description = The energy usage for heating this year December
|
||||
channel-type.daikin.acunit-energyheatinglastweek.label = Energy Heating Last Week
|
||||
channel-type.daikin.acunit-energyheatinglastweek.description = The energy usage for heating last week
|
||||
channel-type.daikin.acunit-energyheatingthisweek.label = Energy Heating This Week
|
||||
|
@ -51,8 +51,14 @@
|
||||
<channel id="energycoolingcurrentyear-10" typeId="acunit-energycoolingcurrentyear-10"></channel>
|
||||
<channel id="energycoolingcurrentyear-11" typeId="acunit-energycoolingcurrentyear-11"></channel>
|
||||
<channel id="energycoolingcurrentyear-12" typeId="acunit-energycoolingcurrentyear-12"></channel>
|
||||
<channel id="demandcontrolmode" typeId="acunit-demandcontrolmode"></channel>
|
||||
<channel id="demandcontrolmaxpower" typeId="acunit-demandcontrolmaxpower"></channel>
|
||||
<channel id="demandcontrolschedule" typeId="acunit-demandcontrolschedule"></channel>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
<representation-property>host</representation-property>
|
||||
<config-description-ref uri="thing-type:daikin:config"/>
|
||||
</thing-type>
|
||||
@ -424,6 +430,32 @@
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="acunit-demandcontrolmode" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Demand Control Mode</label>
|
||||
<description>The demand control mode</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="OFF">Off</option>
|
||||
<option value="AUTO">Auto</option>
|
||||
<option value="SCHEDULED">Scheduled</option>
|
||||
<option value="MANUAL">Manual</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="acunit-demandcontrolmaxpower" advanced="true">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Demand Control Max Power</label>
|
||||
<description>The maximum power for demand control in percent. Allowed range is between 40% and 100% in increments of
|
||||
5%.</description>
|
||||
<state pattern="%d %%" min="40" max="100" step="5"></state>
|
||||
</channel-type>
|
||||
<channel-type id="acunit-demandcontrolschedule" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Demand Control Schedule</label>
|
||||
<description>The demand control schedule in JSON format.</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="airbase-acunit-fan">
|
||||
<item-type>String</item-type>
|
||||
<label>Fan</label>
|
||||
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||
|
||||
<thing-type uid="daikin:ac_unit">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="demandcontrolmode">
|
||||
<type>daikin:acunit-demandcontrolmode</type>
|
||||
</add-channel>
|
||||
<add-channel id="demandcontrolmaxpower">
|
||||
<type>daikin:acunit-demandcontrolmaxpower</type>
|
||||
</add-channel>
|
||||
<add-channel id="demandcontrolschedule">
|
||||
<type>daikin:acunit-demandcontrolschedule</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
</update:update-descriptions>
|
@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.FanMovement;
|
||||
|
||||
/**
|
||||
* This class provides tests for deconz lights
|
||||
* This class provides tests for the ControlInfo class
|
||||
*
|
||||
* @author Leo Siepel - Initial contribution
|
||||
*
|
||||
|
@ -0,0 +1,388 @@
|
||||
/**
|
||||
* 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.daikin.internal.api;
|
||||
|
||||
import static java.time.DayOfWeek.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.binding.daikin.internal.api.DemandControl.ScheduleEntry;
|
||||
import org.openhab.binding.daikin.internal.api.Enums.DemandControlMode;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* This class provides tests for the DemandControl class
|
||||
*
|
||||
* @author Jimmy Tanagra - Initial contribution
|
||||
*
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class DemandControlTest {
|
||||
|
||||
public static Stream<Arguments> parserTest() {
|
||||
return Stream.of( //
|
||||
Arguments.of("ret=OK,type=1,en_demand=0,mode=0,max_pow=100", DemandControlMode.OFF, 100),
|
||||
Arguments.of("ret=OK,type=1,en_demand=1,mode=0,max_pow=100", DemandControlMode.MANUAL, 100),
|
||||
Arguments.of("ret=OK,type=1,en_demand=0,mode=1,max_pow=100", DemandControlMode.OFF, 100),
|
||||
Arguments.of("ret=OK,type=1,en_demand=1,mode=1,max_pow=100", DemandControlMode.SCHEDULED, 100),
|
||||
Arguments.of("ret=OK,type=1,en_demand=0,mode=2,max_pow=100", DemandControlMode.OFF, 100),
|
||||
Arguments.of("ret=OK,type=1,en_demand=1,mode=2,max_pow=100", DemandControlMode.AUTO, 100),
|
||||
Arguments.of("ret=OK,type=1,en_demand=0,mode=0,max_pow=50", DemandControlMode.OFF, 50),
|
||||
Arguments.of("ret=OK,type=1,en_demand=0,mode=0,max_pow=40", DemandControlMode.OFF, 40),
|
||||
|
||||
// Invalid inputs - defaults
|
||||
Arguments.of("ret=OK,type=1,en_demand=,mode=,max_pow=", DemandControlMode.OFF, 100)
|
||||
//
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void parserTest(String input, DemandControlMode expectedMode, int expectedMaxPower) {
|
||||
DemandControl info = DemandControl.parse(input);
|
||||
|
||||
// assert
|
||||
assertEquals(expectedMode, info.mode);
|
||||
assertEquals(expectedMaxPower, info.maxPower);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> inputScheduleParserTest() {
|
||||
return Stream.of( //
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=0,mode=0,max_pow=100,scdl_per_day=4,moc=0,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
Map.of("monday", List.of(), "tuesday", List.of(), "wednesday", List.of(), "thursday", List.of(),
|
||||
"friday", List.of(), "saturday", List.of(), "sunday", List.of())),
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,moc=3,mo1_en=1,mo1_t=720,mo1_p=90,mo2_en=1,mo2_t=840,mo2_p=0,mo3_en=1,mo3_t=600,mo3_p=70,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
Map.of("monday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70)),
|
||||
"tuesday", List.of(), "wednesday", List.of(), "thursday", List.of(), "friday",
|
||||
List.of(), "saturday", List.of(), "sunday", List.of())),
|
||||
// added mo4_xxx but moc=3
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,moc=3,mo1_en=1,mo1_t=720,mo1_p=90,mo2_en=1,mo2_t=840,mo2_p=0,mo3_en=1,mo3_t=600,mo3_p=70,mo4_en=0,mo4_t=30,mo4_p=0",
|
||||
Map.of("monday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70)),
|
||||
"tuesday", List.of(), "wednesday", List.of(), "thursday", List.of(), "friday",
|
||||
List.of(), "saturday", List.of(), "sunday", List.of())),
|
||||
// this time moc=4
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,moc=4,mo1_en=1,mo1_t=720,mo1_p=90,mo2_en=1,mo2_t=840,mo2_p=0,mo3_en=1,mo3_t=600,mo3_p=70,mo4_en=0,mo4_t=30,mo4_p=0",
|
||||
Map.of("monday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70), new ScheduleEntry(false, 30, 0)),
|
||||
"tuesday", List.of(), "wednesday", List.of(), "thursday", List.of(), "friday",
|
||||
List.of(), "saturday", List.of(), "sunday", List.of())),
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,tuc=4,tu1_en=1,tu1_t=720,tu1_p=90,tu2_en=1,tu2_t=840,tu2_p=0,tu3_en=1,tu3_t=600,tu3_p=70,tu4_en=0,tu4_t=30,tu4_p=0",
|
||||
Map.of("tuesday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70), new ScheduleEntry(false, 30, 0)),
|
||||
"monday", List.of(), "wednesday", List.of(), "thursday", List.of(), "friday", List.of(),
|
||||
"saturday", List.of(), "sunday", List.of())),
|
||||
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,wec=4,we1_en=1,we1_t=720,we1_p=90,we2_en=1,we2_t=840,we2_p=0,we3_en=1,we3_t=600,we3_p=70,we4_en=0,we4_t=30,we4_p=0",
|
||||
Map.of("wednesday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70), new ScheduleEntry(false, 30, 0)),
|
||||
"monday", List.of(), "tuesday", List.of(), "thursday", List.of(), "friday", List.of(),
|
||||
"saturday", List.of(), "sunday", List.of())),
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,thc=4,th1_en=1,th1_t=720,th1_p=90,th2_en=1,th2_t=840,th2_p=0,th3_en=1,th3_t=600,th3_p=70,th4_en=0,th4_t=30,th4_p=0",
|
||||
Map.of("thursday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70), new ScheduleEntry(false, 30, 0)),
|
||||
"monday", List.of(), "tuesday", List.of(), "wednesday", List.of(), "friday", List.of(),
|
||||
"saturday", List.of(), "sunday", List.of())),
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,frc=4,fr1_en=1,fr1_t=720,fr1_p=90,fr2_en=1,fr2_t=840,fr2_p=0,fr3_en=1,fr3_t=600,fr3_p=70,fr4_en=0,fr4_t=30,fr4_p=0",
|
||||
Map.of("friday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70), new ScheduleEntry(false, 30, 0)),
|
||||
"monday", List.of(), "tuesday", List.of(), "wednesday", List.of(), "thursday",
|
||||
List.of(), "saturday", List.of(), "sunday", List.of())),
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,sac=4,sa1_en=1,sa1_t=720,sa1_p=90,sa2_en=1,sa2_t=840,sa2_p=0,sa3_en=1,sa3_t=600,sa3_p=70,sa4_en=0,sa4_t=30,sa4_p=0",
|
||||
Map.of("saturday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70), new ScheduleEntry(false, 30, 0)),
|
||||
"monday", List.of(), "tuesday", List.of(), "wednesday", List.of(), "thursday",
|
||||
List.of(), "friday", List.of(), "sunday", List.of())),
|
||||
Arguments.of(
|
||||
"ret=OK,type=1,en_demand=1,mode=1,max_pow=100,scdl_per_day=4,suc=4,su1_en=1,su1_t=720,su1_p=90,su2_en=1,su2_t=840,su2_p=0,su3_en=1,su3_t=600,su3_p=70,su4_en=0,su4_t=30,su4_p=0",
|
||||
Map.of("sunday",
|
||||
List.of(new ScheduleEntry(true, 720, 90), new ScheduleEntry(true, 840, 0),
|
||||
new ScheduleEntry(true, 600, 70), new ScheduleEntry(false, 30, 0)),
|
||||
"monday", List.of(), "tuesday", List.of(), "wednesday", List.of(), "thursday",
|
||||
List.of(), "friday", List.of(), "saturday", List.of()))
|
||||
|
||||
//
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void inputScheduleParserTest(String input, Map<String, List<ScheduleEntry>> expectedSchedule) {
|
||||
DemandControl info = DemandControl.parse(input);
|
||||
|
||||
var parsedJsonObject = parseJson(info.getSchedule());
|
||||
|
||||
String expectedJsonString = new Gson().toJson(expectedSchedule);
|
||||
var expectedJsonObject = parseJson(expectedJsonString);
|
||||
|
||||
// assert
|
||||
assertEquals(expectedJsonObject, parsedJsonObject);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> jsonScheduleToParamStringTest() {
|
||||
return Stream.of( //
|
||||
Arguments.of( //
|
||||
"""
|
||||
{
|
||||
"monday": [
|
||||
{"enabled":true,"time":720,"power":90},
|
||||
{"enabled":true,"time":840,"power":0},
|
||||
{"enabled":false,"time":600,"power":70},
|
||||
{"enabled":true,"time":300,"power":50}
|
||||
],
|
||||
"tuesday":[],"wednesday":[],"thursday":[],"friday":[],"saturday":[],"sunday":[]
|
||||
}
|
||||
""", //
|
||||
"moc=4," + //
|
||||
"mo1_en=1,mo1_t=720,mo1_p=90," + //
|
||||
"mo2_en=1,mo2_t=840,mo2_p=0," + //
|
||||
"mo3_en=0,mo3_t=600,mo3_p=70," + //
|
||||
"mo4_en=1,mo4_t=300,mo4_p=50," + //
|
||||
"tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0" //
|
||||
), //
|
||||
Arguments.of( //
|
||||
"""
|
||||
{
|
||||
"tuesday": [
|
||||
{"enabled":true,"time":720,"power":90},
|
||||
{"enabled":true,"time":840,"power":0},
|
||||
{"enabled":false,"time":600,"power":70},
|
||||
{"enabled":true,"time":300,"power":50}
|
||||
],
|
||||
"monday":[],"wednesday":[],"thursday":[],"friday":[],"saturday":[],"sunday":[]
|
||||
}
|
||||
""", //
|
||||
"tuc=4," + //
|
||||
"tu1_en=1,tu1_t=720,tu1_p=90," + //
|
||||
"tu2_en=1,tu2_t=840,tu2_p=0," + //
|
||||
"tu3_en=0,tu3_t=600,tu3_p=70," + //
|
||||
"tu4_en=1,tu4_t=300,tu4_p=50," + //
|
||||
"moc=0,wec=0,thc=0,frc=0,sac=0,suc=0" //
|
||||
), //
|
||||
Arguments.of( //
|
||||
"""
|
||||
{
|
||||
"wednesday": [
|
||||
{"enabled":true,"time":720,"power":90},
|
||||
{"enabled":true,"time":840,"power":0},
|
||||
{"enabled":false,"time":600,"power":70},
|
||||
{"enabled":true,"time":300,"power":50}
|
||||
],
|
||||
"monday":[],"tuesday":[],"thursday":[],"friday":[],"saturday":[],"sunday":[]
|
||||
}
|
||||
""", //
|
||||
"wec=4," + //
|
||||
"we1_en=1,we1_t=720,we1_p=90," + //
|
||||
"we2_en=1,we2_t=840,we2_p=0," + //
|
||||
"we3_en=0,we3_t=600,we3_p=70," + //
|
||||
"we4_en=1,we4_t=300,we4_p=50," + //
|
||||
"moc=0,tuc=0,thc=0,frc=0,sac=0,suc=0" //
|
||||
), //
|
||||
Arguments.of( //
|
||||
"""
|
||||
{
|
||||
"thursday": [
|
||||
{"enabled":true,"time":720,"power":90},
|
||||
{"enabled":true,"time":840,"power":0},
|
||||
{"enabled":false,"time":600,"power":70},
|
||||
{"enabled":true,"time":300,"power":50}
|
||||
],
|
||||
"monday":[],"tuesday":[],"wednesday":[],"friday":[],"saturday":[],"sunday":[]
|
||||
}
|
||||
""", //
|
||||
"thc=4," + //
|
||||
"th1_en=1,th1_t=720,th1_p=90," + //
|
||||
"th2_en=1,th2_t=840,th2_p=0," + //
|
||||
"th3_en=0,th3_t=600,th3_p=70," + //
|
||||
"th4_en=1,th4_t=300,th4_p=50," + //
|
||||
"moc=0,tuc=0,wec=0,frc=0,sac=0,suc=0" //
|
||||
), //
|
||||
Arguments.of( //
|
||||
"""
|
||||
{
|
||||
"friday": [
|
||||
{"enabled":true,"time":720,"power":90},
|
||||
{"enabled":true,"time":840,"power":0},
|
||||
{"enabled":false,"time":600,"power":70},
|
||||
{"enabled":true,"time":300,"power":50}
|
||||
],
|
||||
"monday":[],"tuesday":[],"wednesday":[],"thursday":[],"saturday":[],"sunday":[]
|
||||
}
|
||||
""", //
|
||||
"frc=4," + //
|
||||
"fr1_en=1,fr1_t=720,fr1_p=90," + //
|
||||
"fr2_en=1,fr2_t=840,fr2_p=0," + //
|
||||
"fr3_en=0,fr3_t=600,fr3_p=70," + //
|
||||
"fr4_en=1,fr4_t=300,fr4_p=50," + //
|
||||
"moc=0,tuc=0,thc=0,wec=0,sac=0,suc=0" //
|
||||
), //
|
||||
Arguments.of( //
|
||||
"""
|
||||
{
|
||||
"saturday": [
|
||||
{"enabled":true,"time":720,"power":90},
|
||||
{"enabled":true,"time":840,"power":0},
|
||||
{"enabled":false,"time":600,"power":70},
|
||||
{"enabled":true,"time":300,"power":50}
|
||||
],
|
||||
"monday":[],"tuesday":[],"wednesday":[],"thursday":[],"friday":[],"sunday":[]
|
||||
}
|
||||
""", //
|
||||
"sac=4," + //
|
||||
"sa1_en=1,sa1_t=720,sa1_p=90," + //
|
||||
"sa2_en=1,sa2_t=840,sa2_p=0," + //
|
||||
"sa3_en=0,sa3_t=600,sa3_p=70," + //
|
||||
"sa4_en=1,sa4_t=300,sa4_p=50," + //
|
||||
"moc=0,tuc=0,thc=0,frc=0,wec=0,suc=0" //
|
||||
), //
|
||||
Arguments.of( //
|
||||
"""
|
||||
{
|
||||
"sunday": [
|
||||
{"enabled":true,"time":720,"power":90},
|
||||
{"enabled":true,"time":840,"power":0},
|
||||
{"enabled":false,"time":600,"power":70},
|
||||
{"enabled":true,"time":300,"power":50}
|
||||
],
|
||||
"monday":[],"tuesday":[],"thursday":[],"friday":[],"saturday":[],"wednesday":[]
|
||||
}
|
||||
""", //
|
||||
"suc=4," + //
|
||||
"su1_en=1,su1_t=720,su1_p=90," + //
|
||||
"su2_en=1,su2_t=840,su2_p=0," + //
|
||||
"su3_en=0,su3_t=600,su3_p=70," + //
|
||||
"su4_en=1,su4_t=300,su4_p=50," + //
|
||||
"moc=0,tuc=0,thc=0,frc=0,sac=0,wec=0" //
|
||||
)//
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void jsonScheduleToParamStringTest(String scheduleJson, String expectedParamString) {
|
||||
DemandControl info = DemandControl.parse("ret=OK,type=1,en_demand=1,mode=1");
|
||||
Map<String, String> expectedParamMap = InfoParser.parse(expectedParamString);
|
||||
|
||||
info.setSchedule(scheduleJson);
|
||||
|
||||
Map<String, String> paramMap = info.getParamString();
|
||||
|
||||
expectedParamMap.entrySet().stream().forEach(expectedParam -> assertThat(paramMap,
|
||||
hasEntry(is(expectedParam.getKey()), is(expectedParam.getValue()))));
|
||||
}
|
||||
|
||||
private @Nullable Map<String, List<ScheduleEntry>> parseJson(String json) {
|
||||
return new Gson().fromJson(json, new TypeToken<Map<String, List<ScheduleEntry>>>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
public static Stream<Arguments> scheduledMaxPowerTest() {
|
||||
return Stream.of( //
|
||||
// empty schedule
|
||||
Arguments.of("moc=0,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0", MONDAY, "12:00", 100),
|
||||
// within the schedule of the day
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "10:00", 60),
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "10:05", 60),
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "12:00", 70),
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "15:00", 80),
|
||||
// it should ignore disabled schedules
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=0,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "15:00", 70),
|
||||
// earlier than first schedule of the day, must look back and find the last schedule
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=1,fr1_en=1,fr1_t=10,fr1_p=77,sac=0,suc=0",
|
||||
MONDAY, "08:00", 77),
|
||||
// test for boundary conditions (last item on the list, ie. sunday)
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=1,su1_en=1,su1_t=10,su1_p=77",
|
||||
MONDAY, "08:00", 77),
|
||||
// earlier than first schedule of the day, no other days have schedules,
|
||||
// so wrap around and pick the last schedule of the same day
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "08:00", 80),
|
||||
// but also ignore disabled schedules
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=0,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "08:00", 70),
|
||||
// empty schedule for the day, so look back until we find the last schedule
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=1,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
WEDNESDAY, "15:00", 80),
|
||||
// it should also ignore disabled schedules in the previous days
|
||||
Arguments.of(
|
||||
"moc=3,mo1_en=1,mo1_t=720,mo1_p=70,mo2_en=0,mo2_t=840,mo2_p=80,mo3_en=1,mo3_t=600,mo3_p=60,tuc=0,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
WEDNESDAY, "15:00", 70),
|
||||
// it should wrap around and start search from the end of the week
|
||||
Arguments.of(
|
||||
"moc=0,tuc=3,tu1_en=1,tu1_t=720,tu1_p=70,tu2_en=1,tu2_t=840,tu2_p=80,tu3_en=1,tu3_t=600,tu3_p=60,wec=0,thc=0,frc=0,sac=0,suc=0",
|
||||
MONDAY, "15:00", 80)
|
||||
//
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void scheduledMaxPowerTest(String input, DayOfWeek dow, String time, int expectedMaxPower) {
|
||||
DemandControl info = DemandControl.parse(input);
|
||||
|
||||
LocalDateTime dateTime = LocalDateTime.now().with(java.time.temporal.TemporalAdjusters.next(dow))
|
||||
.with(java.time.LocalTime.parse(time));
|
||||
|
||||
assertEquals(expectedMaxPower, info.getScheduledMaxPower(dateTime));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user