]> git.basschouten.com Git - openhab-addons.git/blob
05a3ffed8864e947321609eae7e16d9f51012fe7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.hue.internal.clip2;
14
15 import static org.junit.jupiter.api.Assertions.*;
16
17 import java.io.BufferedReader;
18 import java.io.FileReader;
19 import java.io.IOException;
20 import java.lang.reflect.Field;
21 import java.time.Duration;
22 import java.util.List;
23 import java.util.Optional;
24 import java.util.Set;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.junit.jupiter.api.Test;
28 import org.openhab.binding.hue.internal.dto.clip2.ActionEntry;
29 import org.openhab.binding.hue.internal.dto.clip2.Alerts;
30 import org.openhab.binding.hue.internal.dto.clip2.Button;
31 import org.openhab.binding.hue.internal.dto.clip2.Dimming;
32 import org.openhab.binding.hue.internal.dto.clip2.Effects;
33 import org.openhab.binding.hue.internal.dto.clip2.Event;
34 import org.openhab.binding.hue.internal.dto.clip2.LightLevel;
35 import org.openhab.binding.hue.internal.dto.clip2.MetaData;
36 import org.openhab.binding.hue.internal.dto.clip2.MirekSchema;
37 import org.openhab.binding.hue.internal.dto.clip2.Motion;
38 import org.openhab.binding.hue.internal.dto.clip2.Power;
39 import org.openhab.binding.hue.internal.dto.clip2.ProductData;
40 import org.openhab.binding.hue.internal.dto.clip2.RelativeRotary;
41 import org.openhab.binding.hue.internal.dto.clip2.Resource;
42 import org.openhab.binding.hue.internal.dto.clip2.ResourceReference;
43 import org.openhab.binding.hue.internal.dto.clip2.Resources;
44 import org.openhab.binding.hue.internal.dto.clip2.Rotation;
45 import org.openhab.binding.hue.internal.dto.clip2.RotationEvent;
46 import org.openhab.binding.hue.internal.dto.clip2.Temperature;
47 import org.openhab.binding.hue.internal.dto.clip2.TimedEffects;
48 import org.openhab.binding.hue.internal.dto.clip2.enums.ActionType;
49 import org.openhab.binding.hue.internal.dto.clip2.enums.Archetype;
50 import org.openhab.binding.hue.internal.dto.clip2.enums.BatteryStateType;
51 import org.openhab.binding.hue.internal.dto.clip2.enums.ButtonEventType;
52 import org.openhab.binding.hue.internal.dto.clip2.enums.DirectionType;
53 import org.openhab.binding.hue.internal.dto.clip2.enums.EffectType;
54 import org.openhab.binding.hue.internal.dto.clip2.enums.ResourceType;
55 import org.openhab.binding.hue.internal.dto.clip2.enums.RotationEventType;
56 import org.openhab.binding.hue.internal.dto.clip2.enums.ZigbeeStatus;
57 import org.openhab.binding.hue.internal.dto.clip2.helper.Setters;
58 import org.openhab.core.library.types.DecimalType;
59 import org.openhab.core.library.types.HSBType;
60 import org.openhab.core.library.types.OnOffType;
61 import org.openhab.core.library.types.PercentType;
62 import org.openhab.core.library.types.QuantityType;
63 import org.openhab.core.library.types.StringType;
64 import org.openhab.core.types.State;
65 import org.openhab.core.types.UnDefType;
66 import org.openhab.core.util.ColorUtil;
67
68 import com.google.gson.Gson;
69 import com.google.gson.JsonElement;
70 import com.google.gson.JsonObject;
71 import com.google.gson.JsonParser;
72 import com.google.gson.JsonSyntaxException;
73
74 /**
75  * JUnit test for CLIP 2 DTOs.
76  *
77  * @author Andrew Fiddian-Green - Initial contribution
78  */
79 @NonNullByDefault
80 class Clip2DtoTest {
81
82     private static final Gson GSON = new Gson();
83     private static final Double MINIMUM_DIMMING_LEVEL = Double.valueOf(12.34f);
84
85     /**
86      * Load the test JSON payload string from a file
87      */
88     private String load(String fileName) {
89         try (FileReader file = new FileReader(String.format("src/test/resources/%s.json", fileName));
90                 BufferedReader reader = new BufferedReader(file)) {
91             StringBuilder builder = new StringBuilder();
92             String line;
93             while ((line = reader.readLine()) != null) {
94                 builder.append(line).append("\n");
95             }
96             return builder.toString();
97         } catch (IOException e) {
98             fail(e.getMessage());
99         }
100         return "";
101     }
102
103     @Test
104     void testButton() {
105         String json = load(ResourceType.BUTTON.name().toLowerCase());
106         Resources resources = GSON.fromJson(json, Resources.class);
107         assertNotNull(resources);
108         List<Resource> list = resources.getResources();
109         assertNotNull(list);
110         assertEquals(43, list.size());
111         Resource item = list.get(0);
112         assertEquals(ResourceType.BUTTON, item.getType());
113         Button button = item.getButton();
114         assertNotNull(button);
115         assertEquals(ButtonEventType.SHORT_RELEASE, button.getLastEvent());
116     }
117
118     @Test
119     void testDevice() {
120         String json = load(ResourceType.DEVICE.name().toLowerCase());
121         Resources resources = GSON.fromJson(json, Resources.class);
122         assertNotNull(resources);
123         List<Resource> list = resources.getResources();
124         assertNotNull(list);
125         assertEquals(34, list.size());
126         boolean itemFound = false;
127         for (Resource item : list) {
128             assertEquals(ResourceType.DEVICE, item.getType());
129             ProductData productData = item.getProductData();
130             assertNotNull(productData);
131             if (productData.getProductArchetype() == Archetype.BRIDGE_V2) {
132                 itemFound = true;
133                 assertEquals("BSB002", productData.getModelId());
134                 assertEquals("Signify Netherlands B.V.", productData.getManufacturerName());
135                 assertEquals("Philips hue", productData.getProductName());
136                 assertNull(productData.getHardwarePlatformType());
137                 assertTrue(productData.getCertified());
138                 assertEquals("1.53.1953188020", productData.getSoftwareVersion());
139                 break;
140             }
141         }
142         assertTrue(itemFound);
143     }
144
145     @Test
146     void testDevicePower() {
147         String json = load(ResourceType.DEVICE_POWER.name().toLowerCase());
148         Resources resources = GSON.fromJson(json, Resources.class);
149         assertNotNull(resources);
150         List<Resource> list = resources.getResources();
151         assertNotNull(list);
152         assertEquals(16, list.size());
153         Resource item = list.get(0);
154         assertEquals(ResourceType.DEVICE_POWER, item.getType());
155         Power power = item.getPowerState();
156         assertNotNull(power);
157         assertEquals(60, power.getBatteryLevel());
158         assertEquals(BatteryStateType.NORMAL, power.getBatteryState());
159     }
160
161     @Test
162     void testGroupedLight() {
163         String json = load(ResourceType.GROUPED_LIGHT.name().toLowerCase());
164         Resources resources = GSON.fromJson(json, Resources.class);
165         assertNotNull(resources);
166         List<Resource> list = resources.getResources();
167         assertNotNull(list);
168         assertEquals(15, list.size());
169         int itemsFound = 0;
170         for (Resource item : list) {
171             assertEquals(ResourceType.GROUPED_LIGHT, item.getType());
172             Alerts alert;
173             switch (item.getId()) {
174                 case "db4fd630-3798-40de-b642-c1ef464bf770":
175                     itemsFound++;
176                     assertEquals(OnOffType.OFF, item.getOnOffState());
177                     assertEquals(PercentType.ZERO, item.getBrightnessState());
178                     alert = item.getAlerts();
179                     assertNotNull(alert);
180                     for (ActionType actionValue : alert.getActionValues()) {
181                         assertEquals(ActionType.BREATHE, actionValue);
182                     }
183                     break;
184                 case "9228d710-3c54-4ae4-8c88-bfe57d8fd220":
185                     itemsFound++;
186                     assertEquals(OnOffType.ON, item.getOnOffState());
187                     assertEquals(PercentType.HUNDRED, item.getBrightnessState());
188                     alert = item.getAlerts();
189                     assertNotNull(alert);
190                     for (ActionType actionValue : alert.getActionValues()) {
191                         assertEquals(ActionType.BREATHE, actionValue);
192                     }
193                     break;
194                 default:
195             }
196         }
197         assertEquals(2, itemsFound);
198     }
199
200     @Test
201     void testLight() {
202         String json = load(ResourceType.LIGHT.name().toLowerCase());
203         Resources resources = GSON.fromJson(json, Resources.class);
204         assertNotNull(resources);
205         List<Resource> list = resources.getResources();
206         assertNotNull(list);
207         assertEquals(17, list.size());
208         int itemFoundCount = 0;
209         for (Resource item : list) {
210             assertEquals(ResourceType.LIGHT, item.getType());
211             MetaData metaData = item.getMetaData();
212             assertNotNull(metaData);
213             String name = metaData.getName();
214             assertNotNull(name);
215             State state;
216             if (name.contains("Bay Window Lamp")) {
217                 itemFoundCount++;
218                 assertEquals(ResourceType.LIGHT, item.getType());
219                 assertEquals(OnOffType.OFF, item.getOnOffState());
220                 state = item.getBrightnessState();
221                 assertTrue(state instanceof PercentType);
222                 assertEquals(0, ((PercentType) state).doubleValue(), 0.1);
223                 item.setOnOff(OnOffType.ON);
224                 state = item.getBrightnessState();
225                 assertTrue(state instanceof PercentType);
226                 assertEquals(93.0, ((PercentType) state).doubleValue(), 0.1);
227                 assertEquals(UnDefType.UNDEF, item.getColorTemperaturePercentState());
228                 state = item.getColorState();
229                 assertTrue(state instanceof HSBType);
230                 double[] xy = ColorUtil.hsbToXY((HSBType) state);
231                 assertEquals(0.6367, xy[0], 0.01); // note: rounding errors !!
232                 assertEquals(0.3503, xy[1], 0.01); // note: rounding errors !!
233                 assertEquals(item.getBrightnessState(), ((HSBType) state).getBrightness());
234                 Alerts alert = item.getAlerts();
235                 assertNotNull(alert);
236                 for (ActionType actionValue : alert.getActionValues()) {
237                     assertEquals(ActionType.BREATHE, actionValue);
238                 }
239             }
240             if (name.contains("Table Lamp A")) {
241                 itemFoundCount++;
242                 assertEquals(ResourceType.LIGHT, item.getType());
243                 assertEquals(OnOffType.OFF, item.getOnOffState());
244                 state = item.getBrightnessState();
245                 assertTrue(state instanceof PercentType);
246                 assertEquals(0, ((PercentType) state).doubleValue(), 0.1);
247                 item.setOnOff(OnOffType.ON);
248                 state = item.getBrightnessState();
249                 assertTrue(state instanceof PercentType);
250                 assertEquals(56.7, ((PercentType) state).doubleValue(), 0.1);
251                 MirekSchema mirekSchema = item.getMirekSchema();
252                 assertNotNull(mirekSchema);
253                 assertEquals(153, mirekSchema.getMirekMinimum());
254                 assertEquals(454, mirekSchema.getMirekMaximum());
255
256                 // test color temperature percent value on light's own scale
257                 state = item.getColorTemperaturePercentState();
258                 assertTrue(state instanceof PercentType);
259                 assertEquals(96.3, ((PercentType) state).doubleValue(), 0.1);
260                 state = item.getColorTemperatureAbsoluteState();
261                 assertTrue(state instanceof QuantityType<?>);
262                 assertEquals(2257.3, ((QuantityType<?>) state).doubleValue(), 0.1);
263
264                 // test color temperature percent value on the default (full) scale
265                 MirekSchema temp = item.getMirekSchema();
266                 item.setMirekSchema(MirekSchema.DEFAULT_SCHEMA);
267                 state = item.getColorTemperaturePercentState();
268                 assertTrue(state instanceof PercentType);
269                 assertEquals(83.6, ((PercentType) state).doubleValue(), 0.1);
270                 state = item.getColorTemperatureAbsoluteState();
271                 assertTrue(state instanceof QuantityType<?>);
272                 assertEquals(2257.3, ((QuantityType<?>) state).doubleValue(), 0.1);
273                 item.setMirekSchema(temp);
274
275                 // change colour temperature percent to zero
276                 Setters.setColorTemperaturePercent(item, PercentType.ZERO, null);
277                 assertEquals(PercentType.ZERO, item.getColorTemperaturePercentState());
278                 state = item.getColorTemperatureAbsoluteState();
279                 assertTrue(state instanceof QuantityType<?>);
280                 assertEquals(6535.9, ((QuantityType<?>) state).doubleValue(), 0.1);
281
282                 // change colour temperature percent to 100
283                 Setters.setColorTemperaturePercent(item, PercentType.HUNDRED, null);
284                 assertEquals(PercentType.HUNDRED, item.getColorTemperaturePercentState());
285                 state = item.getColorTemperatureAbsoluteState();
286                 assertTrue(state instanceof QuantityType<?>);
287                 assertEquals(2202.6, ((QuantityType<?>) state).doubleValue(), 0.1);
288
289                 // change colour temperature kelvin to 4000 K
290                 Setters.setColorTemperatureAbsolute(item, QuantityType.valueOf("4000 K"), null);
291                 state = item.getColorTemperaturePercentState();
292                 assertTrue(state instanceof PercentType);
293                 assertEquals(32.2, ((PercentType) state).doubleValue(), 0.1);
294                 assertEquals(QuantityType.valueOf("4000 K"), item.getColorTemperatureAbsoluteState());
295
296                 assertEquals(UnDefType.NULL, item.getColorState());
297                 Alerts alert = item.getAlerts();
298                 assertNotNull(alert);
299                 for (ActionType actionValue : alert.getActionValues()) {
300                     assertEquals(ActionType.BREATHE, actionValue);
301                 }
302             }
303         }
304         assertEquals(2, itemFoundCount);
305     }
306
307     @Test
308     void testLightLevel() {
309         String json = load(ResourceType.LIGHT_LEVEL.name().toLowerCase());
310         Resources resources = GSON.fromJson(json, Resources.class);
311         assertNotNull(resources);
312         List<Resource> list = resources.getResources();
313         assertNotNull(list);
314         assertEquals(1, list.size());
315         Resource item = list.get(0);
316         assertEquals(ResourceType.LIGHT_LEVEL, item.getType());
317         Boolean enabled = item.getEnabled();
318         assertNotNull(enabled);
319         assertTrue(enabled);
320         LightLevel lightLevel = item.getLightLevel();
321         assertNotNull(lightLevel);
322         assertEquals(12725, lightLevel.getLightLevel());
323         assertTrue(lightLevel.isLightLevelValid());
324     }
325
326     @Test
327     void testRelativeRotary() {
328         String json = load(ResourceType.RELATIVE_ROTARY.name().toLowerCase());
329         Resources resources = GSON.fromJson(json, Resources.class);
330         assertNotNull(resources);
331         List<Resource> list = resources.getResources();
332         assertNotNull(list);
333         assertEquals(1, list.size());
334         Resource item = list.get(0);
335         assertEquals(ResourceType.RELATIVE_ROTARY, item.getType());
336         RelativeRotary relativeRotary = item.getRelativeRotary();
337         assertNotNull(relativeRotary);
338         RotationEvent rotationEvent = relativeRotary.getLastEvent();
339         assertNotNull(rotationEvent);
340         assertEquals(RotationEventType.REPEAT, rotationEvent.getAction());
341         Rotation rotation = rotationEvent.getRotation();
342         assertNotNull(rotation);
343         assertEquals(DirectionType.CLOCK_WISE, rotation.getDirection());
344         assertEquals(400, rotation.getDuration());
345         assertEquals(30, rotation.getSteps());
346         assertEquals(new DecimalType(30), relativeRotary.getStepsState());
347         assertEquals(new StringType(ButtonEventType.REPEAT.name()), relativeRotary.getActionState());
348     }
349
350     @Test
351     void testResourceMerging() {
352         // create resource one
353         Resource one = new Resource(ResourceType.LIGHT).setId("AARDVARK");
354         assertNotNull(one);
355         // preset the minimum dimming level
356         try {
357             Dimming dimming = new Dimming().setMinimumDimmingLevel(MINIMUM_DIMMING_LEVEL);
358             Field dimming2 = one.getClass().getDeclaredField("dimming");
359             dimming2.setAccessible(true);
360             dimming2.set(one, dimming);
361         } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
362             fail();
363         }
364         Setters.setColorXy(one, HSBType.RED, null);
365         Setters.setDimming(one, PercentType.HUNDRED, null);
366         assertTrue(one.getColorState() instanceof HSBType);
367         assertEquals(PercentType.HUNDRED, one.getBrightnessState());
368         assertTrue(HSBType.RED.closeTo((HSBType) one.getColorState(), 0.01));
369
370         // switching off should change HSB and Brightness
371         one.setOnOff(OnOffType.OFF);
372         assertEquals(0, ((HSBType) one.getColorState()).getBrightness().doubleValue(), 0.01);
373         assertEquals(PercentType.ZERO, one.getBrightnessState());
374         one.setOnOff(OnOffType.ON);
375
376         // setting brightness to zero should change it to the minimum dimming level
377         Setters.setDimming(one, PercentType.ZERO, null);
378         assertEquals(MINIMUM_DIMMING_LEVEL, ((HSBType) one.getColorState()).getBrightness().doubleValue(), 0.01);
379         assertEquals(MINIMUM_DIMMING_LEVEL, ((PercentType) one.getBrightnessState()).doubleValue(), 0.01);
380         one.setOnOff(OnOffType.ON);
381
382         // null its Dimming field
383         try {
384             Field dimming = one.getClass().getDeclaredField("dimming");
385             dimming.setAccessible(true);
386             dimming.set(one, null);
387         } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
388             fail();
389         }
390
391         // confirm that brightness is no longer valid, and therefore that color has also changed
392         assertEquals(UnDefType.NULL, one.getBrightnessState());
393         assertTrue(one.getColorState() instanceof HSBType);
394         assertTrue((new HSBType(DecimalType.ZERO, PercentType.HUNDRED, new PercentType(50)))
395                 .closeTo((HSBType) one.getColorState(), 0.01));
396
397         PercentType testBrightness = new PercentType(42);
398
399         // create resource two
400         Resource two = new Resource(ResourceType.DEVICE).setId("ALLIGATOR");
401         assertNotNull(two);
402         Setters.setDimming(two, testBrightness, null);
403         assertEquals(UnDefType.NULL, two.getColorState());
404         assertEquals(testBrightness, two.getBrightnessState());
405
406         // merge two => one
407         Setters.setResource(one, two);
408
409         // confirm that brightness and color are both once more valid
410         assertEquals("AARDVARK", one.getId());
411         assertEquals(ResourceType.LIGHT, one.getType());
412         assertEquals(testBrightness, one.getBrightnessState());
413         assertTrue(one.getColorState() instanceof HSBType);
414         assertTrue((new HSBType(DecimalType.ZERO, PercentType.HUNDRED, testBrightness))
415                 .closeTo((HSBType) one.getColorState(), 0.01));
416     }
417
418     @Test
419     void testRoomGroup() {
420         String json = load(ResourceType.ROOM.name().toLowerCase());
421         Resources resources = GSON.fromJson(json, Resources.class);
422         assertNotNull(resources);
423         List<Resource> list = resources.getResources();
424         assertNotNull(list);
425         assertEquals(6, list.size());
426         Resource item = list.get(0);
427         assertEquals(ResourceType.ROOM, item.getType());
428         List<ResourceReference> children = item.getChildren();
429         assertEquals(2, children.size());
430         ResourceReference child = children.get(0);
431         assertNotNull(child);
432         assertEquals("0d47bd3d-d82b-4a21-893c-299bff18e22a", child.getId());
433         assertEquals(ResourceType.DEVICE, child.getType());
434         List<ResourceReference> services = item.getServiceReferences();
435         assertEquals(1, services.size());
436         ResourceReference service = services.get(0);
437         assertNotNull(service);
438         assertEquals("08947162-67be-4ed5-bfce-f42dade42416", service.getId());
439         assertEquals(ResourceType.GROUPED_LIGHT, service.getType());
440     }
441
442     @Test
443     void testScene() {
444         String json = load(ResourceType.SCENE.name().toLowerCase());
445         Resources resources = GSON.fromJson(json, Resources.class);
446         assertNotNull(resources);
447         List<Resource> list = resources.getResources();
448         assertNotNull(list);
449         assertEquals(123, list.size());
450         Resource item = list.get(0);
451         List<ActionEntry> actions = item.getActions();
452         assertNotNull(actions);
453         assertEquals(3, actions.size());
454         ActionEntry actionEntry = actions.get(0);
455         assertNotNull(actionEntry);
456         Resource action = actionEntry.getAction();
457         assertNotNull(action);
458         assertEquals(OnOffType.ON, action.getOnOffState());
459     }
460
461     @Test
462     void testSmartScene() {
463         String json = load(ResourceType.SMART_SCENE.name().toLowerCase());
464         Resources resources = GSON.fromJson(json, Resources.class);
465         assertNotNull(resources);
466         List<Resource> list = resources.getResources();
467         assertNotNull(list);
468         assertEquals(1, list.size());
469         Resource item = list.get(0);
470         ResourceReference group = item.getGroup();
471         assertNotNull(group);
472         String groupId = group.getId();
473         assertNotNull(groupId);
474         assertFalse(groupId.isBlank());
475         ResourceType type = group.getType();
476         assertNotNull(type);
477         assertEquals(ResourceType.ROOM, type);
478         Optional<Boolean> state = item.getSmartSceneActive();
479         assertTrue(state.isPresent());
480         assertFalse(state.get());
481     }
482
483     @Test
484     void testSensor2Motion() {
485         String json = load(ResourceType.MOTION.name().toLowerCase());
486         Resources resources = GSON.fromJson(json, Resources.class);
487         assertNotNull(resources);
488         List<Resource> list = resources.getResources();
489         assertNotNull(list);
490         assertEquals(1, list.size());
491         Resource item = list.get(0);
492         assertEquals(ResourceType.MOTION, item.getType());
493         Boolean enabled = item.getEnabled();
494         assertNotNull(enabled);
495         assertTrue(enabled);
496         Motion motion = item.getMotion();
497         assertNotNull(motion);
498         assertTrue(motion.isMotion());
499         assertTrue(motion.isMotionValid());
500     }
501
502     @Test
503     void testSetGetPureColors() {
504         Resource resource = new Resource(ResourceType.LIGHT);
505         assertNotNull(resource);
506
507         HSBType cyan = new HSBType("180,100,100");
508         HSBType yellow = new HSBType("60,100,100");
509         HSBType magenta = new HSBType("300,100,100");
510
511         for (HSBType color : Set.of(HSBType.WHITE, HSBType.RED, HSBType.GREEN, HSBType.BLUE, cyan, yellow, magenta)) {
512             Setters.setColorXy(resource, color, null);
513             State state = resource.getColorState();
514             assertTrue(state instanceof HSBType);
515             assertTrue(color.closeTo((HSBType) state, 0.01));
516         }
517     }
518
519     @Test
520     void testSseLightOrGroupEvent() {
521         String json = load("event");
522         List<Event> eventList = GSON.fromJson(json, Event.EVENT_LIST_TYPE);
523         assertNotNull(eventList);
524         assertEquals(3, eventList.size());
525         Event event = eventList.get(0);
526         List<Resource> resources = event.getData();
527         assertEquals(9, resources.size());
528         for (Resource r : resources) {
529             ResourceType type = r.getType();
530             assertTrue(ResourceType.LIGHT == type || ResourceType.GROUPED_LIGHT == type);
531         }
532     }
533
534     @Test
535     void testSseSceneEvent() {
536         String json = load("event");
537         List<Event> eventList = GSON.fromJson(json, Event.EVENT_LIST_TYPE);
538         assertNotNull(eventList);
539         assertEquals(3, eventList.size());
540         Event event = eventList.get(2);
541         List<Resource> resources = event.getData();
542         assertEquals(6, resources.size());
543         Resource resource = resources.get(1);
544         assertEquals(ResourceType.SCENE, resource.getType());
545         JsonObject status = resource.getStatus();
546         assertNotNull(status);
547         JsonElement active = status.get("active");
548         assertNotNull(active);
549         assertTrue(active.isJsonPrimitive());
550         assertEquals("inactive", active.getAsString());
551         Optional<Boolean> isActive = resource.getSceneActive();
552         assertTrue(isActive.isPresent());
553         assertEquals(Boolean.FALSE, isActive.get());
554     }
555
556     @Test
557     void testTemperature() {
558         String json = load(ResourceType.TEMPERATURE.name().toLowerCase());
559         Resources resources = GSON.fromJson(json, Resources.class);
560         assertNotNull(resources);
561         List<Resource> list = resources.getResources();
562         assertNotNull(list);
563         assertEquals(1, list.size());
564         Resource item = list.get(0);
565         assertEquals(ResourceType.TEMPERATURE, item.getType());
566         Temperature temperature = item.getTemperature();
567         assertNotNull(temperature);
568         assertEquals(17.2, temperature.getTemperature(), 0.1);
569         assertTrue(temperature.isTemperatureValid());
570     }
571
572     @Test
573     void testValidJson() {
574         for (ResourceType res : ResourceType.values()) {
575             if (!ResourceType.SSE_TYPES.contains(res)) {
576                 try {
577                     String file = res.name().toLowerCase();
578                     String json = load(file);
579                     JsonElement jsonElement = JsonParser.parseString(json);
580                     assertTrue(jsonElement.isJsonObject());
581                 } catch (JsonSyntaxException e) {
582                     fail(res.name());
583                 }
584             }
585         }
586     }
587
588     @Test
589     void testZigbeeStatus() {
590         String json = load(ResourceType.ZIGBEE_CONNECTIVITY.name().toLowerCase());
591         Resources resources = GSON.fromJson(json, Resources.class);
592         assertNotNull(resources);
593         List<Resource> list = resources.getResources();
594         assertNotNull(list);
595         assertEquals(35, list.size());
596         Resource item = list.get(0);
597         assertEquals(ResourceType.ZIGBEE_CONNECTIVITY, item.getType());
598         ZigbeeStatus zigbeeStatus = item.getZigbeeStatus();
599         assertNotNull(zigbeeStatus);
600         assertEquals("Connected", zigbeeStatus.toString());
601     }
602
603     @Test
604     void testZoneGroup() {
605         String json = load(ResourceType.ZONE.name().toLowerCase());
606         Resources resources = GSON.fromJson(json, Resources.class);
607         assertNotNull(resources);
608         List<Resource> list = resources.getResources();
609         assertNotNull(list);
610         assertEquals(7, list.size());
611         Resource item = list.get(0);
612         assertEquals(ResourceType.ZONE, item.getType());
613         List<ResourceReference> children = item.getChildren();
614         assertEquals(1, children.size());
615         ResourceReference child = children.get(0);
616         assertNotNull(child);
617         assertEquals("bcad47a0-3f1f-498c-a8aa-3cf389965219", child.getId());
618         assertEquals(ResourceType.LIGHT, child.getType());
619         List<ResourceReference> services = item.getServiceReferences();
620         assertEquals(1, services.size());
621         ResourceReference service = services.get(0);
622         assertNotNull(service);
623         assertEquals("db4fd630-3798-40de-b642-c1ef464bf770", service.getId());
624         assertEquals(ResourceType.GROUPED_LIGHT, service.getType());
625     }
626
627     @Test
628     void testFixedEffectSetter() {
629         Resource source;
630         Resource target;
631         Effects resultEffect;
632
633         // no source effects
634         source = new Resource(ResourceType.LIGHT);
635         target = new Resource(ResourceType.LIGHT);
636         Setters.setResource(target, source);
637         assertNull(target.getFixedEffects());
638
639         // valid source fixed effects
640         source = new Resource(ResourceType.LIGHT).setFixedEffects(
641                 new Effects().setStatusValues(List.of("NO_EFFECT", "SPARKLE", "CANDLE")).setEffect(EffectType.SPARKLE));
642         target = new Resource(ResourceType.LIGHT);
643         Setters.setResource(target, source);
644         resultEffect = target.getFixedEffects();
645         assertNotNull(resultEffect);
646         assertEquals(EffectType.SPARKLE, resultEffect.getEffect());
647         assertEquals(3, resultEffect.getStatusValues().size());
648
649         // valid but different source and target fixed effects
650         source = new Resource(ResourceType.LIGHT).setFixedEffects(
651                 new Effects().setStatusValues(List.of("NO_EFFECT", "SPARKLE", "CANDLE")).setEffect(EffectType.SPARKLE));
652         target = new Resource(ResourceType.LIGHT).setFixedEffects(
653                 new Effects().setStatusValues(List.of("NO_EFFECT", "FIRE")).setEffect(EffectType.FIRE));
654         Setters.setResource(target, source);
655         resultEffect = target.getFixedEffects();
656         assertNotNull(resultEffect);
657         assertNotEquals(EffectType.SPARKLE, resultEffect.getEffect());
658         assertEquals(3, resultEffect.getStatusValues().size());
659
660         // partly valid source fixed effects
661         source = new Resource(ResourceType.LIGHT).setFixedEffects(new Effects().setStatusValues(List.of("SPARKLE"))
662                 .setEffect(EffectType.SPARKLE).setStatusValues(List.of()));
663         target = new Resource(ResourceType.LIGHT);
664         Setters.setResource(target, source);
665         resultEffect = target.getFixedEffects();
666         assertNotNull(resultEffect);
667         assertEquals(EffectType.SPARKLE, resultEffect.getEffect());
668         assertEquals(0, resultEffect.getStatusValues().size());
669         assertFalse(resultEffect.allows(EffectType.SPARKLE));
670         assertFalse(resultEffect.allows(EffectType.NO_EFFECT));
671     }
672
673     @Test
674     void testTimedEffectSetter() {
675         Resource source;
676         Resource target;
677         Effects resultEffect;
678
679         // no source effects
680         source = new Resource(ResourceType.LIGHT);
681         target = new Resource(ResourceType.LIGHT);
682         Setters.setResource(target, source);
683         assertNull(target.getTimedEffects());
684
685         // valid source timed effects
686         source = new Resource(ResourceType.LIGHT).setTimedEffects((TimedEffects) new TimedEffects()
687                 .setStatusValues(List.of("NO_EFFECT", "SUNRISE")).setEffect(EffectType.NO_EFFECT));
688         target = new Resource(ResourceType.LIGHT);
689         Setters.setResource(target, source);
690         resultEffect = target.getTimedEffects();
691         assertNotNull(resultEffect);
692         assertEquals(EffectType.NO_EFFECT, resultEffect.getEffect());
693         assertEquals(2, resultEffect.getStatusValues().size());
694
695         // valid but different source and target timed effects
696         source = new Resource(ResourceType.LIGHT)
697                 .setTimedEffects((TimedEffects) new TimedEffects().setDuration(Duration.ofMinutes(11))
698                         .setStatusValues(List.of("NO_EFFECT", "SPARKLE", "CANDLE")).setEffect(EffectType.SPARKLE));
699         target = new Resource(ResourceType.LIGHT).setTimedEffects((TimedEffects) new TimedEffects()
700                 .setStatusValues(List.of("NO_EFFECT", "FIRE")).setEffect(EffectType.FIRE));
701         Setters.setResource(target, source);
702         resultEffect = target.getTimedEffects();
703         assertNotNull(resultEffect);
704         assertNotEquals(EffectType.SPARKLE, resultEffect.getEffect());
705         assertEquals(3, resultEffect.getStatusValues().size());
706         assertTrue(resultEffect instanceof TimedEffects);
707         assertEquals(Duration.ofMinutes(11), ((TimedEffects) resultEffect).getDuration());
708
709         // partly valid source timed effects
710         source = new Resource(ResourceType.LIGHT).setTimedEffects((TimedEffects) new TimedEffects()
711                 .setStatusValues(List.of("SUNRISE")).setEffect(EffectType.SUNRISE).setStatusValues(List.of()));
712         target = new Resource(ResourceType.LIGHT);
713         Setters.setResource(target, source);
714         resultEffect = target.getTimedEffects();
715         assertNotNull(resultEffect);
716         assertEquals(EffectType.SUNRISE, resultEffect.getEffect());
717         assertEquals(0, resultEffect.getStatusValues().size());
718         assertFalse(resultEffect.allows(EffectType.SPARKLE));
719         assertFalse(resultEffect.allows(EffectType.NO_EFFECT));
720         assertTrue(resultEffect instanceof TimedEffects);
721         assertNull(((TimedEffects) resultEffect).getDuration());
722
723         target.setTimedEffectsDuration(Duration.ofSeconds(22));
724         assertEquals(Duration.ofSeconds(22), ((TimedEffects) resultEffect).getDuration());
725
726         // source timed effect with duration
727         source = new Resource(ResourceType.LIGHT)
728                 .setTimedEffects((TimedEffects) new TimedEffects().setDuration(Duration.ofMillis(44))
729                         .setStatusValues(List.of("SUNRISE")).setEffect(EffectType.SUNRISE).setStatusValues(List.of()));
730         target = new Resource(ResourceType.LIGHT);
731         Setters.setResource(target, source);
732         resultEffect = target.getTimedEffects();
733         assertNotNull(resultEffect);
734         assertTrue(resultEffect instanceof TimedEffects);
735         assertEquals(Duration.ofMillis(44), ((TimedEffects) resultEffect).getDuration());
736     }
737 }