]> git.basschouten.com Git - openhab-addons.git/blob
83a72765806eeaa9225a65c7940f384d3d1d924e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.bmwconnecteddrive.internal.utils;
14
15 import static org.openhab.binding.bmwconnecteddrive.internal.utils.ChargeProfileWrapper.ProfileKey.*;
16 import static org.openhab.binding.bmwconnecteddrive.internal.utils.Constants.*;
17
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;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.Set;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.ChargingMode;
31 import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.ChargingPreference;
32 import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.ChargeProfile;
33 import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.ChargingWindow;
34 import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.Timer;
35 import org.openhab.binding.bmwconnecteddrive.internal.dto.charge.WeeklyPlanner;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.google.gson.JsonSyntaxException;
40
41 /**
42  * The {@link ChargeProfileWrapper} Wrapper for ChargeProfiles
43  *
44  * @author Bernd Weymann - Initial contribution
45  * @author Norbert Truchsess - add ChargeProfileActions
46  */
47 @NonNullByDefault
48 public class ChargeProfileWrapper {
49     private static final Logger LOGGER = LoggerFactory.getLogger(ChargeProfileWrapper.class);
50
51     public enum ProfileType {
52         WEEKLY,
53         TWO_TIMES,
54         EMPTY
55     }
56
57     public enum ProfileKey {
58         CLIMATE,
59         TIMER1,
60         TIMER2,
61         TIMER3,
62         TIMER4,
63         OVERRIDE,
64         WINDOWSTART,
65         WINDOWEND
66     }
67
68     protected final ProfileType type;
69
70     private Optional<ChargingMode> mode = Optional.empty();
71     private Optional<ChargingPreference> preference = Optional.empty();
72
73     private final Map<ProfileKey, Boolean> enabled = new HashMap<>();
74     private final Map<ProfileKey, LocalTime> times = new HashMap<>();
75     private final Map<ProfileKey, Set<DayOfWeek>> daysOfWeek = new HashMap<>();
76
77     public static Optional<ChargeProfileWrapper> fromJson(final String content) {
78         try {
79             final ChargeProfile cp = Converter.getGson().fromJson(content, ChargeProfile.class);
80             if (cp != null) {
81                 return Optional.of(new ChargeProfileWrapper(cp));
82             }
83         } catch (JsonSyntaxException jse) {
84             LOGGER.debug("ChargeProfile unparsable: {}", content);
85         }
86         return Optional.empty();
87     }
88
89     private ChargeProfileWrapper(final ChargeProfile profile) {
90         final WeeklyPlanner planner;
91
92         if (profile.weeklyPlanner != null) {
93             type = ProfileType.WEEKLY;
94             planner = profile.weeklyPlanner;
95         } else if (profile.twoTimesTimer != null) {
96             type = ProfileType.TWO_TIMES;
97             planner = profile.twoTimesTimer;
98             // timer days not supported
99         } else {
100             type = ProfileType.EMPTY;
101             return;
102         }
103
104         setPreference(planner.chargingPreferences);
105         setMode(planner.chargingMode);
106
107         setEnabled(CLIMATE, planner.climatizationEnabled);
108
109         addTimer(TIMER1, planner.timer1);
110         addTimer(TIMER2, planner.timer2);
111
112         if (planner.preferredChargingWindow != null) {
113             addTime(WINDOWSTART, planner.preferredChargingWindow.startTime);
114             addTime(WINDOWEND, planner.preferredChargingWindow.endTime);
115         } else {
116             preference.ifPresent(pref -> {
117                 if (ChargingPreference.CHARGING_WINDOW.equals(pref)) {
118                     addTime(WINDOWSTART, null);
119                     addTime(WINDOWEND, null);
120                 }
121             });
122         }
123
124         if (isWeekly()) {
125             addTimer(TIMER3, planner.timer3);
126             addTimer(OVERRIDE, planner.overrideTimer);
127         }
128     }
129
130     public @Nullable Boolean isEnabled(final ProfileKey key) {
131         return enabled.get(key);
132     }
133
134     public void setEnabled(final ProfileKey key, @Nullable final Boolean enabled) {
135         if (enabled == null) {
136             this.enabled.remove(key);
137         } else {
138             this.enabled.put(key, enabled);
139         }
140     }
141
142     public @Nullable String getMode() {
143         return mode.map(m -> m.name()).orElse(null);
144     }
145
146     public void setMode(final @Nullable String mode) {
147         if (mode != null) {
148             try {
149                 this.mode = Optional.of(ChargingMode.valueOf(mode));
150                 return;
151             } catch (IllegalArgumentException iae) {
152                 LOGGER.warn("unexpected value for chargingMode: {}", mode);
153             }
154         }
155         this.mode = Optional.empty();
156     }
157
158     public @Nullable String getPreference() {
159         return preference.map(pref -> pref.name()).orElse(null);
160     }
161
162     public void setPreference(final @Nullable String preference) {
163         if (preference != null) {
164             try {
165                 this.preference = Optional.of(ChargingPreference.valueOf(preference));
166                 return;
167             } catch (IllegalArgumentException iae) {
168                 LOGGER.warn("unexpected value for chargingPreference: {}", preference);
169             }
170         }
171         this.preference = Optional.empty();
172     }
173
174     public @Nullable Set<DayOfWeek> getDays(final ProfileKey key) {
175         return daysOfWeek.get(key);
176     }
177
178     public void setDays(final ProfileKey key, final @Nullable Set<DayOfWeek> days) {
179         if (days == null) {
180             daysOfWeek.remove(key);
181         } else {
182             daysOfWeek.put(key, days);
183         }
184     }
185
186     public void setDayEnabled(final ProfileKey key, final DayOfWeek day, final boolean enabled) {
187         final Set<DayOfWeek> days = daysOfWeek.get(key);
188         if (days == null) {
189             daysOfWeek.put(key, enabled ? EnumSet.of(day) : EnumSet.noneOf(DayOfWeek.class));
190         } else {
191             if (enabled) {
192                 days.add(day);
193             } else {
194                 days.remove(day);
195             }
196         }
197     }
198
199     public @Nullable LocalTime getTime(final ProfileKey key) {
200         return times.get(key);
201     }
202
203     public void setTime(final ProfileKey key, @Nullable LocalTime time) {
204         if (time == null) {
205             times.remove(key);
206         } else {
207             times.put(key, time);
208         }
209     }
210
211     public String getJson() {
212         final ChargeProfile profile = new ChargeProfile();
213         final WeeklyPlanner planner = new WeeklyPlanner();
214
215         preference.ifPresent(pref -> planner.chargingPreferences = pref.name());
216         planner.climatizationEnabled = isEnabled(CLIMATE);
217         preference.ifPresent(pref -> {
218             if (ChargingPreference.CHARGING_WINDOW.equals(pref)) {
219                 planner.chargingMode = getMode();
220                 final LocalTime start = getTime(WINDOWSTART);
221                 final LocalTime end = getTime(WINDOWEND);
222                 if (start != null || end != null) {
223                     planner.preferredChargingWindow = new ChargingWindow();
224                     planner.preferredChargingWindow.startTime = start == null ? null : start.format(TIME_FORMATER);
225                     planner.preferredChargingWindow.endTime = end == null ? null : end.format(TIME_FORMATER);
226                 }
227             }
228         });
229         planner.timer1 = getTimer(TIMER1);
230         planner.timer2 = getTimer(TIMER2);
231         if (isWeekly()) {
232             planner.timer3 = getTimer(TIMER3);
233             planner.overrideTimer = getTimer(OVERRIDE);
234             profile.weeklyPlanner = planner;
235         } else if (isTwoTimes()) {
236             profile.twoTimesTimer = planner;
237         }
238         return Converter.getGson().toJson(profile);
239     }
240
241     private void addTime(final ProfileKey key, @Nullable final String time) {
242         try {
243             times.put(key, time == null ? NULL_LOCAL_TIME : LocalTime.parse(time, TIME_FORMATER));
244         } catch (DateTimeParseException dtpe) {
245             LOGGER.warn("unexpected value for {} time: {}", key.name(), time);
246         }
247     }
248
249     private void addTimer(final ProfileKey key, @Nullable final Timer timer) {
250         if (timer == null) {
251             enabled.put(key, false);
252             addTime(key, null);
253             if (isWeekly()) {
254                 daysOfWeek.put(key, EnumSet.noneOf(DayOfWeek.class));
255             }
256         } else {
257             enabled.put(key, timer.timerEnabled);
258             addTime(key, timer.departureTime);
259             if (isWeekly()) {
260                 final EnumSet<DayOfWeek> daySet = EnumSet.noneOf(DayOfWeek.class);
261                 if (timer.weekdays != null) {
262                     for (String day : timer.weekdays) {
263                         try {
264                             daySet.add(DayOfWeek.valueOf(day));
265                         } catch (IllegalArgumentException iae) {
266                             LOGGER.warn("unexpected value for {} day: {}", key.name(), day);
267                         }
268                     }
269                 }
270                 daysOfWeek.put(key, daySet);
271             }
272         }
273     }
274
275     private @Nullable Timer getTimer(final ProfileKey key) {
276         final Timer timer = new Timer();
277         timer.timerEnabled = enabled.get(key);
278         final LocalTime time = times.get(key);
279         timer.departureTime = time == null ? null : time.format(TIME_FORMATER);
280         if (isWeekly()) {
281             final Set<DayOfWeek> days = daysOfWeek.get(key);
282             if (days != null) {
283                 timer.weekdays = new ArrayList<>();
284                 for (DayOfWeek day : days) {
285                     timer.weekdays.add(day.name());
286                 }
287             }
288         }
289         return timer.timerEnabled == null && timer.departureTime == null && timer.weekdays == null ? null : timer;
290     }
291
292     private boolean isWeekly() {
293         return ProfileType.WEEKLY.equals(type);
294     }
295
296     private boolean isTwoTimes() {
297         return ProfileType.TWO_TIMES.equals(type);
298     }
299 }