2 * Copyright (c) 2010-2022 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.hdpowerview.internal.builders;
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;
21 import java.util.StringJoiner;
22 import java.util.stream.Collectors;
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;
41 * The {@link AutomationChannelBuilder} class creates automation channels
42 * from structured scheduled event data.
44 * @author Jacob Laursen - Initial contribution
47 public class AutomationChannelBuilder extends BaseChannelBuilder {
49 private final Logger logger = LoggerFactory.getLogger(AutomationChannelBuilder.class);
52 private Map<Integer, Scene> scenes;
54 private Map<Integer, SceneCollection> sceneCollections;
56 private List<ScheduledEvent> scheduledEvents;
58 private AutomationChannelBuilder(HDPowerViewTranslationProvider translationProvider,
59 ChannelGroupUID channelGroupUid) {
60 super(translationProvider, channelGroupUid, HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED);
64 * Creates an {@link AutomationChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and
65 * {@link ChannelGroupUID}.
67 * @param translationProvider the {@link HDPowerViewTranslationProvider}
68 * @param channelGroupUid parent {@link ChannelGroupUID} for created channels
69 * @return channel builder
71 public static AutomationChannelBuilder create(HDPowerViewTranslationProvider translationProvider,
72 ChannelGroupUID channelGroupUid) {
73 return new AutomationChannelBuilder(translationProvider, channelGroupUid);
77 * Adds created channels to existing list.
79 * @param channels list that channels will be added to
80 * @return channel builder
82 public AutomationChannelBuilder withChannels(List<Channel> channels) {
83 this.channels = channels;
90 * @param scenes the scenes
91 * @return channel builder
93 public AutomationChannelBuilder withScenes(List<Scene> scenes) {
94 this.scenes = scenes.stream().collect(Collectors.toMap(scene -> scene.id, scene -> scene));
99 * Sets the scene collections.
101 * @param sceneCollections the scene collections
102 * @return channel builder
104 public AutomationChannelBuilder withSceneCollections(List<SceneCollection> sceneCollections) {
105 this.sceneCollections = sceneCollections.stream()
106 .collect(Collectors.toMap(sceneCollection -> sceneCollection.id, sceneCollection -> sceneCollection));
111 * Sets the scheduled events.
113 * @param scheduledEvents the sceduled events
114 * @return channel builder
116 public AutomationChannelBuilder withScheduledEvents(List<ScheduledEvent> scheduledEvents) {
117 this.scheduledEvents = scheduledEvents;
122 * Builds and returns the channels.
124 * @return the {@link Channel} list
126 public List<Channel> build() {
127 List<ScheduledEvent> scheduledEvents = this.scheduledEvents;
128 if (scheduledEvents == null || (scenes == null && sceneCollections == null)) {
129 return getChannelList(0);
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);
142 private @Nullable Channel createChannel(ScheduledEvent scheduledEvent) {
143 String referencedName = getReferencedSceneOrSceneCollectionName(scheduledEvent);
144 if (referencedName == null) {
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",
151 Channel channel = ChannelBuilder.create(channelUid, CoreItemFactory.SWITCH).withType(channelTypeUid)
152 .withLabel(label).withDescription(description).build();
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);
165 Scene scene = scenes.get(scheduledEvent.sceneId);
167 return scene.getName();
169 logger.warn("Scene '{}' was not found for scheduled event '{}'", scheduledEvent.sceneId, scheduledEvent.id);
171 } else if (scheduledEvent.sceneCollectionId > 0) {
172 Map<Integer, SceneCollection> sceneCollections = this.sceneCollections;
173 if (sceneCollections == null) {
175 "Scheduled event '{}' references scene collection '{}', but no scene collections are loaded",
176 scheduledEvent.id, scheduledEvent.sceneCollectionId);
179 SceneCollection sceneCollection = sceneCollections.get(scheduledEvent.sceneCollectionId);
180 if (sceneCollection != null) {
181 return sceneCollection.getName();
183 logger.warn("Scene collection '{}' was not found for scheduled event '{}'",
184 scheduledEvent.sceneCollectionId, scheduledEvent.id);
187 logger.warn("Scheduled event '{}'' not related to any scene or scene collection", scheduledEvent.id);
192 private String getScheduledEventName(String sceneName, ScheduledEvent scheduledEvent) {
193 String timeString, daysString;
195 switch (scheduledEvent.eventType) {
196 case ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME:
197 timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString();
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));
206 timeString = translationProvider.getText("dynamic-channel.automation.after-sunrise",
207 getFormattedTimeOffset(scheduledEvent.minute));
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));
217 timeString = translationProvider.getText("dynamic-channel.automation.after-sunset",
218 getFormattedTimeOffset(scheduledEvent.minute));
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");
233 StringJoiner joiner = new StringJoiner(", ");
234 days.forEach(day -> joiner.add(day.getDisplayName(TextStyle.SHORT, translationProvider.getLocale())));
235 daysString = joiner.toString();
238 return translationProvider.getText("dynamic-channel.automation-enabled.label", sceneName, timeString,
242 private String getFormattedTimeOffset(int minutes) {
244 int remainder = minutes % 60;
245 if (remainder == 0) {
246 return translationProvider.getText("dynamic-channel.automation.hour", minutes / 60);
248 return translationProvider.getText("dynamic-channel.automation.hour-minute", minutes / 60, remainder);
250 return translationProvider.getText("dynamic-channel.automation.minute", minutes);