2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mybmw.internal.utils;
15 import static org.openhab.binding.mybmw.internal.utils.ChargeProfileWrapper.ProfileKey.*;
16 import static org.openhab.binding.mybmw.internal.utils.Constants.*;
18 import java.time.DayOfWeek;
19 import java.time.LocalTime;
20 import java.time.format.DateTimeParseException;
21 import java.util.ArrayList;
22 import java.util.EnumSet;
23 import java.util.HashMap;
25 import java.util.Optional;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.mybmw.internal.MyBMWConstants.ChargingMode;
31 import org.openhab.binding.mybmw.internal.MyBMWConstants.ChargingPreference;
32 import org.openhab.binding.mybmw.internal.dto.charge.ChargeProfile;
33 import org.openhab.binding.mybmw.internal.dto.charge.ChargingSettings;
34 import org.openhab.binding.mybmw.internal.dto.charge.ChargingWindow;
35 import org.openhab.binding.mybmw.internal.dto.charge.Time;
36 import org.openhab.binding.mybmw.internal.dto.charge.Timer;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * The {@link ChargeProfileWrapper} Wrapper for ChargeProfiles
43 * @author Bernd Weymann - Initial contribution
44 * @author Norbert Truchsess - add ChargeProfileActions
47 public class ChargeProfileWrapper {
48 private static final Logger LOGGER = LoggerFactory.getLogger(ChargeProfileWrapper.class);
50 private static final String CHARGING_WINDOW = "chargingWindow";
51 private static final String WEEKLY_PLANNER = "weeklyPlanner";
52 private static final String ACTIVATE = "activate";
53 private static final String DEACTIVATE = "deactivate";
55 public enum ProfileKey {
65 private Optional<ChargingMode> mode = Optional.empty();
66 private Optional<ChargingPreference> preference = Optional.empty();
67 private Optional<String> controlType = Optional.empty();
68 private Optional<ChargingSettings> chargeSettings = Optional.empty();
70 private final Map<ProfileKey, Boolean> enabled = new HashMap<>();
71 private final Map<ProfileKey, LocalTime> times = new HashMap<>();
72 private final Map<ProfileKey, Set<DayOfWeek>> daysOfWeek = new HashMap<>();
74 public ChargeProfileWrapper(final ChargeProfile profile) {
75 setPreference(profile.chargingPreference);
76 setMode(profile.chargingMode);
77 controlType = Optional.of(profile.chargingControlType);
78 chargeSettings = Optional.of(profile.chargingSettings);
79 setEnabled(CLIMATE, profile.climatisationOn);
81 addTimer(TIMER1, profile.getTimerId(1));
82 addTimer(TIMER2, profile.getTimerId(2));
83 if (profile.chargingControlType.equals(WEEKLY_PLANNER)) {
84 addTimer(TIMER3, profile.getTimerId(3));
85 addTimer(TIMER4, profile.getTimerId(4));
88 if (CHARGING_WINDOW.equals(profile.chargingPreference)) {
89 addTime(WINDOWSTART, profile.reductionOfChargeCurrent.start);
90 addTime(WINDOWEND, profile.reductionOfChargeCurrent.end);
92 preference.ifPresent(pref -> {
93 if (ChargingPreference.chargingWindow.equals(pref)) {
94 addTime(WINDOWSTART, null);
95 addTime(WINDOWEND, null);
101 public @Nullable Boolean isEnabled(final ProfileKey key) {
102 return enabled.get(key);
105 public void setEnabled(final ProfileKey key, @Nullable final Boolean enabled) {
106 if (enabled == null) {
107 this.enabled.remove(key);
109 this.enabled.put(key, enabled);
113 public @Nullable String getMode() {
114 return mode.map(m -> m.name()).orElse(null);
117 public @Nullable String getControlType() {
118 return controlType.get();
121 public @Nullable ChargingSettings getChargeSettings() {
122 return chargeSettings.get();
125 public void setMode(final @Nullable String mode) {
128 this.mode = Optional.of(ChargingMode.valueOf(mode));
130 } catch (IllegalArgumentException iae) {
131 LOGGER.warn("unexpected value for chargingMode: {}", mode);
134 this.mode = Optional.empty();
137 public @Nullable String getPreference() {
138 return preference.map(pref -> pref.name()).orElse(null);
141 public void setPreference(final @Nullable String preference) {
142 if (preference != null) {
144 this.preference = Optional.of(ChargingPreference.valueOf(preference));
146 } catch (IllegalArgumentException iae) {
147 LOGGER.warn("unexpected value for chargingPreference: {}", preference);
150 this.preference = Optional.empty();
153 public @Nullable Set<DayOfWeek> getDays(final ProfileKey key) {
154 return daysOfWeek.get(key);
157 public void setDays(final ProfileKey key, final @Nullable Set<DayOfWeek> days) {
159 daysOfWeek.remove(key);
161 daysOfWeek.put(key, days);
165 public void setDayEnabled(final ProfileKey key, final DayOfWeek day, final boolean enabled) {
166 final Set<DayOfWeek> days = daysOfWeek.get(key);
168 daysOfWeek.put(key, enabled ? EnumSet.of(day) : EnumSet.noneOf(DayOfWeek.class));
178 public LocalTime getTime(final ProfileKey key) {
179 LocalTime t = times.get(key);
183 LOGGER.debug("Profile not valid - Key {} doesn't contain boolean value", key);
184 return Constants.NULL_LOCAL_TIME;
188 public void setTime(final ProfileKey key, @Nullable LocalTime time) {
192 times.put(key, time);
196 public String getJson() {
197 final ChargeProfile profile = new ChargeProfile();
199 preference.ifPresent(pref -> profile.chargingPreference = pref.name());
200 profile.chargingControlType = controlType.get();
201 Boolean enabledBool = isEnabled(CLIMATE);
202 profile.climatisationOn = enabledBool == null ? false : enabledBool;
203 preference.ifPresent(pref -> {
204 if (ChargingPreference.chargingWindow.equals(pref)) {
205 profile.chargingMode = getMode();
206 final LocalTime start = getTime(WINDOWSTART);
207 final LocalTime end = getTime(WINDOWEND);
208 if (!start.equals(Constants.NULL_LOCAL_TIME) && !end.equals(Constants.NULL_LOCAL_TIME)) {
209 ChargingWindow cw = new ChargingWindow();
210 profile.reductionOfChargeCurrent = cw;
211 cw.start = new Time();
212 cw.start.hour = start.getHour();
213 cw.start.minute = start.getMinute();
215 cw.end.hour = end.getHour();
216 cw.end.minute = end.getMinute();
220 profile.departureTimes = new ArrayList<Timer>();
221 profile.departureTimes.add(getTimer(TIMER1));
222 profile.departureTimes.add(getTimer(TIMER2));
223 if (profile.chargingControlType.equals(WEEKLY_PLANNER)) {
224 profile.departureTimes.add(getTimer(TIMER3));
225 profile.departureTimes.add(getTimer(TIMER4));
228 profile.chargingSettings = chargeSettings.get();
229 return Converter.getGson().toJson(profile);
232 private void addTime(final ProfileKey key, @Nullable final Time time) {
234 times.put(key, time == null ? NULL_LOCAL_TIME : LocalTime.parse(Converter.getTime(time), TIME_FORMATER));
235 } catch (DateTimeParseException dtpe) {
236 LOGGER.warn("unexpected value for {} time: {}", key.name(), time);
240 private void addTimer(final ProfileKey key, @Nullable final Timer timer) {
242 enabled.put(key, false);
244 daysOfWeek.put(key, EnumSet.noneOf(DayOfWeek.class));
246 enabled.put(key, ACTIVATE.equals(timer.action));
247 addTime(key, timer.timeStamp);
248 final EnumSet<DayOfWeek> daySet = EnumSet.noneOf(DayOfWeek.class);
249 if (timer.timerWeekDays != null) {
250 daysOfWeek.put(key, EnumSet.noneOf(DayOfWeek.class));
251 for (String day : timer.timerWeekDays) {
253 daySet.add(DayOfWeek.valueOf(day.toUpperCase()));
254 } catch (IllegalArgumentException iae) {
255 LOGGER.warn("unexpected value for {} day: {}", key.name(), day);
257 daysOfWeek.put(key, daySet);
263 private Timer getTimer(final ProfileKey key) {
264 final Timer timer = new Timer();
282 Boolean enabledBool = isEnabled(key);
283 if (enabledBool != null) {
284 timer.action = enabledBool ? ACTIVATE : DEACTIVATE;
286 timer.action = DEACTIVATE;
288 final LocalTime time = getTime(key);
289 if (!time.equals(Constants.NULL_LOCAL_TIME)) {
290 timer.timeStamp = new Time();
291 timer.timeStamp.hour = time.getHour();
292 timer.timeStamp.minute = time.getMinute();
294 final Set<DayOfWeek> days = daysOfWeek.get(key);
296 timer.timerWeekDays = new ArrayList<>();
297 for (DayOfWeek day : days) {
298 timer.timerWeekDays.add(day.name().toLowerCase());