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.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.List;
19 import java.util.Map.Entry;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.CopyOnWriteArrayList;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.eclipse.jetty.client.HttpClient;
28 import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants;
29 import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider;
30 import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
31 import org.openhab.binding.hdpowerview.internal.api.Firmware;
32 import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersions;
33 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
34 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
35 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
36 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
37 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents;
38 import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent;
39 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
40 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
41 import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder;
42 import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder;
43 import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder;
44 import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration;
45 import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration;
46 import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
47 import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
48 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
49 import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
50 import org.openhab.core.library.CoreItemFactory;
51 import org.openhab.core.library.types.OnOffType;
52 import org.openhab.core.thing.Bridge;
53 import org.openhab.core.thing.Channel;
54 import org.openhab.core.thing.ChannelGroupUID;
55 import org.openhab.core.thing.ChannelUID;
56 import org.openhab.core.thing.Thing;
57 import org.openhab.core.thing.ThingStatus;
58 import org.openhab.core.thing.ThingStatusDetail;
59 import org.openhab.core.thing.ThingStatusInfo;
60 import org.openhab.core.thing.ThingUID;
61 import org.openhab.core.thing.binding.BaseBridgeHandler;
62 import org.openhab.core.thing.binding.ThingHandler;
63 import org.openhab.core.thing.binding.builder.ChannelBuilder;
64 import org.openhab.core.thing.type.ChannelTypeUID;
65 import org.openhab.core.types.Command;
66 import org.openhab.core.types.RefreshType;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
71 * The {@link HDPowerViewHubHandler} is responsible for handling commands, which
72 * are sent to one of the channels.
74 * @author Andy Lintner - Initial contribution
75 * @author Andrew Fiddian-Green - Added support for secondary rail positions
76 * @author Jacob Laursen - Added support for scene groups and automations
79 public class HDPowerViewHubHandler extends BaseBridgeHandler {
81 private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class);
82 private final HttpClient httpClient;
83 private final HDPowerViewTranslationProvider translationProvider;
84 private final ConcurrentHashMap<ThingUID, ShadeData> pendingShadeInitializations = new ConcurrentHashMap<>();
86 private long refreshInterval;
87 private long hardRefreshPositionInterval;
88 private long hardRefreshBatteryLevelInterval;
90 private @NonNullByDefault({}) HDPowerViewWebTargets webTargets;
91 private @Nullable ScheduledFuture<?> pollFuture;
92 private @Nullable ScheduledFuture<?> hardRefreshPositionFuture;
93 private @Nullable ScheduledFuture<?> hardRefreshBatteryLevelFuture;
95 private List<Scene> sceneCache = new CopyOnWriteArrayList<>();
96 private List<SceneCollection> sceneCollectionCache = new CopyOnWriteArrayList<>();
97 private List<ScheduledEvent> scheduledEventCache = new CopyOnWriteArrayList<>();
98 private @Nullable FirmwareVersions firmwareVersions;
99 private Boolean deprecatedChannelsCreated = false;
101 private final ChannelTypeUID sceneChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
102 HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE);
104 private final ChannelTypeUID sceneGroupChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
105 HDPowerViewBindingConstants.CHANNELTYPE_SCENE_GROUP_ACTIVATE);
107 private final ChannelTypeUID automationChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID,
108 HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED);
110 public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient,
111 HDPowerViewTranslationProvider translationProvider) {
113 this.httpClient = httpClient;
114 this.translationProvider = translationProvider;
118 public void handleCommand(ChannelUID channelUID, Command command) {
119 if (RefreshType.REFRESH == command) {
120 requestRefreshShadePositions();
124 Channel channel = getThing().getChannel(channelUID.getId());
125 if (channel == null) {
130 int id = Integer.parseInt(channelUID.getIdWithoutGroup());
131 if (sceneChannelTypeUID.equals(channel.getChannelTypeUID()) && OnOffType.ON == command) {
132 webTargets.activateScene(id);
133 // Reschedule soft poll for immediate shade position update.
135 } else if (sceneGroupChannelTypeUID.equals(channel.getChannelTypeUID()) && OnOffType.ON == command) {
136 webTargets.activateSceneCollection(id);
137 // Reschedule soft poll for immediate shade position update.
139 } else if (automationChannelTypeUID.equals(channel.getChannelTypeUID())) {
140 webTargets.enableScheduledEvent(id, OnOffType.ON == command);
142 } catch (HubMaintenanceException e) {
143 // exceptions are logged in HDPowerViewWebTargets
144 } catch (NumberFormatException | HubException e) {
145 logger.debug("Unexpected error {}", e.getMessage());
150 public void initialize() {
151 logger.debug("Initializing hub");
152 HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class);
153 String host = config.host;
155 if (host == null || host.isEmpty()) {
156 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
157 "@text/offline.conf-error.no-host-address");
161 pendingShadeInitializations.clear();
162 webTargets = new HDPowerViewWebTargets(httpClient, host);
163 refreshInterval = config.refresh;
164 hardRefreshPositionInterval = config.hardRefresh;
165 hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel;
166 initializeChannels();
167 firmwareVersions = null;
169 updateStatus(ThingStatus.UNKNOWN);
173 private void initializeChannels() {
174 // Rebuild dynamic channels and synchronize with cache.
175 updateThing(editThing().withChannels(new ArrayList<Channel>()).build());
177 sceneCollectionCache.clear();
178 scheduledEventCache.clear();
179 deprecatedChannelsCreated = false;
182 public HDPowerViewWebTargets getWebTargets() {
187 public void handleRemoval() {
188 super.handleRemoval();
193 public void dispose() {
196 pendingShadeInitializations.clear();
200 public void childHandlerInitialized(final ThingHandler childHandler, final Thing childThing) {
201 logger.debug("Child handler initialized: {}", childThing.getUID());
202 if (childHandler instanceof HDPowerViewShadeHandler) {
203 ShadeData shadeData = pendingShadeInitializations.remove(childThing.getUID());
204 if (shadeData != null) {
205 if (shadeData.id > 0) {
206 updateShadeThing(shadeData.id, childThing, shadeData);
208 updateUnknownShadeThing(childThing);
212 super.childHandlerInitialized(childHandler, childThing);
216 public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
217 logger.debug("Child handler disposed: {}", childThing.getUID());
218 if (childHandler instanceof HDPowerViewShadeHandler) {
219 pendingShadeInitializations.remove(childThing.getUID());
221 super.childHandlerDisposed(childHandler, childThing);
224 private void schedulePoll() {
229 private void scheduleSoftPoll() {
230 ScheduledFuture<?> future = this.pollFuture;
231 if (future != null) {
232 future.cancel(false);
234 logger.debug("Scheduling poll every {} ms", refreshInterval);
235 this.pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 0, refreshInterval, TimeUnit.MILLISECONDS);
238 private void scheduleHardPoll() {
239 ScheduledFuture<?> future = this.hardRefreshPositionFuture;
240 if (future != null) {
241 future.cancel(false);
243 if (hardRefreshPositionInterval > 0) {
244 logger.debug("Scheduling hard position refresh every {} minutes", hardRefreshPositionInterval);
245 this.hardRefreshPositionFuture = scheduler.scheduleWithFixedDelay(this::requestRefreshShadePositions, 1,
246 hardRefreshPositionInterval, TimeUnit.MINUTES);
249 future = this.hardRefreshBatteryLevelFuture;
250 if (future != null) {
251 future.cancel(false);
253 if (hardRefreshBatteryLevelInterval > 0) {
254 logger.debug("Scheduling hard battery level refresh every {} hours", hardRefreshBatteryLevelInterval);
255 this.hardRefreshBatteryLevelFuture = scheduler.scheduleWithFixedDelay(
256 this::requestRefreshShadeBatteryLevels, 1, hardRefreshBatteryLevelInterval, TimeUnit.HOURS);
260 private synchronized void stopPoll() {
261 ScheduledFuture<?> future = this.pollFuture;
262 if (future != null) {
265 this.pollFuture = null;
267 future = this.hardRefreshPositionFuture;
268 if (future != null) {
271 this.hardRefreshPositionFuture = null;
273 future = this.hardRefreshBatteryLevelFuture;
274 if (future != null) {
277 this.hardRefreshBatteryLevelFuture = null;
280 private synchronized void poll() {
282 updateFirmwareProperties();
283 } catch (HubException e) {
284 logger.warn("Failed to update firmware properties: {}", e.getMessage());
288 logger.debug("Polling for state");
291 List<Scene> scenes = updateSceneChannels();
292 List<SceneCollection> sceneCollections = updateSceneGroupChannels();
293 List<ScheduledEvent> scheduledEvents = updateAutomationChannels(scenes, sceneCollections);
295 // Scheduled events should also have their current state updated if event has been
296 // enabled or disabled through app or other integration.
297 updateAutomationStates(scheduledEvents);
298 } catch (HubInvalidResponseException e) {
299 Throwable cause = e.getCause();
301 logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
303 logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
305 } catch (HubMaintenanceException e) {
306 // exceptions are logged in HDPowerViewWebTargets
307 } catch (HubException e) {
308 logger.warn("Error connecting to bridge: {}", e.getMessage());
309 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
313 private void updateFirmwareProperties()
314 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
315 if (firmwareVersions != null) {
318 FirmwareVersions firmwareVersions = webTargets.getFirmwareVersions();
319 Firmware mainProcessor = firmwareVersions.mainProcessor;
320 if (mainProcessor == null) {
321 logger.warn("Main processor firmware version missing in response.");
324 logger.debug("Main processor firmware version received: {}, {}", mainProcessor.name, mainProcessor.toString());
325 Map<String, String> properties = editProperties();
326 String mainProcessorName = mainProcessor.name;
327 if (mainProcessorName != null) {
328 properties.put(HDPowerViewBindingConstants.PROPERTY_FIRMWARE_NAME, mainProcessorName);
330 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, mainProcessor.toString());
331 Firmware radio = firmwareVersions.radio;
333 logger.debug("Radio firmware version received: {}", radio.toString());
334 properties.put(HDPowerViewBindingConstants.PROPERTY_RADIO_FIRMWARE_VERSION, radio.toString());
336 updateProperties(properties);
339 private void pollShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
340 Shades shades = webTargets.getShades();
341 List<ShadeData> shadesData = shades.shadeData;
342 if (shadesData == null) {
343 throw new HubInvalidResponseException("Missing 'shades.shadeData' element");
346 updateStatus(ThingStatus.ONLINE);
347 logger.debug("Received data for {} shades", shadesData.size());
349 Map<Integer, ShadeData> idShadeDataMap = getIdShadeDataMap(shadesData);
350 Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
351 for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
352 Thing thing = item.getKey();
353 int shadeId = item.getValue();
354 ShadeData shadeData = idShadeDataMap.get(shadeId);
355 if (shadeData != null) {
356 updateShadeThing(shadeId, thing, shadeData);
358 updateUnknownShadeThing(thing);
363 private void updateShadeThing(int shadeId, Thing thing, ShadeData shadeData) {
364 HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler());
365 if (thingHandler == null) {
366 logger.debug("Shade '{}' handler not initialized", shadeId);
367 pendingShadeInitializations.put(thing.getUID(), shadeData);
370 ThingStatus thingStatus = thingHandler.getThing().getStatus();
371 switch (thingStatus) {
375 logger.debug("Updating shade '{}'", shadeId);
376 thingHandler.onReceiveUpdate(shadeData);
380 logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus);
381 pendingShadeInitializations.put(thing.getUID(), shadeData);
386 logger.debug("Ignoring shade update for shade '{}' in status {}", shadeId, thingStatus);
391 private void updateUnknownShadeThing(Thing thing) {
392 String shadeId = thing.getUID().getId();
393 logger.debug("Shade '{}' has no data in hub", shadeId);
394 HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler());
395 if (thingHandler == null) {
396 logger.debug("Shade '{}' handler not initialized", shadeId);
397 pendingShadeInitializations.put(thing.getUID(), new ShadeData());
400 ThingStatus thingStatus = thingHandler.getThing().getStatus();
401 switch (thingStatus) {
405 thing.setStatusInfo(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.GONE,
406 "@text/offline.gone.shade-unknown-to-hub"));
410 logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus);
411 pendingShadeInitializations.put(thing.getUID(), new ShadeData());
416 logger.debug("Ignoring shade status update for shade '{}' in status {}", shadeId, thingStatus);
421 private List<Scene> fetchScenes()
422 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
423 Scenes scenes = webTargets.getScenes();
424 List<Scene> sceneData = scenes.sceneData;
425 if (sceneData == null) {
426 throw new HubInvalidResponseException("Missing 'scenes.sceneData' element");
428 logger.debug("Received data for {} scenes", sceneData.size());
433 private List<Scene> updateSceneChannels()
434 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
435 List<Scene> scenes = fetchScenes();
437 if (scenes.size() == sceneCache.size() && sceneCache.containsAll(scenes)) {
438 // Duplicates are not allowed. Reordering is not supported.
439 logger.debug("Preserving scene channels, no changes detected");
443 logger.debug("Updating all scene channels, changes detected");
444 sceneCache = new CopyOnWriteArrayList<Scene>(scenes);
446 List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
447 allChannels.removeIf(c -> HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES.equals(c.getUID().getGroupId()));
449 SceneChannelBuilder channelBuilder = SceneChannelBuilder
450 .create(this.translationProvider,
451 new ChannelGroupUID(thing.getUID(), HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES))
452 .withScenes(scenes).withChannels(allChannels);
454 updateThing(editThing().withChannels(channelBuilder.build()).build());
456 createDeprecatedSceneChannels(scenes);
462 * Create backwards compatible scene channels if any items configured before release 3.2
463 * are still linked. Users should have a reasonable amount of time to migrate to the new
464 * scene channels that are connected to a channel group.
466 private void createDeprecatedSceneChannels(List<Scene> scenes) {
467 if (deprecatedChannelsCreated) {
468 // Only do this once.
471 ChannelGroupUID channelGroupUid = new ChannelGroupUID(thing.getUID(),
472 HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES);
473 for (Scene scene : scenes) {
474 String channelId = Integer.toString(scene.id);
475 ChannelUID newChannelUid = new ChannelUID(channelGroupUid, channelId);
476 ChannelUID deprecatedChannelUid = new ChannelUID(getThing().getUID(), channelId);
477 String description = translationProvider.getText("dynamic-channel.scene-activate.deprecated.description",
479 Channel channel = ChannelBuilder.create(deprecatedChannelUid, CoreItemFactory.SWITCH)
480 .withType(sceneChannelTypeUID).withLabel(scene.getName()).withDescription(description).build();
481 logger.debug("Creating deprecated channel '{}' ('{}') to probe for linked items", deprecatedChannelUid,
483 updateThing(editThing().withChannel(channel).build());
484 if (this.isLinked(deprecatedChannelUid) && !this.isLinked(newChannelUid)) {
485 logger.warn("Created deprecated channel '{}' ('{}'), please link items to '{}' instead",
486 deprecatedChannelUid, scene.getName(), newChannelUid);
488 if (this.isLinked(newChannelUid)) {
489 logger.debug("Removing deprecated channel '{}' ('{}') since new channel '{}' is linked",
490 deprecatedChannelUid, scene.getName(), newChannelUid);
493 logger.debug("Removing deprecated channel '{}' ('{}') since it has no linked items",
494 deprecatedChannelUid, scene.getName());
496 updateThing(editThing().withoutChannel(deprecatedChannelUid).build());
499 deprecatedChannelsCreated = true;
502 private List<SceneCollection> fetchSceneCollections()
503 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
504 SceneCollections sceneCollections = webTargets.getSceneCollections();
505 List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
506 if (sceneCollectionData == null) {
507 throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element");
509 logger.debug("Received data for {} sceneCollections", sceneCollectionData.size());
511 return sceneCollectionData;
514 private List<SceneCollection> updateSceneGroupChannels()
515 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
516 List<SceneCollection> sceneCollections = fetchSceneCollections();
518 if (sceneCollections.size() == sceneCollectionCache.size()
519 && sceneCollectionCache.containsAll(sceneCollections)) {
520 // Duplicates are not allowed. Reordering is not supported.
521 logger.debug("Preserving scene group channels, no changes detected");
522 return sceneCollections;
525 logger.debug("Updating all scene group channels, changes detected");
526 sceneCollectionCache = new CopyOnWriteArrayList<SceneCollection>(sceneCollections);
528 List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
530 .removeIf(c -> HDPowerViewBindingConstants.CHANNEL_GROUP_SCENE_GROUPS.equals(c.getUID().getGroupId()));
532 SceneGroupChannelBuilder channelBuilder = SceneGroupChannelBuilder
533 .create(this.translationProvider,
534 new ChannelGroupUID(thing.getUID(), HDPowerViewBindingConstants.CHANNEL_GROUP_SCENE_GROUPS))
535 .withSceneCollections(sceneCollections).withChannels(allChannels);
537 updateThing(editThing().withChannels(channelBuilder.build()).build());
539 return sceneCollections;
542 private List<ScheduledEvent> fetchScheduledEvents()
543 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
544 ScheduledEvents scheduledEvents = webTargets.getScheduledEvents();
545 List<ScheduledEvent> scheduledEventData = scheduledEvents.scheduledEventData;
546 if (scheduledEventData == null) {
547 throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element");
549 logger.debug("Received data for {} scheduledEvents", scheduledEventData.size());
551 return scheduledEventData;
554 private List<ScheduledEvent> updateAutomationChannels(List<Scene> scenes, List<SceneCollection> sceneCollections)
555 throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
556 List<ScheduledEvent> scheduledEvents = fetchScheduledEvents();
558 if (scheduledEvents.size() == scheduledEventCache.size() && scheduledEventCache.containsAll(scheduledEvents)) {
559 // Duplicates are not allowed. Reordering is not supported.
560 logger.debug("Preserving automation channels, no changes detected");
561 return scheduledEvents;
564 logger.debug("Updating all automation channels, changes detected");
565 scheduledEventCache = new CopyOnWriteArrayList<ScheduledEvent>(scheduledEvents);
567 List<Channel> allChannels = new ArrayList<>(getThing().getChannels());
569 .removeIf(c -> HDPowerViewBindingConstants.CHANNEL_GROUP_AUTOMATIONS.equals(c.getUID().getGroupId()));
570 AutomationChannelBuilder channelBuilder = AutomationChannelBuilder
571 .create(this.translationProvider,
572 new ChannelGroupUID(thing.getUID(), HDPowerViewBindingConstants.CHANNEL_GROUP_AUTOMATIONS))
573 .withScenes(scenes).withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents)
574 .withChannels(allChannels);
575 updateThing(editThing().withChannels(channelBuilder.build()).build());
577 return scheduledEvents;
580 private void updateAutomationStates(List<ScheduledEvent> scheduledEvents) {
581 ChannelGroupUID channelGroupUid = new ChannelGroupUID(thing.getUID(),
582 HDPowerViewBindingConstants.CHANNEL_GROUP_AUTOMATIONS);
583 for (ScheduledEvent scheduledEvent : scheduledEvents) {
584 String scheduledEventId = Integer.toString(scheduledEvent.id);
585 ChannelUID channelUid = new ChannelUID(channelGroupUid, scheduledEventId);
586 updateState(channelUid, scheduledEvent.enabled ? OnOffType.ON : OnOffType.OFF);
590 private Map<Thing, Integer> getShadeThingIdMap() {
591 Map<Thing, Integer> ret = new HashMap<>();
592 getThing().getThings().stream()
593 .filter(thing -> HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thing.getThingTypeUID()))
595 int id = thing.getConfiguration().as(HDPowerViewShadeConfiguration.class).id;
603 private Map<Integer, ShadeData> getIdShadeDataMap(List<ShadeData> shadeData) {
604 Map<Integer, ShadeData> ret = new HashMap<>();
605 for (ShadeData shade : shadeData) {
607 ret.put(shade.id, shade);
613 private void requestRefreshShadePositions() {
614 Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
615 for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
616 Thing thing = item.getKey();
617 if (thing.getStatusInfo().getStatusDetail() == ThingStatusDetail.GONE) {
618 // Skip shades unknown to the Hub.
619 logger.debug("Shade '{}' is unknown, skipping position refresh", item.getValue());
622 ThingHandler handler = thing.getHandler();
623 if (handler instanceof HDPowerViewShadeHandler) {
624 ((HDPowerViewShadeHandler) handler).requestRefreshShadePosition();
626 int shadeId = item.getValue();
627 logger.debug("Shade '{}' handler not initialized", shadeId);
632 private void requestRefreshShadeBatteryLevels() {
633 Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
634 for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
635 Thing thing = item.getKey();
636 if (thing.getStatusInfo().getStatusDetail() == ThingStatusDetail.GONE) {
637 // Skip shades unknown to the Hub.
638 logger.debug("Shade '{}' is unknown, skipping battery level refresh", item.getValue());
641 ThingHandler handler = thing.getHandler();
642 if (handler instanceof HDPowerViewShadeHandler) {
643 ((HDPowerViewShadeHandler) handler).requestRefreshShadeBatteryLevel();
645 int shadeId = item.getValue();
646 logger.debug("Shade '{}' handler not initialized", shadeId);