]> git.basschouten.com Git - openhab-addons.git/blob
17470593b24a157048632b9943b1aa17c0a840a7
[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.io.hueemulation.internal.rest;
14
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertEquals;
18 import static org.mockito.ArgumentMatchers.argThat;
19 import static org.mockito.Mockito.verify;
20
21 import java.io.IOException;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jetty.client.api.ContentResponse;
25 import org.glassfish.jersey.server.ResourceConfig;
26 import org.junit.jupiter.api.AfterEach;
27 import org.junit.jupiter.api.BeforeEach;
28 import org.junit.jupiter.api.Test;
29 import org.openhab.core.events.Event;
30 import org.openhab.core.items.GroupItem;
31 import org.openhab.core.items.ItemRegistry;
32 import org.openhab.core.items.events.ItemCommandEvent;
33 import org.openhab.core.library.items.ColorItem;
34 import org.openhab.core.library.items.SwitchItem;
35 import org.openhab.core.library.types.HSBType;
36 import org.openhab.core.library.types.OnOffType;
37 import org.openhab.io.hueemulation.internal.ConfigStore;
38 import org.openhab.io.hueemulation.internal.DeviceType;
39 import org.openhab.io.hueemulation.internal.dto.HueGroupEntry;
40 import org.openhab.io.hueemulation.internal.dto.HueLightEntry;
41 import org.openhab.io.hueemulation.internal.dto.HueStateColorBulb;
42 import org.openhab.io.hueemulation.internal.dto.HueStatePlug;
43 import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry;
44
45 /**
46  * Tests for {@link LightsAndGroups}.
47  *
48  * @author David Graeff - Initial contribution
49  */
50 @NonNullByDefault
51 public class LightsAndGroupsTests {
52     protected @NonNullByDefault({}) CommonSetup commonSetup;
53     protected @NonNullByDefault({}) ItemRegistry itemRegistry;
54     protected @NonNullByDefault({}) ConfigStore cs;
55
56     LightsAndGroups subject = new LightsAndGroups();
57
58     @BeforeEach
59     public void setUp() throws IOException {
60         commonSetup = new CommonSetup(false);
61         itemRegistry = new DummyItemRegistry();
62
63         this.cs = commonSetup.cs;
64
65         subject.cs = cs;
66         subject.eventPublisher = commonSetup.eventPublisher;
67         subject.userManagement = commonSetup.userManagement;
68         subject.itemRegistry = itemRegistry;
69         subject.activate();
70
71         // Add simulated lights
72         cs.ds.lights.put("1", new HueLightEntry(new SwitchItem("switch"), "switch", DeviceType.SwitchType));
73         cs.ds.lights.put("2", new HueLightEntry(new ColorItem("color"), "color", DeviceType.ColorType));
74         cs.ds.lights.put("3", new HueLightEntry(new ColorItem("white"), "white", DeviceType.WhiteTemperatureType));
75
76         // Add group item
77         cs.ds.groups.put("10",
78                 new HueGroupEntry("name", new GroupItem("white", new SwitchItem("switch")), DeviceType.SwitchType));
79
80         commonSetup.start(new ResourceConfig().registerInstances(subject));
81     }
82
83     @AfterEach
84     public void tearDown() throws Exception {
85         commonSetup.dispose();
86     }
87
88     @Test
89     public void addSwitchableByCategory() {
90         SwitchItem item = new SwitchItem("switch1");
91         item.setCategory("Light");
92         itemRegistry.add(item);
93         HueLightEntry device = cs.ds.lights.get(cs.mapItemUIDtoHueID(item));
94         assertThat(device.item, is(item));
95         assertThat(device.state, is(instanceOf(HueStatePlug.class)));
96     }
97
98     @Test
99     public void addSwitchableByTag() {
100         SwitchItem item = new SwitchItem("switch1");
101         item.addTag("Switchable");
102         itemRegistry.add(item);
103         HueLightEntry device = cs.ds.lights.get(cs.mapItemUIDtoHueID(item));
104         assertThat(device.item, is(item));
105         assertThat(device.state, is(instanceOf(HueStatePlug.class)));
106     }
107
108     @Test
109     public void ignoreByTag() {
110         SwitchItem item = new SwitchItem("switch1");
111         item.addTags("Switchable", "internal"); // The ignore tag will win
112         itemRegistry.add(item);
113         HueLightEntry device = cs.ds.lights.get(cs.mapItemUIDtoHueID(item));
114         assertThat(device, is(nullValue()));
115     }
116
117     @Test
118     public void addGroupSwitchableByTag() {
119         GroupItem item = new GroupItem("group1", new SwitchItem("switch1"));
120         item.addTag("Switchable");
121         itemRegistry.add(item);
122         HueGroupEntry device = cs.ds.groups.get(cs.mapItemUIDtoHueID(item));
123         assertThat(device.groupItem, is(item));
124         assertThat(device.action, is(instanceOf(HueStatePlug.class)));
125     }
126
127     @Test
128     public void addDeviceAsGroupSwitchableByTag() {
129         GroupItem item = new GroupItem("group1", new SwitchItem("switch1"));
130         item.addTag("Switchable");
131         item.addTag("Huelight");
132         itemRegistry.add(item);
133         HueLightEntry device = cs.ds.lights.get(cs.mapItemUIDtoHueID(item));
134         assertThat(device.item, is(item));
135         assertThat(device.state, is(instanceOf(HueStatePlug.class)));
136     }
137
138     @Test
139     public void addGroupWithoutTypeByTag() {
140         GroupItem item = new GroupItem("group1", null);
141         item.addTag("Switchable");
142
143         itemRegistry.add(item);
144
145         HueGroupEntry device = cs.ds.groups.get(cs.mapItemUIDtoHueID(item));
146         assertThat(device.groupItem, is(item));
147         assertThat(device.action, is(instanceOf(HueStatePlug.class)));
148         assertThat(cs.ds.groups.get(cs.mapItemUIDtoHueID(item)).groupItem, is(item));
149     }
150
151     @Test
152     public void removeGroupWithoutTypeAndTag() {
153         String groupName = "group1";
154         GroupItem item = new GroupItem(groupName, null);
155         item.addTag("Switchable");
156         itemRegistry.add(item);
157
158         String hueID = cs.mapItemUIDtoHueID(item);
159         assertThat(cs.ds.groups.get(hueID), notNullValue());
160
161         subject.updated(item, new GroupItem(groupName, null));
162
163         assertThat(cs.ds.groups.get(hueID), nullValue());
164     }
165
166     @Test
167     public void updateSwitchable() {
168         SwitchItem item = new SwitchItem("switch1");
169         item.setLabel("labelOld");
170         item.addTag("Switchable");
171         itemRegistry.add(item);
172         String hueID = cs.mapItemUIDtoHueID(item);
173         HueLightEntry device = cs.ds.lights.get(hueID);
174         assertThat(device.item, is(item));
175         assertThat(device.state, is(instanceOf(HueStatePlug.class)));
176         assertThat(device.name, is("labelOld"));
177
178         SwitchItem newitem = new SwitchItem("switch1");
179         newitem.setLabel("labelNew");
180         newitem.addTag("Switchable");
181         subject.updated(item, newitem);
182         device = cs.ds.lights.get(hueID);
183         assertThat(device.item, is(newitem));
184         assertThat(device.state, is(instanceOf(HueStatePlug.class)));
185         assertThat(device.name, is("labelNew"));
186
187         // Update with an item that has no tags anymore -> should be removed
188         SwitchItem newitemWithoutTag = new SwitchItem("switch1");
189         newitemWithoutTag.setLabel("labelNew2");
190         subject.updated(newitem, newitemWithoutTag);
191
192         device = cs.ds.lights.get(hueID);
193         assertThat(device, nullValue());
194     }
195
196     @Test
197     public void changeSwitchState() throws Exception {
198         assertThat(((HueStatePlug) cs.ds.lights.get("1").state).on, is(false));
199
200         String body = "{'on':true}";
201         ContentResponse response = commonSetup.sendPut("/testuser/lights/1/state", body);
202         assertEquals(200, response.getStatus());
203         assertThat(response.getContentAsString(), containsString("success"));
204         assertThat(((HueStatePlug) cs.ds.lights.get("1").state).on, is(true));
205         verify(commonSetup.eventPublisher).post(argThat((Event t) -> {
206             assertThat(t.getPayload(), is("{\"type\":\"OnOff\",\"value\":\"ON\"}"));
207             return true;
208         }));
209     }
210
211     @Test
212     public void changeGroupItemSwitchState() throws Exception {
213         assertThat(((HueStatePlug) cs.ds.groups.get("10").action).on, is(false));
214
215         String body = "{'on':true}";
216         ContentResponse response = commonSetup.sendPut("/testuser/groups/10/action", body);
217         assertEquals(200, response.getStatus());
218         assertThat(response.getContentAsString(), containsString("success"));
219         assertThat(((HueStatePlug) cs.ds.groups.get("10").action).on, is(true));
220         verify(commonSetup.eventPublisher).post(argThat((Event t) -> {
221             assertThat(t.getPayload(), is("{\"type\":\"OnOff\",\"value\":\"ON\"}"));
222             return true;
223         }));
224     }
225
226     @Test
227     public void changeOnValue() throws Exception {
228         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false));
229
230         String body = "{'on':true}";
231         ContentResponse response = commonSetup.sendPut("/testuser/lights/2/state", body);
232         assertEquals(200, response.getStatus());
233         String entity = response.getContentAsString();
234         assertThat(entity, is("[{\"success\":{\"/lights/2/state/on\":true}}]"));
235         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true));
236     }
237
238     @Test
239     public void changeOnAndBriValues() throws Exception {
240         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false));
241         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(1));
242
243         String body = "{'on':true,'bri':200}";
244         ContentResponse response = commonSetup.sendPut("/testuser/lights/2/state", body);
245         assertEquals(200, response.getStatus());
246         assertThat(response.getContentAsString(), containsString("success"));
247         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true));
248         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(200));
249     }
250
251     @Test
252     public void changeHueSatValues() throws Exception {
253         HueLightEntry hueDevice = cs.ds.lights.get("2");
254         hueDevice.item.setState(OnOffType.ON);
255         hueDevice.state.as(HueStateColorBulb.class).on = true;
256
257         String body = "{'hue':1000,'sat':50}";
258         ContentResponse response = commonSetup.sendPut("/testuser/lights/2/state", body);
259         assertEquals(200, response.getStatus());
260         assertThat(response.getContentAsString(), containsString("success"));
261         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true));
262         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).hue, is(1000));
263         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).sat, is(50));
264
265         verify(commonSetup.eventPublisher).post(argThat(ce -> assertHueValue((ItemCommandEvent) ce, 1000)));
266     }
267
268     /**
269      * Amazon echos are setting ct only, if commanded to turn a light white.
270      */
271     @Test
272     public void changeCtValue() throws Exception {
273         HueLightEntry hueDevice = cs.ds.lights.get("2");
274         hueDevice.item.setState(OnOffType.ON);
275         hueDevice.state.as(HueStateColorBulb.class).on = true;
276
277         String body = "{'ct':500}";
278         ContentResponse response = commonSetup.sendPut("/testuser/lights/2/state", body);
279         assertEquals(200, response.getStatus());
280         body = response.getContentAsString();
281         assertThat(body, containsString("success"));
282         assertThat(body, containsString("ct"));
283         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true));
284         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).ct, is(500));
285         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).sat, is(0));
286
287         // Saturation is expected to be 0 -> white light
288         verify(commonSetup.eventPublisher).post(argThat(ce -> assertSatValue((ItemCommandEvent) ce, 0)));
289     }
290
291     @Test
292     public void switchOnWithXY() throws Exception {
293         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false));
294         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(1));
295
296         String body = "{'on':true,'bri':200,'xy':[0.5119,0.4147]}";
297         ContentResponse response = commonSetup.sendPut("/testuser/lights/2/state", body);
298         assertEquals(200, response.getStatus());
299         assertThat(response.getContentAsString(), containsString("success"));
300         assertThat(response.getContentAsString(), containsString("xy"));
301         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true));
302         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(200));
303         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).xy[0], is(0.5119));
304         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).xy[1], is(0.4147));
305         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).colormode, is(HueStateColorBulb.ColorMode.xy));
306         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).toHSBType().getHue().intValue(),
307                 is((int) 27.47722590981918));
308         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).toHSBType().getSaturation().intValue(), is(88));
309         assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).toHSBType().getBrightness().intValue(), is(78));
310     }
311
312     @Test
313     public void allLightsAndSingleLight() throws Exception {
314         ContentResponse response = commonSetup.sendGet("/testuser/lights");
315         assertEquals(200, response.getStatus());
316
317         String body = response.getContentAsString();
318
319         assertThat(body, containsString("switch"));
320         assertThat(body, containsString("color"));
321         assertThat(body, containsString("white"));
322
323         // Single light access test
324         response = commonSetup.sendGet("/testuser/lights/2");
325         assertEquals(200, response.getStatus());
326         body = response.getContentAsString();
327         assertThat(body, containsString("color"));
328     }
329
330     private boolean assertHueValue(ItemCommandEvent ce, int hueValue) {
331         assertThat(((HSBType) ce.getItemCommand()).getHue().intValue(), is(hueValue * 360 / HueStateColorBulb.MAX_HUE));
332         return true;
333     }
334
335     private boolean assertSatValue(ItemCommandEvent ce, int satValue) {
336         assertThat(((HSBType) ce.getItemCommand()).getSaturation().intValue(),
337                 is(satValue * 100 / HueStateColorBulb.MAX_SAT));
338         return true;
339     }
340 }