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.handler;
15 import java.time.Duration;
16 import java.time.Instant;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
21 import java.util.Map.Entry;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.CopyOnWriteArrayList;
24 import java.util.concurrent.ScheduledFuture;
25 import java.util.concurrent.TimeUnit;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.eclipse.jetty.client.HttpClient;
30 import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants;
31 import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider;
32 import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
33 import org.openhab.binding.hdpowerview.internal.api.Firmware;
34 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersions;
35 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
36 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
37 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
38 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
39 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents;
40 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent;
41 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
42 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
43 import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder;
44 import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder;
45 import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder;
46 import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration;
47 import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration;
48 import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
49 import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
50 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
51 import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
52 import org.openhab.core.library.CoreItemFactory;
53 import org.openhab.core.library.types.OnOffType;
54 import org.openhab.core.thing.Bridge;
55 import org.openhab.core.thing.Channel;
56 import org.openhab.core.thing.ChannelGroupUID;
57 import org.openhab.core.thing.ChannelUID;
58 import org.openhab.core.thing.Thing;
59 import org.openhab.core.thing.ThingStatus;
60 import org.openhab.core.thing.ThingStatusDetail;
61 import org.openhab.core.thing.ThingStatusInfo;
62 import org.openhab.core.thing.ThingUID;
63 import org.openhab.core.thing.binding.BaseBridgeHandler;
64 import org.openhab.core.thing.binding.ThingHandler;
65 import org.openhab.core.thing.binding.builder.ChannelBuilder;
66 import org.openhab.core.thing.type.ChannelTypeUID;
67 import org.openhab.core.types.Command;
68 import org.openhab.core.types.RefreshType;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
73 * The {@link HDPowerViewHubHandler} is responsible for handling commands, which
74 * are sent to one of the channels.
76 * @author Andy Lintner - Initial contribution
77 * @author Andrew Fiddian-Green - Added support for secondary rail positions
78 * @author Jacob Laursen - Added support for scene groups and automations
81 public class HDPowerViewHubHandler extends BaseBridgeHandler {
83 private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class);
84 private final HttpClient httpClient;
85 private final HDPowerViewTranslationProvider translationProvider;
86 private final ConcurrentHashMap<ThingUID, ShadeData> pendingShadeInitializations = new ConcurrentHashMap<>();
87 private final Duration firmwareVersionValidityPeriod = Duration.ofDays(1);
89 private long refreshInterval;
90 private long hardRefreshPositionInterval;
91 private long hardRefreshBatteryLevelInterval;
93 private @NonNullByDefault({}) HDPowerViewWebTargets webTargets;
94 private @Nullable ScheduledFuture<?> pollFuture;
95 private @Nullable ScheduledFuture<?> hardRefreshPositionFuture;
96 private @Nullable ScheduledFuture<?> hardRefreshBatteryLevelFuture;
98 private List<Scene> sceneCache = new CopyOnWriteArrayList<>();
99 private List<SceneCollection> sceneCollectionCache = new CopyOnWriteArrayList<>();
100 private List<ScheduledEvent> scheduledEventCache = new CopyOnWriteArrayList<>();
101 private Instant firmwareVersionsUpdated = Instant.MIN;
102 private Boolean deprecatedChannelsCreated = false;
104 private final ChannelTypeUID sceneChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
105 HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE);
107 private final ChannelTypeUID sceneGroupChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
108 HDPowerViewBindingConstants.CHANNELTYPE_SCENE_GROUP_ACTIVATE);
110 private final ChannelTypeUID automationChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
111 HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED);
113 public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient,
114 HDPowerViewTranslationProvider translationProvider) {
116 this.httpClient = httpClient;
117 this.translationProvider = translationProvider;
121 public void handleCommand(ChannelUID channelUID, Command command) {
122 if (RefreshType.REFRESH == command) {
123 requestRefreshShadePositions();
127 Channel channel = getThing().getChannel(channelUID.getId());
128 if (channel == null) {
133 int id = Integer.parseInt(channelUID.getIdWithoutGroup());
134 if (sceneChannelTypeUID.equals(channel.getChannelTypeUID()) && OnOffType.ON == command) {
135 webTargets.activateScene(id);
136 // Reschedule soft poll for immediate shade position update.
138 } else if (sceneGroupChannelTypeUID.equals(channel.getChannelTypeUID()) && OnOffType.ON == command) {
139 webTargets.activateSceneCollection(id);
140 // Reschedule soft poll for immediate shade position update.
142 } else if (automationChannelTypeUID.equals(channel.getChannelTypeUID())) {
143 webTargets.enableScheduledEvent(id, OnOffType.ON == command);
145 } catch (HubMaintenanceException e) {
146 // exceptions are logged in HDPowerViewWebTargets
147 firmwareVersionsUpdated = Instant.MIN;
148 } catch (NumberFormatException | HubException e) {
149 logger.debug("Unexpected error {}", e.getMessage());
154 public void initialize() {
155 logger.debug("Initializing hub");
156 HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class);
157 String host = config.host;
159 if (host == null || host.isEmpty()) {
160 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
161 "@text/offline.conf-error.no-host-address");
165 pendingShadeInitializations.clear();
166 webTargets = new HDPowerViewWebTargets(httpClient, host);
167 refreshInterval = config.refresh;
168 hardRefreshPositionInterval = config.hardRefresh;
169 hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel;
170 initializeChannels();
171 firmwareVersionsUpdated = Instant.MIN;
173 updateStatus(ThingStatus.UNKNOWN);
177 private void initializeChannels() {
178 // Rebuild dynamic channels and synchronize with cache.
179 updateThing(editThing().withChannels(new ArrayList<Channel>()).build());
181 sceneCollectionCache.clear();
182 scheduledEventCache.clear();
183 deprecatedChannelsCreated = false;
186 public HDPowerViewWebTargets getWebTargets() {
191 public void handleRemoval() {
192 super.handleRemoval();
197 public void dispose() {
200 pendingShadeInitializations.clear();
204 public void childHandlerInitialized(final ThingHandler childHandler, final Thing childThing) {
205 logger.debug("Child handler initialized: {}", childThing.getUID());
206 if (childHandler instanceof HDPowerViewShadeHandler) {
207 ShadeData shadeData = pendingShadeInitializations.remove(childThing.getUID());
208 if (shadeData != null) {
209 if (shadeData.id > 0) {
210 updateShadeThing(shadeData.id, childThing, shadeData);
212 updateUnknownShadeThing(childThing);
216 super.childHandlerInitialized(childHandler, childThing);
220 public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
221 logger.debug("Child handler disposed: {}", childThing.getUID());
222 if (childHandler instanceof HDPowerViewShadeHandler) {
223 pendingShadeInitializations.remove(childThing.getUID());
225 super.childHandlerDisposed(childHandler, childThing);
228 private void schedulePoll() {
233 private void scheduleSoftPoll() {
234 ScheduledFuture<?> future = this.pollFuture;
235 if (future != null) {
236 future.cancel(false);
238 logger.debug("Scheduling poll every {} ms", refreshInterval);
239 this.pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 0, refreshInterval, TimeUnit.MILLISECONDS);
242 private void scheduleHardPoll() {
243 ScheduledFuture<?> future = this.hardRefreshPositionFuture;
244 if (future != null) {
245 future.cancel(false);
247 if (hardRefreshPositionInterval > 0) {
248 logger.debug("Scheduling hard position refresh every {} minutes", hardRefreshPositionInterval);
249 this.hardRefreshPositionFuture = scheduler.scheduleWithFixedDelay(this::requestRefreshShadePositions, 1,
250 hardRefreshPositionInterval, TimeUnit.MINUTES);
253 future = this.hardRefreshBatteryLevelFuture;
254 if (future != null) {
255 future.cancel(false);
257 if (hardRefreshBatteryLevelInterval > 0) {
258 logger.debug("Scheduling hard battery level refresh every {} hours", hardRefreshBatteryLevelInterval);
259 this.hardRefreshBatteryLevelFuture = scheduler.scheduleWithFixedDelay(
260 this::requestRefreshShadeBatteryLevels, 1, hardRefreshBatteryLevelInterval, TimeUnit.HOURS);
264 private synchronized void stopPoll() {
265 ScheduledFuture<?> future = this.pollFuture;
266 if (future != null) {
269 this.pollFuture = null;
271 future = this.hardRefreshPositionFuture;
272 if (future != null) {
275 this.hardRefreshPositionFuture = null;
277 future = this.hardRefreshBatteryLevelFuture;
278 if (future != null) {
281 this.hardRefreshBatteryLevelFuture = null;
284 private synchronized void poll() {
286 updateFirmwareProperties();
287 } catch (HubException e) {
288 logger.warn("Failed to update firmware properties: {}", e.getMessage());
292 logger.debug("Polling for state");
295 List<Scene> scenes = updateSceneChannels();
296 List<SceneCollection> sceneCollections = updateSceneGroupChannels();
297 List<ScheduledEvent> scheduledEvents = updateAutomationChannels(scenes, sceneCollections);
299 // Scheduled events should also have their current state updated if event has been
300 // enabled or disabled through app or other integration.
301 updateAutomationStates(scheduledEvents);
302 } catch (HubInvalidResponseException e) {
303 Throwable cause = e.getCause();
305 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
307 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
309 } catch (HubMaintenanceException e) {
310 // exceptions are logged in HDPowerViewWebTargets
311 firmwareVersionsUpdated = Instant.MIN;
312 } catch (HubException e) {
313 logger.warn("Error connecting to bridge: {}", e.getMessage());
314 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
315 firmwareVersionsUpdated = Instant.MIN;
319 private void updateFirmwareProperties()
320 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
321 if (firmwareVersionsUpdated.isAfter(Instant.now().minus(firmwareVersionValidityPeriod))) {
324 FirmwareVersions firmwareVersions = webTargets.getFirmwareVersions();
325 Firmware mainProcessor = firmwareVersions.mainProcessor;
326 if (mainProcessor == null) {
327 logger.warn("Main processor firmware version missing in response.");
330 logger.debug("Main processor firmware version received: {}, {}", mainProcessor.name, mainProcessor.toString());
331 Map<String, String> properties = editProperties();
332 String mainProcessorName = mainProcessor.name;
333 if (mainProcessorName != null) {
334 properties.put(HDPowerViewBindingConstants.PROPERTY_FIRMWARE_NAME, mainProcessorName);
336 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, mainProcessor.toString());
337 Firmware radio = firmwareVersions.radio;
339 logger.debug("Radio firmware version received: {}", radio.toString());
340 properties.put(HDPowerViewBindingConstants.PROPERTY_RADIO_FIRMWARE_VERSION, radio.toString());
342 updateProperties(properties);
343 firmwareVersionsUpdated = Instant.now();
346 private void pollShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
347 Shades shades = webTargets.getShades();
348 List<ShadeData> shadesData = shades.shadeData;
349 if (shadesData == null) {
350 throw new HubInvalidResponseException("Missing 'shades.shadeData' element");
353 updateStatus(ThingStatus.ONLINE);
354 logger.debug("Received data for {} shades", shadesData.size());
356 Map<Integer, ShadeData> idShadeDataMap = getIdShadeDataMap(shadesData);
357 Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
358 for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
359 Thing thing = item.getKey();
360 int shadeId = item.getValue();
361 ShadeData shadeData = idShadeDataMap.get(shadeId);
362 if (shadeData != null) {
363 updateShadeThing(shadeId, thing, shadeData);
365 updateUnknownShadeThing(thing);
370 private void updateShadeThing(int shadeId, Thing thing, ShadeData shadeData) {
371 HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler());
372 if (thingHandler == null) {
373 logger.debug("Shade '{}' handler not initialized", shadeId);
374 pendingShadeInitializations.put(thing.getUID(), shadeData);
377 ThingStatus thingStatus = thingHandler.getThing().getStatus();
378 switch (thingStatus) {
382 logger.debug("Updating shade '{}'", shadeId);
383 thingHandler.onReceiveUpdate(shadeData);
387 logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus);
388 pendingShadeInitializations.put(thing.getUID(), shadeData);
393 logger.debug("Ignoring shade update for shade '{}' in status {}", shadeId, thingStatus);
398 private void updateUnknownShadeThing(Thing thing) {
399 String shadeId = thing.getUID().getId();
400 logger.debug("Shade '{}' has no data in hub", shadeId);
401 HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler());
402 if (thingHandler == null) {
403 logger.debug("Shade '{}' handler not initialized", shadeId);
404 pendingShadeInitializations.put(thing.getUID(), new ShadeData());
407 ThingStatus thingStatus = thingHandler.getThing().getStatus();
408 switch (thingStatus) {
412 thing.setStatusInfo(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.GONE,
413 "@text/offline.gone.shade-unknown-to-hub"));
417 logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus);
418 pendingShadeInitializations.put(thing.getUID(), new ShadeData());
423 logger.debug("Ignoring shade status update for shade '{}' in status {}", shadeId, thingStatus);
428 private List<Scene> fetchScenes()
429 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
430 Scenes scenes = webTargets.getScenes();
431 List<Scene> sceneData = scenes.sceneData;
432 if (sceneData == null) {
433 throw new HubInvalidResponseException("Missing 'scenes.sceneData' element");
435 logger.debug("Received data for {} scenes", sceneData.size());
440 private List<Scene> updateSceneChannels()
441 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
442 List<Scene> scenes = fetchScenes();
444 if (scenes.size() == sceneCache.size() && sceneCache.containsAll(scenes)) {
445 // Duplicates are not allowed. Reordering is not supported.
446 logger.debug("Preserving scene channels, no changes detected");
450 logger.debug("Updating all scene channels, changes detected");
451 sceneCache = new CopyOnWriteArrayList<Scene>(scenes);
453 List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
454 allChannels.removeIf(c -> HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES.equals(c.getUID().getGroupId()));
456 SceneChannelBuilder channelBuilder = SceneChannelBuilder
457 .create(this.translationProvider,
458 new ChannelGroupUID(thing.getUID(), HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES))
459 .withScenes(scenes).withChannels(allChannels);
461 updateThing(editThing().withChannels(channelBuilder.build()).build());
463 createDeprecatedSceneChannels(scenes);
469 * Create backwards compatible scene channels if any items configured before release 3.2
470 * are still linked. Users should have a reasonable amount of time to migrate to the new
471 * scene channels that are connected to a channel group.
473 private void createDeprecatedSceneChannels(List<Scene> scenes) {
474 if (deprecatedChannelsCreated) {
475 // Only do this once.
478 ChannelGroupUID channelGroupUid = new ChannelGroupUID(thing.getUID(),
479 HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES);
480 for (Scene scene : scenes) {
481 String channelId = Integer.toString(scene.id);
482 ChannelUID newChannelUid = new ChannelUID(channelGroupUid, channelId);
483 ChannelUID deprecatedChannelUid = new ChannelUID(getThing().getUID(), channelId);
484 String description = translationProvider.getText("dynamic-channel.scene-activate.deprecated.description",
486 Channel channel = ChannelBuilder.create(deprecatedChannelUid, CoreItemFactory.SWITCH)
487 .withType(sceneChannelTypeUID).withLabel(scene.getName()).withDescription(description).build();
488 logger.debug("Creating deprecated channel '{}' ('{}') to probe for linked items", deprecatedChannelUid,
490 updateThing(editThing().withChannel(channel).build());
491 if (this.isLinked(deprecatedChannelUid) && !this.isLinked(newChannelUid)) {
492 logger.warn("Created deprecated channel '{}' ('{}'), please link items to '{}' instead",
493 deprecatedChannelUid, scene.getName(), newChannelUid);
495 if (this.isLinked(newChannelUid)) {
496 logger.debug("Removing deprecated channel '{}' ('{}') since new channel '{}' is linked",
497 deprecatedChannelUid, scene.getName(), newChannelUid);
500 logger.debug("Removing deprecated channel '{}' ('{}') since it has no linked items",
501 deprecatedChannelUid, scene.getName());
503 updateThing(editThing().withoutChannel(deprecatedChannelUid).build());
506 deprecatedChannelsCreated = true;
509 private List<SceneCollection> fetchSceneCollections()
510 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
511 SceneCollections sceneCollections = webTargets.getSceneCollections();
512 List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
513 if (sceneCollectionData == null) {
514 throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element");
516 logger.debug("Received data for {} sceneCollections", sceneCollectionData.size());
518 return sceneCollectionData;
521 private List<SceneCollection> updateSceneGroupChannels()
522 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
523 List<SceneCollection> sceneCollections = fetchSceneCollections();
525 if (sceneCollections.size() == sceneCollectionCache.size()
526 && sceneCollectionCache.containsAll(sceneCollections)) {
527 // Duplicates are not allowed. Reordering is not supported.
528 logger.debug("Preserving scene group channels, no changes detected");
529 return sceneCollections;
532 logger.debug("Updating all scene group channels, changes detected");
533 sceneCollectionCache = new CopyOnWriteArrayList<SceneCollection>(sceneCollections);
535 List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
537 .removeIf(c -> HDPowerViewBindingConstants.CHANNEL_GROUP_SCENE_GROUPS.equals(c.getUID().getGroupId()));
539 SceneGroupChannelBuilder channelBuilder = SceneGroupChannelBuilder
540 .create(this.translationProvider,
541 new ChannelGroupUID(thing.getUID(), HDPowerViewBindingConstants.CHANNEL_GROUP_SCENE_GROUPS))
542 .withSceneCollections(sceneCollections).withChannels(allChannels);
544 updateThing(editThing().withChannels(channelBuilder.build()).build());
546 return sceneCollections;
549 private List<ScheduledEvent> fetchScheduledEvents()
550 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
551 ScheduledEvents scheduledEvents = webTargets.getScheduledEvents();
552 List<ScheduledEvent> scheduledEventData = scheduledEvents.scheduledEventData;
553 if (scheduledEventData == null) {
554 throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element");
556 logger.debug("Received data for {} scheduledEvents", scheduledEventData.size());
558 return scheduledEventData;
561 private List<ScheduledEvent> updateAutomationChannels(List<Scene> scenes, List<SceneCollection> sceneCollections)
562 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
563 List<ScheduledEvent> scheduledEvents = fetchScheduledEvents();
565 if (scheduledEvents.size() == scheduledEventCache.size() && scheduledEventCache.containsAll(scheduledEvents)) {
566 // Duplicates are not allowed. Reordering is not supported.
567 logger.debug("Preserving automation channels, no changes detected");
568 return scheduledEvents;
571 logger.debug("Updating all automation channels, changes detected");
572 scheduledEventCache = new CopyOnWriteArrayList<ScheduledEvent>(scheduledEvents);
574 List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
576 .removeIf(c -> HDPowerViewBindingConstants.CHANNEL_GROUP_AUTOMATIONS.equals(c.getUID().getGroupId()));
577 AutomationChannelBuilder channelBuilder = AutomationChannelBuilder
578 .create(this.translationProvider,
579 new ChannelGroupUID(thing.getUID(), HDPowerViewBindingConstants.CHANNEL_GROUP_AUTOMATIONS))
580 .withScenes(scenes).withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents)
581 .withChannels(allChannels);
582 updateThing(editThing().withChannels(channelBuilder.build()).build());
584 return scheduledEvents;
587 private void updateAutomationStates(List<ScheduledEvent> scheduledEvents) {
588 ChannelGroupUID channelGroupUid = new ChannelGroupUID(thing.getUID(),
589 HDPowerViewBindingConstants.CHANNEL_GROUP_AUTOMATIONS);
590 for (ScheduledEvent scheduledEvent : scheduledEvents) {
591 String scheduledEventId = Integer.toString(scheduledEvent.id);
592 ChannelUID channelUid = new ChannelUID(channelGroupUid, scheduledEventId);
593 updateState(channelUid, scheduledEvent.enabled ? OnOffType.ON : OnOffType.OFF);
597 private Map<Thing, Integer> getShadeThingIdMap() {
598 Map<Thing, Integer> ret = new HashMap<>();
599 getThing().getThings().stream()
600 .filter(thing -> HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thing.getThingTypeUID()))
602 int id = thing.getConfiguration().as(HDPowerViewShadeConfiguration.class).id;
610 private Map<Integer, ShadeData> getIdShadeDataMap(List<ShadeData> shadeData) {
611 Map<Integer, ShadeData> ret = new HashMap<>();
612 for (ShadeData shade : shadeData) {
614 ret.put(shade.id, shade);
620 private void requestRefreshShadePositions() {
621 Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
622 for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
623 Thing thing = item.getKey();
624 if (thing.getStatusInfo().getStatusDetail() == ThingStatusDetail.GONE) {
625 // Skip shades unknown to the Hub.
626 logger.debug("Shade '{}' is unknown, skipping position refresh", item.getValue());
629 ThingHandler handler = thing.getHandler();
630 if (handler instanceof HDPowerViewShadeHandler) {
631 ((HDPowerViewShadeHandler) handler).requestRefreshShadePosition();
633 int shadeId = item.getValue();
634 logger.debug("Shade '{}' handler not initialized", shadeId);
639 private void requestRefreshShadeBatteryLevels() {
640 Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
641 for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
642 Thing thing = item.getKey();
643 if (thing.getStatusInfo().getStatusDetail() == ThingStatusDetail.GONE) {
644 // Skip shades unknown to the Hub.
645 logger.debug("Shade '{}' is unknown, skipping battery level refresh", item.getValue());
648 ThingHandler handler = thing.getHandler();
649 if (handler instanceof HDPowerViewShadeHandler) {
650 ((HDPowerViewShadeHandler) handler).requestRefreshShadeBatteryLevel();
652 int shadeId = item.getValue();
653 logger.debug("Shade '{}' handler not initialized", shadeId);