From e783136b9cb8f5f02e489a8327120333380c78bf Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 24 Aug 2024 11:34:56 +0100 Subject: [PATCH] [hue] Support dynamic add/delete of scenes (#17302) Signed-off-by: AndrewFG Signed-off-by: Ciprian Pascu --- .../internal/handler/Clip2ThingHandler.java | 68 ++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java index 7944e5085f2..e4dcc2c5e35 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java @@ -50,6 +50,7 @@ import org.openhab.binding.hue.internal.api.dto.clip2.ResourceReference; import org.openhab.binding.hue.internal.api.dto.clip2.Resources; import org.openhab.binding.hue.internal.api.dto.clip2.TimedEffects; import org.openhab.binding.hue.internal.api.dto.clip2.enums.ActionType; +import org.openhab.binding.hue.internal.api.dto.clip2.enums.ContentType; import org.openhab.binding.hue.internal.api.dto.clip2.enums.EffectType; import org.openhab.binding.hue.internal.api.dto.clip2.enums.ResourceType; import org.openhab.binding.hue.internal.api.dto.clip2.enums.SceneRecallAction; @@ -114,6 +115,14 @@ public class Clip2ThingHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(Clip2ThingHandler.class); + // flag values for logging resource consumption + private static final int FLAG_PROPERTIES_UPDATE = 1; + private static final int FLAG_DEPENDENCIES_UPDATE = 2; + private static final int FLAG_CACHE_UPDATE = 4; + private static final int FLAG_CHANNELS_UPDATE = 8; + private static final int FLAG_SCENE_ADD = 16; + private static final int FLAG_SCENE_DELETE = 32; + /** * A map of service Resources whose state contributes to the overall state of this thing. It is a map between the * resource ID (string) and a Resource object containing the last known state. e.g. a DEVICE thing may support a @@ -667,36 +676,71 @@ public class Clip2ThingHandler extends BaseThingHandler { if (disposing) { return; } - boolean resourceConsumed = false; + int resourceConsumedFlags = 0; if (resourceId.equals(resource.getId())) { if (resource.hasFullState()) { thisResource = resource; if (!updatePropertiesDone) { updateProperties(resource); - resourceConsumed = updatePropertiesDone; + resourceConsumedFlags = updatePropertiesDone ? FLAG_PROPERTIES_UPDATE : 0; } } if (!updateDependenciesDone) { - resourceConsumed = true; + resourceConsumedFlags |= FLAG_DEPENDENCIES_UPDATE; cancelTask(updateDependenciesTask, false); updateDependenciesTask = scheduler.submit(() -> updateDependencies()); } } else { + if (SUPPORTED_SCENE_TYPES.contains(resource.getType())) { + resourceConsumedFlags = checkSceneResourceAddDelete(resource); + } Resource cachedResource = getResourceFromCache(resource); if (cachedResource != null) { Setters.setResource(resource, cachedResource); - resourceConsumed = updateChannels && updateChannels(resource); + resourceConsumedFlags |= FLAG_CACHE_UPDATE; + resourceConsumedFlags |= updateChannels && updateChannels(resource) ? FLAG_CHANNELS_UPDATE : 0; putResourceToCache(resource); if (ResourceType.LIGHT == resource.getType() && !updateLightPropertiesDone) { updateLightProperties(resource); } } } - if (resourceConsumed) { - logger.debug("{} -> onResource() consumed resource {}", resourceId, resource); + if (resourceConsumedFlags != 0) { + logger.debug("{} -> onResource() consumed resource {}, flags:{}", resourceId, resource, + resourceConsumedFlags); } } + /** + * Check if a scene resource is of type 'ADD or 'DELETE' and either add it to, or delete it from, the two scene + * resource caches; and refresh the scene channel state description selection options. + * + * @param sceneResource the respective scene resource + * @return a flag value indicating if the scene was added or deleted + */ + private int checkSceneResourceAddDelete(Resource sceneResource) { + switch (sceneResource.getContentType()) { + case ADD: + if (getResourceReference().equals(sceneResource.getGroup())) { + sceneResource.setContentType(ContentType.FULL_STATE); + sceneContributorsCache.put(sceneResource.getId(), sceneResource); + sceneResourceEntries.put(sceneResource.getName(), sceneResource); + updateSceneChannelStateDescription(); + return FLAG_SCENE_ADD; + } + break; + case DELETE: + Resource deletedScene = sceneContributorsCache.remove(sceneResource.getId()); + if (Objects.nonNull(deletedScene)) { + sceneResourceEntries.remove(deletedScene.getName()); + updateSceneChannelStateDescription(); + return FLAG_SCENE_DELETE; + } + default: + } + return 0; + } + private void putResourceToCache(Resource resource) { if (SUPPORTED_SCENE_TYPES.contains(resource.getType())) { sceneContributorsCache.put(resource.getId(), resource); @@ -1197,6 +1241,14 @@ public class Clip2ThingHandler extends BaseThingHandler { } } + /** + * Update the scene channel state description selection options + */ + private void updateSceneChannelStateDescription() { + stateDescriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), CHANNEL_2_SCENE), + sceneResourceEntries.keySet().stream().map(n -> new StateOption(n, n)).collect(Collectors.toList())); + } + /** * Fetch the full list of normal resp. smart scenes from the bridge, and call * {@code updateSceneContributors(List allScenes)} @@ -1249,9 +1301,7 @@ public class Clip2ThingHandler extends BaseThingHandler { } updateState(CHANNEL_2_SCENE, state, true); - - stateDescriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), CHANNEL_2_SCENE), scenes - .stream().map(s -> s.getName()).map(n -> new StateOption(n, n)).collect(Collectors.toList())); + updateSceneChannelStateDescription(); logger.debug("{} -> updateSceneContributors() found {} normal resp. smart scenes", resourceId, scenes.size());