]> git.basschouten.com Git - openhab-addons.git/commitdiff
[hue] Support smart scenes (#15388)
authorAndrew Fiddian-Green <software@whitebear.ch>
Tue, 17 Oct 2023 12:35:21 +0000 (13:35 +0100)
committerGitHub <noreply@github.com>
Tue, 17 Oct 2023 12:35:21 +0000 (14:35 +0200)
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Recall.java
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Resource.java
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/RecallAction.java [deleted file]
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/ResourceType.java
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SceneRecallAction.java [new file with mode: 0644]
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneRecallAction.java [new file with mode: 0644]
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneState.java [new file with mode: 0644]
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2BridgeHandler.java
bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java
bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/Clip2DtoTest.java
bundles/org.openhab.binding.hue/src/test/resources/smart_scene.json [new file with mode: 0644]

index 226ae8a6576c3bfd321ea7bc97114a8049778606..f489e1cdae48bee1bf7b3d8468abf4aa68ff7035 100644 (file)
@@ -16,10 +16,11 @@ import java.time.Duration;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.hue.internal.dto.clip2.enums.RecallAction;
+import org.openhab.binding.hue.internal.dto.clip2.enums.SceneRecallAction;
+import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneRecallAction;
 
 /**
- * DTO for scene recall.
+ * DTO for scene and smart scene recall.
  *
  * @author Andrew Fiddian-Green - Initial contribution
  */
@@ -29,7 +30,12 @@ public class Recall {
     private @Nullable @SuppressWarnings("unused") String status;
     private @Nullable @SuppressWarnings("unused") Long duration;
 
-    public Recall setAction(RecallAction action) {
+    public Recall setAction(SceneRecallAction action) {
+        this.action = action.name().toLowerCase();
+        return this;
+    }
+
+    public Recall setAction(SmartSceneRecallAction action) {
         this.action = action.name().toLowerCase();
         return this;
     }
index 02330cd20d9be52cc0b14327297f4cc578bd547d..b749da3c9bd706e8893b37022d7be56ca872a6a0 100644 (file)
@@ -25,8 +25,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.hue.internal.dto.clip2.enums.ActionType;
 import org.openhab.binding.hue.internal.dto.clip2.enums.EffectType;
-import org.openhab.binding.hue.internal.dto.clip2.enums.RecallAction;
 import org.openhab.binding.hue.internal.dto.clip2.enums.ResourceType;
+import org.openhab.binding.hue.internal.dto.clip2.enums.SceneRecallAction;
+import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneRecallAction;
+import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneState;
 import org.openhab.binding.hue.internal.dto.clip2.enums.ZigbeeStatus;
 import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
 import org.openhab.core.library.types.DecimalType;
@@ -94,6 +96,7 @@ public class Resource {
     private @Nullable List<ResourceReference> children;
     private @Nullable JsonElement status;
     private @Nullable @SuppressWarnings("unused") Dynamics dynamics;
+    private @Nullable String state;
 
     /**
      * Constructor
@@ -507,8 +510,34 @@ public class Resource {
      * @return either 'UnDefType.NULL', a StringType containing the (active) scene name, or 'UnDefType.UNDEF'.
      */
     public State getSceneState() {
-        Optional<Boolean> active = getSceneActive();
-        return active.isEmpty() ? UnDefType.NULL : active.get() ? new StringType(getName()) : UnDefType.UNDEF;
+        return getSceneActive().map(a -> a ? new StringType(getName()) : UnDefType.UNDEF).orElse(UnDefType.NULL);
+    }
+
+    /**
+     * Check if the smart scene resource contains a 'state' element. If such an element is present, returns a Boolean
+     * Optional whose value depends on the value of that element, or an empty Optional if it is not.
+     *
+     * @return true, false, or empty.
+     */
+    public Optional<Boolean> getSmartSceneActive() {
+        if (ResourceType.SMART_SCENE == getType()) {
+            String state = this.state;
+            if (Objects.nonNull(state)) {
+                return Optional.of(SmartSceneState.ACTIVE == SmartSceneState.of(state));
+            }
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * If the getSmartSceneActive() optional result is empty return 'UnDefType.NULL'. Otherwise if the optional result
+     * is present and 'true' (i.e. the scene is active) return the smart scene name. Or finally (the optional result is
+     * present and 'false') return 'UnDefType.UNDEF'.
+     *
+     * @return either 'UnDefType.NULL', a StringType containing the (active) scene name, or 'UnDefType.UNDEF'.
+     */
+    public State getSmartSceneState() {
+        return getSmartSceneActive().map(a -> a ? new StringType(getName()) : UnDefType.UNDEF).orElse(UnDefType.NULL);
     }
 
     public List<ResourceReference> getServiceReferences() {
@@ -649,7 +678,13 @@ public class Resource {
         this.on = on;
     }
 
-    public Resource setRecallAction(RecallAction recallAction) {
+    public Resource setRecallAction(SceneRecallAction recallAction) {
+        Recall recall = this.recall;
+        this.recall = ((Objects.nonNull(recall) ? recall : new Recall())).setAction(recallAction);
+        return this;
+    }
+
+    public Resource setRecallAction(SmartSceneRecallAction recallAction) {
         Recall recall = this.recall;
         this.recall = ((Objects.nonNull(recall) ? recall : new Recall())).setAction(recallAction);
         return this;
diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/RecallAction.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/RecallAction.java
deleted file mode 100644 (file)
index 70fdf4b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.hue.internal.dto.clip2.enums;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Enum for scene recall actions.
- *
- * @author Andrew Fiddian-Green - Initial contribution
- */
-@NonNullByDefault
-public enum RecallAction {
-    ACTIVE,
-    DYNAMIC_PALETTE,
-    STATIC;
-
-    public static RecallAction of(@Nullable String value) {
-        if (value != null) {
-            try {
-                return valueOf(value.toUpperCase());
-            } catch (IllegalArgumentException e) {
-                // fall through
-            }
-        }
-        return ACTIVE;
-    }
-}
index a640783b78e58cc8c19ae69dcce648cb6f982bab..f08566c77f9f21959c2d12c07b84396ecb6457c9 100644 (file)
@@ -47,6 +47,7 @@ public enum ResourceType {
     ROOM,
     RELATIVE_ROTARY,
     SCENE,
+    SMART_SCENE,
     TEMPERATURE,
     ZGP_CONNECTIVITY,
     ZIGBEE_CONNECTIVITY,
diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SceneRecallAction.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SceneRecallAction.java
new file mode 100644 (file)
index 0000000..36987c9
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hue.internal.dto.clip2.enums;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Enum for scene recall actions.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public enum SceneRecallAction {
+    ACTIVE,
+    DYNAMIC_PALETTE,
+    STATIC,
+    UNKNOWN;
+
+    public static SceneRecallAction of(@Nullable String value) {
+        if (value != null) {
+            try {
+                return valueOf(value.toUpperCase());
+            } catch (IllegalArgumentException e) {
+                // fall through
+            }
+        }
+        return UNKNOWN;
+    }
+}
diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneRecallAction.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneRecallAction.java
new file mode 100644 (file)
index 0000000..b12c452
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hue.internal.dto.clip2.enums;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Enum for smart scene recall actions.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public enum SmartSceneRecallAction {
+    ACTIVATE,
+    DEACTIVATE,
+    UNKNOWN;
+
+    public static SmartSceneRecallAction of(@Nullable String value) {
+        if (value != null) {
+            try {
+                return valueOf(value.toUpperCase());
+            } catch (IllegalArgumentException e) {
+                // fall through
+            }
+        }
+        return UNKNOWN;
+    }
+}
diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneState.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneState.java
new file mode 100644 (file)
index 0000000..470f9fc
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hue.internal.dto.clip2.enums;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Enum for 'smart_scene' states.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public enum SmartSceneState {
+    INACTIVE,
+    ACTIVE,
+    UNKNOWN;
+
+    public static SmartSceneState of(@Nullable String value) {
+        if (value != null) {
+            try {
+                return valueOf(value.toUpperCase());
+            } catch (IllegalArgumentException e) {
+                // fall through
+            }
+        }
+        return UNKNOWN;
+    }
+}
index aabdf973577c68c331ff8f31e81476b657061d60..2891cf9e4ea7b18f23ea89a336fe1a44b089ab95 100644 (file)
@@ -91,6 +91,7 @@ public class Clip2BridgeHandler extends BaseBridgeHandler {
     private static final ResourceReference BRIDGE = new ResourceReference().setType(ResourceType.BRIDGE);
     private static final ResourceReference BRIDGE_HOME = new ResourceReference().setType(ResourceType.BRIDGE_HOME);
     private static final ResourceReference SCENE = new ResourceReference().setType(ResourceType.SCENE);
+    private static final ResourceReference SMART_SCENE = new ResourceReference().setType(ResourceType.SMART_SCENE);
 
     /**
      * List of resource references that need to be mass down loaded.
@@ -729,9 +730,19 @@ public class Clip2BridgeHandler extends BaseBridgeHandler {
             for (ResourceReference reference : MASS_DOWNLOAD_RESOURCE_REFERENCES) {
                 ResourceType resourceType = reference.getType();
                 List<Resource> resourceList = bridge.getResources(reference).getResources();
-                if (resourceType == ResourceType.ZONE) {
-                    // add special 'All Lights' zone to the zone resource list
-                    resourceList.addAll(bridge.getResources(BRIDGE_HOME).getResources());
+                switch (resourceType) {
+                    case ZONE:
+                        // add special 'All Lights' zone to the zone resource list
+                        resourceList.addAll(bridge.getResources(BRIDGE_HOME).getResources());
+                        break;
+
+                    case SCENE:
+                        // add 'smart scenes' to the scene resource list
+                        resourceList.addAll(bridge.getResources(SMART_SCENE).getResources());
+                        break;
+
+                    default:
+                        break;
                 }
                 getThing().getThings().forEach(thing -> {
                     ThingHandler handler = thing.getHandler();
index 93037557c26e492874b65ca6a9fd492a5e6bb919..bab93b79beba0ea2a44dea6984d4138d536b8c8a 100644 (file)
@@ -17,6 +17,7 @@ import static org.openhab.binding.hue.internal.HueBindingConstants.*;
 import java.math.BigDecimal;
 import java.time.Duration;
 import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -49,8 +50,9 @@ import org.openhab.binding.hue.internal.dto.clip2.Resources;
 import org.openhab.binding.hue.internal.dto.clip2.TimedEffects;
 import org.openhab.binding.hue.internal.dto.clip2.enums.ActionType;
 import org.openhab.binding.hue.internal.dto.clip2.enums.EffectType;
-import org.openhab.binding.hue.internal.dto.clip2.enums.RecallAction;
 import org.openhab.binding.hue.internal.dto.clip2.enums.ResourceType;
+import org.openhab.binding.hue.internal.dto.clip2.enums.SceneRecallAction;
+import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneRecallAction;
 import org.openhab.binding.hue.internal.dto.clip2.enums.ZigbeeStatus;
 import org.openhab.binding.hue.internal.dto.clip2.helper.Setters;
 import org.openhab.binding.hue.internal.exceptions.ApiException;
@@ -99,6 +101,8 @@ public class Clip2ThingHandler extends BaseThingHandler {
     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_DEVICE, THING_TYPE_ROOM,
             THING_TYPE_ZONE);
 
+    private static final Set<ResourceType> SUPPORTED_SCENE_TYPES = Set.of(ResourceType.SCENE, ResourceType.SMART_SCENE);
+
     private static final Duration DYNAMICS_ACTIVE_WINDOW = Duration.ofSeconds(10);
 
     private static final String LK_WISER_DIMMER_MODEL_ID = "LK Dimmer";
@@ -136,16 +140,16 @@ public class Clip2ThingHandler extends BaseThingHandler {
     private final Set<String> supportedChannelIdSet = new HashSet<>();
 
     /**
-     * A map of scene IDs and respective scene Resources for the scenes that contribute to and command this thing. It is
-     * a map between the resource ID (string) and a Resource object containing the scene's last known state.
+     * A map of scene IDs versus scene Resources for the scenes that contribute to and command this thing. It is a map
+     * between the resource ID (string) and a Resource object containing the scene's last known state.
      */
     private final Map<String, Resource> sceneContributorsCache = new ConcurrentHashMap<>();
 
     /**
-     * A map of scene names versus Resource IDs for the scenes that contribute to and command this thing. e.g. a command
-     * for a scene named 'Energize' shall be sent to the respective SCENE resource ID.
+     * A map of scene names versus scene Resources for the scenes that contribute to and command this thing. e.g. a
+     * command for a scene named 'Energize' shall be sent to the respective SCENE resource ID.
      */
-    private final Map<String, String> sceneResourceIds = new ConcurrentHashMap<>();
+    private final Map<String, Resource> sceneResourceEntries = new ConcurrentHashMap<>();
 
     /**
      * A list of API v1 thing channel UIDs that are linked to items. It is used in the process of replicating the
@@ -248,7 +252,7 @@ public class Clip2ThingHandler extends BaseThingHandler {
         updateServiceContributorsTask = null;
         legacyLinkedChannelUIDs.clear();
         sceneContributorsCache.clear();
-        sceneResourceIds.clear();
+        sceneResourceEntries.clear();
         supportedChannelIdSet.clear();
         commandResourceIds.clear();
         serviceContributorsCache.clear();
@@ -426,9 +430,23 @@ public class Clip2ThingHandler extends BaseThingHandler {
 
             case CHANNEL_2_SCENE:
                 if (command instanceof StringType) {
-                    putResourceId = sceneResourceIds.get(((StringType) command).toString());
-                    if (Objects.nonNull(putResourceId)) {
-                        putResource = new Resource(ResourceType.SCENE).setRecallAction(RecallAction.ACTIVE);
+                    Resource scene = sceneResourceEntries.get(((StringType) command).toString());
+                    if (Objects.nonNull(scene)) {
+                        ResourceType putResourceType = scene.getType();
+                        putResource = new Resource(putResourceType);
+                        switch (putResourceType) {
+                            case SCENE:
+                                putResource.setRecallAction(SceneRecallAction.ACTIVE);
+                                break;
+                            case SMART_SCENE:
+                                putResource.setRecallAction(SmartSceneRecallAction.ACTIVATE);
+                                break;
+                            default:
+                                logger.debug("{} -> handleCommand() type '{}' is not a supported scene type",
+                                        resourceId, putResourceType);
+                                return;
+                        }
+                        putResourceId = scene.getId();
                     }
                 }
                 break;
@@ -624,7 +642,7 @@ public class Clip2ThingHandler extends BaseThingHandler {
                     cancelTask(updateDependenciesTask, false);
                     updateDependenciesTask = scheduler.submit(() -> updateDependencies());
                 }
-            } else if (ResourceType.SCENE == resource.getType()) {
+            } else if (SUPPORTED_SCENE_TYPES.contains(resource.getType())) {
                 Resource cachedScene = sceneContributorsCache.get(incomingResourceId);
                 if (Objects.nonNull(cachedScene)) {
                     Setters.setResource(resource, cachedScene);
@@ -876,6 +894,10 @@ public class Clip2ThingHandler extends BaseThingHandler {
                 updateState(CHANNEL_2_SCENE, resource.getSceneState(), fullUpdate);
                 break;
 
+            case SMART_SCENE:
+                updateState(CHANNEL_2_SCENE, resource.getSmartSceneState(), fullUpdate);
+                break;
+
             default:
                 return false;
         }
@@ -1096,7 +1118,8 @@ public class Clip2ThingHandler extends BaseThingHandler {
     }
 
     /**
-     * Fetch the full list of scenes from the bridge, and call {@code updateSceneContributors(List<Resource> allScenes)}
+     * Fetch the full list of normal resp. smart scenes from the bridge, and call
+     * {@code updateSceneContributors(List<Resource> allScenes)}
      *
      * @throws ApiException if a communication error occurred.
      * @throws AssetNotLoadedException if one of the assets is not loaded.
@@ -1104,22 +1127,26 @@ public class Clip2ThingHandler extends BaseThingHandler {
      */
     public boolean updateSceneContributors() throws ApiException, AssetNotLoadedException, InterruptedException {
         if (!disposing && !updateSceneContributorsDone) {
-            ResourceReference scenesReference = new ResourceReference().setType(ResourceType.SCENE);
-            updateSceneContributors(getBridgeHandler().getResources(scenesReference).getResources());
+            List<Resource> allScenes = new ArrayList<>();
+            for (ResourceType type : SUPPORTED_SCENE_TYPES) {
+                allScenes.addAll(getBridgeHandler().getResources(new ResourceReference().setType(type)).getResources());
+            }
+            updateSceneContributors(allScenes);
         }
         return updateSceneContributorsDone;
     }
 
     /**
-     * Process the incoming list of scene resources to find those scenes which contribute to this thing. And if there
-     * are any, include a scene channel in the supported channel list, and populate its respective state options.
+     * Process the incoming list of normal resp. smart scene resources to find those which contribute to this thing. And
+     * if there are any, include a scene channel in the supported channel list, and populate its respective state
+     * options.
      *
-     * @param allScenes the full list of scene resources.
+     * @param allScenes the full list of normal resp. smart scene resources.
      */
     public synchronized boolean updateSceneContributors(List<Resource> allScenes) {
         if (!disposing && !updateSceneContributorsDone) {
             sceneContributorsCache.clear();
-            sceneResourceIds.clear();
+            sceneResourceEntries.clear();
 
             ResourceReference thisReference = getResourceReference();
             List<Resource> scenes = allScenes.stream().filter(s -> thisReference.equals(s.getGroup()))
@@ -1127,16 +1154,18 @@ public class Clip2ThingHandler extends BaseThingHandler {
 
             if (!scenes.isEmpty()) {
                 sceneContributorsCache.putAll(scenes.stream().collect(Collectors.toMap(s -> s.getId(), s -> s)));
-                sceneResourceIds.putAll(scenes.stream().collect(Collectors.toMap(s -> s.getName(), s -> s.getId())));
+                sceneResourceEntries.putAll(scenes.stream().collect(Collectors.toMap(s -> s.getName(), s -> s)));
 
                 State state = scenes.stream().filter(s -> s.getSceneActive().orElse(false)).map(s -> s.getSceneState())
                         .findAny().orElse(UnDefType.UNDEF);
+
                 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()));
 
-                logger.debug("{} -> updateSceneContributors() found {} scenes", resourceId, scenes.size());
+                logger.debug("{} -> updateSceneContributors() found {} normal resp. smart scenes", resourceId,
+                        scenes.size());
             }
             updateSceneContributorsDone = true;
         }
index 84e7623e61ee242786354f30e068aeae4599ec06..05a3ffed8864e947321609eae7e16d9f51012fe7 100644 (file)
@@ -458,6 +458,28 @@ class Clip2DtoTest {
         assertEquals(OnOffType.ON, action.getOnOffState());
     }
 
+    @Test
+    void testSmartScene() {
+        String json = load(ResourceType.SMART_SCENE.name().toLowerCase());
+        Resources resources = GSON.fromJson(json, Resources.class);
+        assertNotNull(resources);
+        List<Resource> list = resources.getResources();
+        assertNotNull(list);
+        assertEquals(1, list.size());
+        Resource item = list.get(0);
+        ResourceReference group = item.getGroup();
+        assertNotNull(group);
+        String groupId = group.getId();
+        assertNotNull(groupId);
+        assertFalse(groupId.isBlank());
+        ResourceType type = group.getType();
+        assertNotNull(type);
+        assertEquals(ResourceType.ROOM, type);
+        Optional<Boolean> state = item.getSmartSceneActive();
+        assertTrue(state.isPresent());
+        assertFalse(state.get());
+    }
+
     @Test
     void testSensor2Motion() {
         String json = load(ResourceType.MOTION.name().toLowerCase());
diff --git a/bundles/org.openhab.binding.hue/src/test/resources/smart_scene.json b/bundles/org.openhab.binding.hue/src/test/resources/smart_scene.json
new file mode 100644 (file)
index 0000000..f89eb9c
--- /dev/null
@@ -0,0 +1,126 @@
+{
+    "errors": [
+    ],
+    "data": [
+        {
+            "id": "0707ec71-8d7a-4bf8-91ca-71db273ddfe9",
+            "type": "smart_scene",
+            "metadata": {
+                "name": "Natural light",
+                "image": {
+                    "rid": "eb014820-a902-4652-8ca7-6e29c03b87a1",
+                    "rtype": "public_image"
+                }
+            },
+            "group": {
+                "rid": "1166743f-fe3d-47d1-bd7d-b5bb378098cc",
+                "rtype": "room"
+            },
+            "week_timeslots": [
+                {
+                    "timeslots": [
+                        {
+                            "start_time": {
+                                "kind": "time",
+                                "time": {
+                                    "hour": 7,
+                                    "minute": 0,
+                                    "second": 0
+                                }
+                            },
+                            "target": {
+                                "rid": "9c633a2d-f7bf-4bba-b5ee-a4d69ce6e050",
+                                "rtype": "scene"
+                            }
+                        },
+                        {
+                            "start_time": {
+                                "kind": "time",
+                                "time": {
+                                    "hour": 10,
+                                    "minute": 0,
+                                    "second": 0
+                                }
+                            },
+                            "target": {
+                                "rid": "7616d9fe-4889-472b-93bf-5090c19fb902",
+                                "rtype": "scene"
+                            }
+                        },
+                        {
+                            "start_time": {
+                                "kind": "sunset",
+                                "time": {
+                                    "hour": 0,
+                                    "minute": 0,
+                                    "second": 0
+                                }
+                            },
+                            "target": {
+                                "rid": "eeabcfdb-97db-4e4a-a94a-fe1fea8e6c2c",
+                                "rtype": "scene"
+                            }
+                        },
+                        {
+                            "start_time": {
+                                "kind": "time",
+                                "time": {
+                                    "hour": 20,
+                                    "minute": 0,
+                                    "second": 0
+                                }
+                            },
+                            "target": {
+                                "rid": "2f474323-0c6b-4361-a8c7-b52da902cd7b",
+                                "rtype": "scene"
+                            }
+                        },
+                        {
+                            "start_time": {
+                                "kind": "time",
+                                "time": {
+                                    "hour": 22,
+                                    "minute": 0,
+                                    "second": 0
+                                }
+                            },
+                            "target": {
+                                "rid": "6fa2a278-43ec-4586-bdd8-2fe6f4ea5f85",
+                                "rtype": "scene"
+                            }
+                        },
+                        {
+                            "start_time": {
+                                "kind": "time",
+                                "time": {
+                                    "hour": 0,
+                                    "minute": 0,
+                                    "second": 0
+                                }
+                            },
+                            "target": {
+                                "rid": "6ca12be2-c547-4722-8db0-6e12f20688f3",
+                                "rtype": "scene"
+                            }
+                        }
+                    ],
+                    "recurrence": [
+                        "sunday",
+                        "monday",
+                        "tuesday",
+                        "wednesday",
+                        "thursday",
+                        "friday",
+                        "saturday"
+                    ]
+                }
+            ],
+            "transition_duration": 60000,
+            "active_timeslot": {
+                "timeslot_id": 1,
+                "weekday": "wednesday"
+            },
+            "state": "inactive"
+        }
+    ]
+}