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