]> git.basschouten.com Git - openhab-addons.git/blob
5d5b9fd2900640b54e1a031db739d6e6c04e9df7
[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         return ChannelBuilder.create(channelUid, CoreItemFactory.SWITCH).withType(channelTypeUid).withLabel(label)
151                 .withDescription(description).build();
152     }
153
154     private @Nullable String getReferencedSceneOrSceneCollectionName(ScheduledEvent scheduledEvent) {
155         if (scheduledEvent.sceneId > 0) {
156             Map<Integer, Scene> scenes = this.scenes;
157             if (scenes == null) {
158                 logger.warn("Scheduled event '{}' references scene '{}', but no scenes are loaded", scheduledEvent.id,
159                         scheduledEvent.sceneId);
160                 return null;
161             }
162             Scene scene = scenes.get(scheduledEvent.sceneId);
163             if (scene != null) {
164                 return scene.getName();
165             }
166             logger.warn("Scene '{}' was not found for scheduled event '{}'", scheduledEvent.sceneId, scheduledEvent.id);
167             return null;
168         } else if (scheduledEvent.sceneCollectionId > 0) {
169             Map<Integer, SceneCollection> sceneCollections = this.sceneCollections;
170             if (sceneCollections == null) {
171                 logger.warn(
172                         "Scheduled event '{}' references scene collection '{}', but no scene collections are loaded",
173                         scheduledEvent.id, scheduledEvent.sceneCollectionId);
174                 return null;
175             }
176             SceneCollection sceneCollection = sceneCollections.get(scheduledEvent.sceneCollectionId);
177             if (sceneCollection != null) {
178                 return sceneCollection.getName();
179             }
180             logger.warn("Scene collection '{}' was not found for scheduled event '{}'",
181                     scheduledEvent.sceneCollectionId, scheduledEvent.id);
182             return null;
183         } else {
184             logger.warn("Scheduled event '{}'' not related to any scene or scene collection", scheduledEvent.id);
185             return null;
186         }
187     }
188
189     private String getScheduledEventName(String sceneName, ScheduledEvent scheduledEvent) {
190         String timeString, daysString;
191
192         switch (scheduledEvent.eventType) {
193             case ScheduledEvent.SCHEDULED_EVENT_TYPE_TIME:
194                 timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString();
195                 break;
196             case ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE:
197                 if (scheduledEvent.minute == 0) {
198                     timeString = translationProvider.getText("dynamic-channel.automation.at-sunrise");
199                 } else if (scheduledEvent.minute < 0) {
200                     timeString = translationProvider.getText("dynamic-channel.automation.before-sunrise",
201                             getFormattedTimeOffset(-scheduledEvent.minute));
202                 } else {
203                     timeString = translationProvider.getText("dynamic-channel.automation.after-sunrise",
204                             getFormattedTimeOffset(scheduledEvent.minute));
205                 }
206                 break;
207             case ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNSET:
208                 if (scheduledEvent.minute == 0) {
209                     timeString = translationProvider.getText("dynamic-channel.automation.at-sunset");
210                 } else if (scheduledEvent.minute < 0) {
211                     timeString = translationProvider.getText("dynamic-channel.automation.before-sunset",
212                             getFormattedTimeOffset(-scheduledEvent.minute));
213                 } else {
214                     timeString = translationProvider.getText("dynamic-channel.automation.after-sunset",
215                             getFormattedTimeOffset(scheduledEvent.minute));
216                 }
217                 break;
218             default:
219                 return sceneName;
220         }
221
222         EnumSet<DayOfWeek> days = scheduledEvent.getDays();
223         if (EnumSet.allOf(DayOfWeek.class).equals(days)) {
224             daysString = translationProvider.getText("dynamic-channel.automation.all-days");
225         } else if (ScheduledEvent.WEEKDAYS.equals(days)) {
226             daysString = translationProvider.getText("dynamic-channel.automation.weekdays");
227         } else if (ScheduledEvent.WEEKENDS.equals(days)) {
228             daysString = translationProvider.getText("dynamic-channel.automation.weekends");
229         } else {
230             StringJoiner joiner = new StringJoiner(", ");
231             days.forEach(day -> joiner.add(day.getDisplayName(TextStyle.SHORT, translationProvider.getLocale())));
232             daysString = joiner.toString();
233         }
234
235         return translationProvider.getText("dynamic-channel.automation-enabled.label", sceneName, timeString,
236                 daysString);
237     }
238
239     private String getFormattedTimeOffset(int minutes) {
240         if (minutes >= 60) {
241             int remainder = minutes % 60;
242             if (remainder == 0) {
243                 return translationProvider.getText("dynamic-channel.automation.hour", minutes / 60);
244             }
245             return translationProvider.getText("dynamic-channel.automation.hour-minute", minutes / 60, remainder);
246         }
247         return translationProvider.getText("dynamic-channel.automation.minute", minutes);
248     }
249 }