]> git.basschouten.com Git - openhab-addons.git/blob
8bd12d92b3a2de641ee61afa411c2c0814647307
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.hdpowerview;
14
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.openhab.binding.hdpowerview.internal.api.ActuatorClass.*;
17 import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
18
19 import java.io.IOException;
20 import java.nio.file.Files;
21 import java.nio.file.Paths;
22 import java.util.List;
23 import java.util.regex.Pattern;
24 import java.util.stream.Collectors;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.jetty.client.HttpClient;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
31 import org.openhab.binding.hdpowerview.internal.HubMaintenanceException;
32 import org.openhab.binding.hdpowerview.internal.HubProcessingException;
33 import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem;
34 import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
35 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
36 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
37 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
38 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
39 import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
40 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
41 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
42 import org.openhab.core.library.types.PercentType;
43 import org.openhab.core.types.State;
44 import org.openhab.core.types.UnDefType;
45
46 import com.google.gson.Gson;
47 import com.google.gson.JsonParseException;
48
49 /**
50  * Unit tests for HD PowerView binding
51  *
52  * @author Andrew Fiddian-Green - Initial contribution
53  * @author Jacob Laursen - Add support for scene groups
54  */
55 @NonNullByDefault
56 public class HDPowerViewJUnitTests {
57
58     private static final Pattern VALID_IP_V4_ADDRESS = Pattern
59             .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
60
61     /*
62      * load a test JSON string from a file
63      */
64     private String loadJson(String fileName) {
65         try {
66             return Files.readAllLines(Paths.get(String.format("src/test/resources/%s.json", fileName))).stream()
67                     .collect(Collectors.joining());
68         } catch (IOException e) {
69             fail(e.getMessage());
70         }
71         return "";
72     }
73
74     /**
75      * Run a series of ONLINE tests on the communication with a hub
76      *
77      * @param hubIPAddress must be a valid hub IP address to run the
78      *            tests on; or an INVALID IP address to
79      *            suppress the tests
80      * @param allowShadeMovementCommands set to true if you accept that the tests
81      *            shall physically move the shades
82      */
83     @Test
84     public void testOnlineCommunication() {
85         /*
86          * NOTE: in order to actually run these tests you must have a hub physically
87          * available, and its IP address must be correctly configured in the
88          * "hubIPAddress" string constant e.g. "192.168.1.123"
89          */
90         String hubIPAddress = "192.168.1.xxx";
91
92         /*
93          * NOTE: set allowShadeMovementCommands = true if you accept physically moving
94          * the shades during these tests
95          */
96         boolean allowShadeMovementCommands = false;
97
98         if (VALID_IP_V4_ADDRESS.matcher(hubIPAddress).matches()) {
99             // ==== initialize stuff ====
100             HttpClient client = new HttpClient();
101             assertNotNull(client);
102
103             // ==== start the client ====
104             try {
105                 client.start();
106                 assertTrue(client.isStarted());
107             } catch (Exception e) {
108                 fail(e.getMessage());
109             }
110
111             HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress);
112             assertNotNull(webTargets);
113
114             // ==== exercise some code ====
115             ShadePosition test;
116             State pos;
117
118             // shade fully up
119             test = ShadePosition.create(ZERO_IS_CLOSED, 0);
120             assertNotNull(test);
121             pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
122             assertEquals(PercentType.class, pos.getClass());
123             assertEquals(0, ((PercentType) pos).intValue());
124             pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
125             assertTrue(UnDefType.UNDEF.equals(pos));
126
127             // shade fully down (method 1)
128             test = ShadePosition.create(ZERO_IS_CLOSED, 100);
129             assertNotNull(test);
130             pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
131             assertEquals(PercentType.class, pos.getClass());
132             assertEquals(100, ((PercentType) pos).intValue());
133             pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
134             assertEquals(PercentType.class, pos.getClass());
135             assertEquals(0, ((PercentType) pos).intValue());
136
137             // shade fully down (method 2)
138             test = ShadePosition.create(VANE_COORDS, 0);
139             assertNotNull(test);
140             pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
141             assertEquals(PercentType.class, pos.getClass());
142             assertEquals(100, ((PercentType) pos).intValue());
143             pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
144             assertEquals(PercentType.class, pos.getClass());
145             assertEquals(0, ((PercentType) pos).intValue());
146
147             // shade fully down (method 2) and vane fully open
148             test = ShadePosition.create(VANE_COORDS, 100);
149             assertNotNull(test);
150             pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
151             assertEquals(PercentType.class, pos.getClass());
152             assertEquals(100, ((PercentType) pos).intValue());
153             pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
154             assertEquals(PercentType.class, pos.getClass());
155             assertEquals(100, ((PercentType) pos).intValue());
156
157             int shadeId = 0;
158             @Nullable
159             ShadePosition shadePos = null;
160             @Nullable
161             Shades shadesX = null;
162
163             // ==== get all shades ====
164             try {
165                 shadesX = webTargets.getShades();
166                 assertNotNull(shadesX);
167                 @Nullable
168                 List<ShadeData> shadesData = shadesX.shadeData;
169                 assertNotNull(shadesData);
170                 assertTrue(!shadesData.isEmpty());
171                 @Nullable
172                 ShadeData shadeData;
173                 shadeData = shadesData.get(0);
174                 assertNotNull(shadeData);
175                 assertTrue(shadeData.getName().length() > 0);
176                 shadePos = shadeData.positions;
177                 assertNotNull(shadePos);
178                 @Nullable
179                 ShadeData shadeZero = shadesData.get(0);
180                 assertNotNull(shadeZero);
181                 shadeId = shadeZero.id;
182                 assertNotEquals(0, shadeId);
183
184                 for (ShadeData shadexData : shadesData) {
185                     String shadeName = shadexData.getName();
186                     assertNotNull(shadeName);
187                 }
188             } catch (JsonParseException | HubProcessingException | HubMaintenanceException e) {
189                 fail(e.getMessage());
190             }
191
192             // ==== get all scenes ====
193             int sceneId = 0;
194             try {
195                 Scenes scenes = webTargets.getScenes();
196                 assertNotNull(scenes);
197                 @Nullable
198                 List<Scene> scenesData = scenes.sceneData;
199                 assertNotNull(scenesData);
200                 assertTrue(!scenesData.isEmpty());
201                 @Nullable
202                 Scene sceneZero = scenesData.get(0);
203                 assertNotNull(sceneZero);
204                 sceneId = sceneZero.id;
205                 assertTrue(sceneId > 0);
206
207                 for (Scene scene : scenesData) {
208                     String sceneName = scene.getName();
209                     assertNotNull(sceneName);
210                 }
211             } catch (JsonParseException | HubProcessingException | HubMaintenanceException e) {
212                 fail(e.getMessage());
213             }
214
215             // ==== refresh a specific shade ====
216             @Nullable
217             Shade shade = null;
218             try {
219                 assertNotEquals(0, shadeId);
220                 shade = webTargets.refreshShadePosition(shadeId);
221                 assertNotNull(shade);
222             } catch (HubProcessingException | HubMaintenanceException e) {
223                 fail(e.getMessage());
224             }
225
226             // ==== move a specific shade ====
227             try {
228                 assertNotEquals(0, shadeId);
229                 assertNotNull(shade);
230                 @Nullable
231                 ShadeData shadeData = shade.shade;
232                 assertNotNull(shadeData);
233                 ShadePosition positions = shadeData.positions;
234                 assertNotNull(positions);
235                 CoordinateSystem coordSys = positions.getCoordinateSystem(PRIMARY_ACTUATOR);
236                 assertNotNull(coordSys);
237
238                 pos = positions.getState(PRIMARY_ACTUATOR, coordSys);
239                 assertEquals(PercentType.class, pos.getClass());
240
241                 pos = positions.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
242                 assertEquals(PercentType.class, pos.getClass());
243
244                 int position = ((PercentType) pos).intValue();
245                 position = position + ((position <= 10) ? 5 : -5);
246
247                 ShadePosition newPos = ShadePosition.create(ZERO_IS_CLOSED, position);
248                 assertNotNull(newPos);
249
250                 if (allowShadeMovementCommands) {
251                     webTargets.moveShade(shadeId, newPos);
252                 }
253             } catch (HubProcessingException | HubMaintenanceException e) {
254                 fail(e.getMessage());
255             }
256
257             // ==== activate a specific scene ====
258             if (allowShadeMovementCommands) {
259                 try {
260                     assertNotNull(sceneId);
261                     webTargets.activateScene(sceneId);
262                 } catch (HubProcessingException | HubMaintenanceException e) {
263                     fail(e.getMessage());
264                 }
265             }
266
267             // ==== test stop command ====
268             if (allowShadeMovementCommands) {
269                 try {
270                     assertNotNull(sceneId);
271                     webTargets.stopShade(shadeId);
272                 } catch (HubProcessingException | HubMaintenanceException e) {
273                     fail(e.getMessage());
274                 }
275             }
276
277             // ==== stop the client ====
278             if (client.isRunning()) {
279                 try {
280                     client.stop();
281                 } catch (Exception e) {
282                     fail(e.getMessage());
283                 }
284             }
285         }
286     }
287
288     /**
289      * Test generic JSON shades response
290      */
291     @Test
292     public void shadeResponseIsParsedCorrectly() throws JsonParseException {
293         final Gson gson = new Gson();
294         @Nullable
295         Shades shades;
296         String json = loadJson("shades");
297         assertNotEquals("", json);
298         shades = gson.fromJson(json, Shades.class);
299         assertNotNull(shades);
300     }
301
302     /**
303      * Test generic JSON scene response
304      */
305     @Test
306     public void sceneResponseIsParsedCorrectly() throws JsonParseException {
307         final Gson gson = new Gson();
308         String json = loadJson("scenes");
309         assertNotEquals("", json);
310
311         @Nullable
312         Scenes scenes = gson.fromJson(json, Scenes.class);
313         assertNotNull(scenes);
314
315         @Nullable
316         List<Scene> sceneData = scenes.sceneData;
317         assertNotNull(sceneData);
318
319         assertEquals(4, sceneData.size());
320         @Nullable
321         Scene scene = sceneData.get(0);
322         assertEquals("Door Open", scene.getName());
323         assertEquals(18097, scene.id);
324     }
325
326     /**
327      * Test generic JSON scene collection response
328      */
329     @Test
330     public void sceneCollectionResponseIsParsedCorrectly() throws JsonParseException {
331         final Gson gson = new Gson();
332         String json = loadJson("sceneCollections");
333         assertNotEquals("", json);
334
335         @Nullable
336         SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
337         assertNotNull(sceneCollections);
338         @Nullable
339         List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
340         assertNotNull(sceneCollectionData);
341
342         assertEquals(1, sceneCollectionData.size());
343         @Nullable
344         SceneCollection sceneCollection = sceneCollectionData.get(0);
345         assertEquals("Børn op", sceneCollection.getName());
346         assertEquals(27119, sceneCollection.id);
347     }
348
349     /**
350      * Test the JSON parsing for a duette top down bottom up shade
351      */
352     @Test
353     public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws JsonParseException {
354         final Gson gson = new Gson();
355         String json = loadJson("duette");
356         assertNotEquals("", json);
357
358         @Nullable
359         Shades shades = gson.fromJson(json, Shades.class);
360         assertNotNull(shades);
361         @Nullable
362         List<ShadeData> shadesData = shades.shadeData;
363         assertNotNull(shadesData);
364
365         assertEquals(1, shadesData.size());
366         @Nullable
367         ShadeData shadeData = shadesData.get(0);
368         assertNotNull(shadeData);
369
370         assertEquals("Gardin 1", shadeData.getName());
371         assertEquals(63778, shadeData.id);
372
373         ShadePosition shadePos = shadeData.positions;
374         assertNotNull(shadePos);
375         assertEquals(ZERO_IS_CLOSED, shadePos.getCoordinateSystem(PRIMARY_ACTUATOR));
376
377         State pos = shadePos.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
378         assertEquals(PercentType.class, pos.getClass());
379         assertEquals(59, ((PercentType) pos).intValue());
380
381         pos = shadePos.getState(SECONDARY_ACTUATOR, ZERO_IS_OPEN);
382         assertEquals(PercentType.class, pos.getClass());
383         assertEquals(35, ((PercentType) pos).intValue());
384
385         pos = shadePos.getState(PRIMARY_ACTUATOR, VANE_COORDS);
386         assertEquals(UnDefType.class, pos.getClass());
387
388         assertEquals(3, shadeData.batteryStatus);
389
390         assertEquals(4, shadeData.signalStrength);
391     }
392 }