]> git.basschouten.com Git - openhab-addons.git/blob
a29e7766e726778898cf497d9a231786d73c588b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.hdpowerview.internal.builders;
14
15 import java.time.DayOfWeek;
16 import java.time.LocalTime;
17 import java.time.format.TextStyle;
18 import java.util.EnumSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.StringJoiner;
22 import java.util.stream.Collectors;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants;
27 import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider;
28 import org.openhab.binding.hdpowerview.internal.dto.Scene;
29 import org.openhab.binding.hdpowerview.internal.dto.SceneCollection;
30 import org.openhab.binding.hdpowerview.internal.dto.ScheduledEvent;
31 import org.openhab.core.library.CoreItemFactory;
32 import org.openhab.core.thing.Channel;
33 import org.openhab.core.thing.ChannelGroupUID;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.binding.builder.ChannelBuilder;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * The {@link AutomationChannelBuilder} class creates automation channels
41  * from structured scheduled event data.
42  *
43  * @author Jacob Laursen - Initial contribution
44  */
45 @NonNullByDefault
46 public class AutomationChannelBuilder extends BaseChannelBuilder {
47
48     private final Logger logger = LoggerFactory.getLogger(AutomationChannelBuilder.class);
49
50     @Nullable
51     private Map<Integer, Scene> scenes;
52     @Nullable
53     private Map<Integer, SceneCollection> sceneCollections;
54     @Nullable
55     private List<ScheduledEvent> scheduledEvents;
56
57     private AutomationChannelBuilder(HDPowerViewTranslationProvider translationProvider,
58             ChannelGroupUID channelGroupUid) {
59         super(translationProvider, channelGroupUid, HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED);
60     }
61
62     /**
63      * Creates an {@link AutomationChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and
64      * {@link ChannelGroupUID}.
65      * 
66      * @param translationProvider the {@link HDPowerViewTranslationProvider}
67      * @param channelGroupUid parent {@link ChannelGroupUID} for created channels
68      * @return channel builder
69      */
70     public static AutomationChannelBuilder create(HDPowerViewTranslationProvider translationProvider,
71             ChannelGroupUID channelGroupUid) {
72         return new AutomationChannelBuilder(translationProvider, channelGroupUid);
73     }
74
75     /**
76      * Adds created channels to existing list.
77      * 
78      * @param channels list that channels will be added to
79      * @return channel builder
80      */
81     public AutomationChannelBuilder withChannels(List<Channel> channels) {
82         this.channels = channels;
83         return this;
84     }
85
86     /**
87      * Sets the scenes.
88      * 
89      * @param scenes the scenes
90      * @return channel builder
91      */
92     public AutomationChannelBuilder withScenes(List<Scene> scenes) {
93         this.scenes = scenes.stream().collect(Collectors.toMap(scene -> scene.id, scene -> scene));
94         return this;
95     }
96
97     /**
98      * Sets the scene collections.
99      * 
100      * @param sceneCollections the scene collections
101      * @return channel builder
102      */
103     public AutomationChannelBuilder withSceneCollections(List<SceneCollection> sceneCollections) {
104         this.sceneCollections = sceneCollections.stream()
105                 .collect(Collectors.toMap(sceneCollection -> sceneCollection.id, sceneCollection -> sceneCollection));
106         return this;
107     }
108
109     /**
110      * Sets the scheduled events.
111      * 
112      * @param scheduledEvents the sceduled events
113      * @return channel builder
114      */
115     public AutomationChannelBuilder withScheduledEvents(List<ScheduledEvent> scheduledEvents) {
116         this.scheduledEvents = scheduledEvents;
117         return this;
118     }
119
120     /**
121      * Builds and returns the channels.
122      *
123      * @return the {@link Channel} list
124      */
125     public List<Channel> build() {
126         List<ScheduledEvent> scheduledEvents = this.scheduledEvents;
127         if (scheduledEvents == null || (scenes == null && sceneCollections == null)) {
128             return getChannelList(0);
129         }
130         List<Channel> channels = getChannelList(scheduledEvents.size());
131         scheduledEvents.stream().forEach(scheduledEvent -> {
132             Channel channel = createChannel(scheduledEvent);
133             if (channel != null) {
134                 channels.add(channel);
135             }
136         });
137
138         return channels;
139     }
140
141     private @Nullable Channel createChannel(ScheduledEvent scheduledEvent) {
142         String referencedName = getReferencedSceneOrSceneCollectionName(scheduledEvent);
143         if (referencedName == null) {
144             return null;
145         }
146         ChannelUID channelUid = new ChannelUID(channelGroupUid, Integer.toString(scheduledEvent.id));
147         String label = getScheduledEventName(referencedName, scheduledEvent);
148         String description = translationProvider.getText("dynamic-channel.automation-enabled.description",
149                 referencedName);
150         Channel channel = ChannelBuilder.create(channelUid, CoreItemFactory.SWITCH).withType(channelTypeUid)
151                 .withLabel(label).withDescription(description).build();
152
153         return channel;
154     }
155
156     private @Nullable String getReferencedSceneOrSceneCollectionName(ScheduledEvent scheduledEvent) {
157         if (scheduledEvent.sceneId > 0) {
158             Map<Integer, Scene> scenes = this.scenes;
159             if (scenes == null) {
160                 logger.warn("Scheduled event '{}' references scene '{}', but no scenes are loaded", scheduledEvent.id,
161                         scheduledEvent.sceneId);
162                 return null;
163             }
164             Scene scene = scenes.get(scheduledEvent.sceneId);
165             if (scene != null) {
166                 return scene.getName();
167             }
168             logger.warn("Scene '{}' was not found for scheduled event '{}'", scheduledEvent.sceneId, scheduledEvent.id);
169             return null;
170         } else if (scheduledEvent.sceneCollectionId > 0) {
171             Map<Integer, SceneCollection> sceneCollections = this.sceneCollections;
172             if (sceneCollections == null) {
173                 logger.warn(
174                         "Scheduled event '{}' references scene collection '{}', but no scene collections are loaded",
175                         scheduledEvent.id, scheduledEvent.sceneCollectionId);
176                 return null;
177             }
178             SceneCollection sceneCollection = sceneCollections.get(scheduledEvent.sceneCollectionId);
179             if (sceneCollection != null) {
180                 return sceneCollection.getName();
181             }
182             logger.warn("Scene collection '{}' was not found for scheduled event '{}'",
183                     scheduledEvent.sceneCollectionId, scheduledEvent.id);
184             return null;
185         } else {
186             logger.warn("Scheduled event '{}'' not related to any scene or scene collection", scheduledEvent.id);
187             return null;
188         }
189     }
190
191     private String getScheduledEventName(String sceneName, ScheduledEvent scheduledEvent) {
192         String timeString, daysString;
193
194         switch (scheduledEvent.eventType) {
195             case ScheduledEvent.SCHEDULED_EVENT_TYPE_TIME:
196                 timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString();
197                 break;
198             case ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE:
199                 if (scheduledEvent.minute == 0) {
200                     timeString = translationProvider.getText("dynamic-channel.automation.at-sunrise");
201                 } else if (scheduledEvent.minute < 0) {
202                     timeString = translationProvider.getText("dynamic-channel.automation.before-sunrise",
203                             getFormattedTimeOffset(-scheduledEvent.minute));
204                 } else {
205                     timeString = translationProvider.getText("dynamic-channel.automation.after-sunrise",
206                             getFormattedTimeOffset(scheduledEvent.minute));
207                 }
208                 break;
209             case ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNSET:
210                 if (scheduledEvent.minute == 0) {
211                     timeString = translationProvider.getText("dynamic-channel.automation.at-sunset");
212                 } else if (scheduledEvent.minute < 0) {
213                     timeString = translationProvider.getText("dynamic-channel.automation.before-sunset",
214                             getFormattedTimeOffset(-scheduledEvent.minute));
215                 } else {
216                     timeString = translationProvider.getText("dynamic-channel.automation.after-sunset",
217                             getFormattedTimeOffset(scheduledEvent.minute));
218                 }
219                 break;
220             default:
221                 return sceneName;
222         }
223
224         EnumSet<DayOfWeek> days = scheduledEvent.getDays();
225         if (EnumSet.allOf(DayOfWeek.class).equals(days)) {
226             daysString = translationProvider.getText("dynamic-channel.automation.all-days");
227         } else if (ScheduledEvent.WEEKDAYS.equals(days)) {
228             daysString = translationProvider.getText("dynamic-channel.automation.weekdays");
229         } else if (ScheduledEvent.WEEKENDS.equals(days)) {
230             daysString = translationProvider.getText("dynamic-channel.automation.weekends");
231         } else {
232             StringJoiner joiner = new StringJoiner(", ");
233             days.forEach(day -> joiner.add(day.getDisplayName(TextStyle.SHORT, translationProvider.getLocale())));
234             daysString = joiner.toString();
235         }
236
237         return translationProvider.getText("dynamic-channel.automation-enabled.label", sceneName, timeString,
238                 daysString);
239     }
240
241     private String getFormattedTimeOffset(int minutes) {
242         if (minutes >= 60) {
243             int remainder = minutes % 60;
244             if (remainder == 0) {
245                 return translationProvider.getText("dynamic-channel.automation.hour", minutes / 60);
246             }
247             return translationProvider.getText("dynamic-channel.automation.hour-minute", minutes / 60, remainder);
248         }
249         return translationProvider.getText("dynamic-channel.automation.minute", minutes);
250     }
251 }