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