diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Recall.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Recall.java index 226ae8a65..f489e1cda 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Recall.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Recall.java @@ -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; } diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Resource.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Resource.java index 02330cd20..b749da3c9 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Resource.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/Resource.java @@ -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 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 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 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 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/ResourceType.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/ResourceType.java index a640783b7..f08566c77 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/ResourceType.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/ResourceType.java @@ -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/RecallAction.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SceneRecallAction.java similarity index 86% rename from bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/RecallAction.java rename to bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SceneRecallAction.java index 70fdf4b3b..36987c960 100644 --- 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/SceneRecallAction.java @@ -21,12 +21,13 @@ import org.eclipse.jdt.annotation.Nullable; * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public enum RecallAction { +public enum SceneRecallAction { ACTIVE, DYNAMIC_PALETTE, - STATIC; + STATIC, + UNKNOWN; - public static RecallAction of(@Nullable String value) { + public static SceneRecallAction of(@Nullable String value) { if (value != null) { try { return valueOf(value.toUpperCase()); @@ -34,6 +35,6 @@ public enum RecallAction { // fall through } } - return ACTIVE; + 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 index 000000000..b12c45282 --- /dev/null +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneRecallAction.java @@ -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 index 000000000..470f9fc7d --- /dev/null +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/dto/clip2/enums/SmartSceneState.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2BridgeHandler.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2BridgeHandler.java index aabdf9735..2891cf9e4 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2BridgeHandler.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2BridgeHandler.java @@ -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 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(); 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 93037557c..bab93b79b 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 @@ -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 SUPPORTED_THING_TYPES = Set.of(THING_TYPE_DEVICE, THING_TYPE_ROOM, THING_TYPE_ZONE); + private static final Set 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 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 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 sceneResourceIds = new ConcurrentHashMap<>(); + private final Map 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 allScenes)} + * Fetch the full list of normal resp. smart scenes from the bridge, and call + * {@code updateSceneContributors(List 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 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 allScenes) { if (!disposing && !updateSceneContributorsDone) { sceneContributorsCache.clear(); - sceneResourceIds.clear(); + sceneResourceEntries.clear(); ResourceReference thisReference = getResourceReference(); List 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; } diff --git a/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/Clip2DtoTest.java b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/Clip2DtoTest.java index 84e7623e6..05a3ffed8 100644 --- a/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/Clip2DtoTest.java +++ b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/Clip2DtoTest.java @@ -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 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 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 index 000000000..f89eb9cf9 --- /dev/null +++ b/bundles/org.openhab.binding.hue/src/test/resources/smart_scene.json @@ -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" + } + ] +}