2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.hdpowerview;
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.*;
19 import java.io.BufferedReader;
20 import java.io.FileReader;
21 import java.io.IOException;
22 import java.util.List;
23 import java.util.regex.Pattern;
25 import javax.ws.rs.ProcessingException;
26 import javax.ws.rs.client.Client;
27 import javax.ws.rs.client.ClientBuilder;
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.junit.jupiter.api.Test;
32 import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
33 import org.openhab.binding.hdpowerview.internal.HubMaintenanceException;
34 import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem;
35 import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
36 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
37 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
38 import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
39 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
40 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
41 import org.openhab.core.library.types.PercentType;
42 import org.openhab.core.types.State;
43 import org.openhab.core.types.UnDefType;
45 import com.google.gson.Gson;
46 import com.google.gson.JsonParseException;
49 * Unit tests for HD PowerView binding
51 * @author Andrew Fiddian-Green - Initial contribution
54 public class HDPowerViewJUnitTests {
56 private static final Pattern VALID_IP_V4_ADDRESS = Pattern
57 .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
60 * load a test JSON string from a file
62 private String loadJson(String fileName) {
63 try (FileReader file = new FileReader(String.format("src/test/resources/%s.json", fileName));
64 BufferedReader reader = new BufferedReader(file)) {
65 StringBuilder builder = new StringBuilder();
67 while ((line = reader.readLine()) != null) {
68 builder.append(line).append("\n");
70 return builder.toString();
71 } catch (IOException e) {
78 * Run a series of ONLINE tests on the communication with a hub
80 * @param hubIPAddress must be a valid hub IP address to run the
81 * tests on; or an INVALID IP address to
83 * @param allowShadeMovementCommands set to true if you accept that the tests
84 * shall physically move the shades
87 public void testOnlineCommunication() {
89 * NOTE: in order to actually run these tests you must have a hub physically
90 * available, and its IP address must be correctly configured in the
91 * "hubIPAddress" string constant e.g. "192.168.1.123"
93 String hubIPAddress = "192.168.1.xxx";
96 * NOTE: set allowShadeMovementCommands = true if you accept physically moving
97 * the shades during these tests
99 boolean allowShadeMovementCommands = false;
101 if (VALID_IP_V4_ADDRESS.matcher(hubIPAddress).matches()) {
103 Client client = ClientBuilder.newClient();
104 assertNotNull(client);
105 // client.register(new Logger());
106 HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress);
107 assertNotNull(webTargets);
109 // ==== exercise some code ====
114 test = ShadePosition.create(ZERO_IS_CLOSED, 0);
116 pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
117 assertEquals(PercentType.class, pos.getClass());
118 assertEquals(0, ((PercentType) pos).intValue());
119 pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
120 assertTrue(UnDefType.UNDEF.equals(pos));
122 // shade fully down (method 1)
123 test = ShadePosition.create(ZERO_IS_CLOSED, 100);
125 pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
126 assertEquals(PercentType.class, pos.getClass());
127 assertEquals(100, ((PercentType) pos).intValue());
128 pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
129 assertEquals(PercentType.class, pos.getClass());
130 assertEquals(0, ((PercentType) pos).intValue());
132 // shade fully down (method 2)
133 test = ShadePosition.create(VANE_COORDS, 0);
135 pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
136 assertEquals(PercentType.class, pos.getClass());
137 assertEquals(100, ((PercentType) pos).intValue());
138 pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
139 assertEquals(PercentType.class, pos.getClass());
140 assertEquals(0, ((PercentType) pos).intValue());
142 // shade fully down (method 2) and vane fully open
143 test = ShadePosition.create(VANE_COORDS, 100);
145 pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
146 assertEquals(PercentType.class, pos.getClass());
147 assertEquals(100, ((PercentType) pos).intValue());
148 pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
149 assertEquals(PercentType.class, pos.getClass());
150 assertEquals(100, ((PercentType) pos).intValue());
154 ShadePosition shadePos = null;
156 Shades shadesX = null;
158 // ==== get all shades ====
160 shadesX = webTargets.getShades();
161 assertNotNull(shadesX);
163 List<ShadeData> shadesData = shadesX.shadeData;
164 assertNotNull(shadesData);
165 assertTrue(shadesData.size() > 0);
168 shadeData = shadesData.get(0);
169 assertNotNull(shadeData);
170 assertTrue(shadeData.getName().length() > 0);
171 shadePos = shadeData.positions;
172 assertNotNull(shadePos);
174 ShadeData shadeZero = shadesData.get(0);
175 assertNotNull(shadeZero);
176 shadeId = shadeZero.id;
177 assertNotEquals(0, shadeId);
179 for (ShadeData shadexData : shadesData) {
180 String shadeName = shadexData.getName();
181 assertNotNull(shadeName);
183 } catch (JsonParseException | ProcessingException | HubMaintenanceException e) {
184 fail(e.getMessage());
187 // ==== get all scenes ====
190 Scenes scenes = webTargets.getScenes();
191 assertNotNull(scenes);
193 List<Scene> scenesData = scenes.sceneData;
194 assertNotNull(scenesData);
195 assertTrue(scenesData.size() > 0);
197 Scene sceneZero = scenesData.get(0);
198 assertNotNull(sceneZero);
199 sceneId = sceneZero.id;
200 assertTrue(sceneId > 0);
202 for (Scene scene : scenesData) {
203 String sceneName = scene.getName();
204 assertNotNull(sceneName);
206 } catch (JsonParseException | ProcessingException | HubMaintenanceException e) {
207 fail(e.getMessage());
210 // ==== refresh a specific shade ====
214 assertNotEquals(0, shadeId);
215 shade = webTargets.refreshShade(shadeId);
216 assertNotNull(shade);
217 } catch (ProcessingException | HubMaintenanceException e) {
218 fail(e.getMessage());
221 // ==== move a specific shade ====
223 assertNotEquals(0, shadeId);
224 assertNotNull(shade);
226 ShadeData shadeData = shade.shade;
227 assertNotNull(shadeData);
228 ShadePosition positions = shadeData.positions;
229 assertNotNull(positions);
230 CoordinateSystem coordSys = positions.getCoordinateSystem(PRIMARY_ACTUATOR);
231 assertNotNull(coordSys);
233 pos = positions.getState(PRIMARY_ACTUATOR, coordSys);
234 assertEquals(PercentType.class, pos.getClass());
236 pos = positions.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
237 assertEquals(PercentType.class, pos.getClass());
239 int position = ((PercentType) pos).intValue();
240 position = position + ((position <= 10) ? 5 : -5);
242 ShadePosition newPos = ShadePosition.create(ZERO_IS_CLOSED, position);
243 assertNotNull(newPos);
245 if (allowShadeMovementCommands) {
246 webTargets.moveShade(shadeId, newPos);
248 } catch (ProcessingException | HubMaintenanceException e) {
249 fail(e.getMessage());
252 // ==== activate a specific scene ====
253 if (allowShadeMovementCommands) {
255 assertNotNull(sceneId);
256 webTargets.activateScene(sceneId);
257 } catch (ProcessingException | HubMaintenanceException e) {
258 fail(e.getMessage());
265 * Run a series of OFFLINE tests on the JSON parsing machinery
268 public void testOfflineJsonParsing() {
269 final Gson gson = new Gson();
273 // test generic JSON shades response
276 String json = loadJson("shades");
278 assertNotEquals("", json);
279 shades = gson.fromJson(json, Shades.class);
280 assertNotNull(shades);
281 } catch (JsonParseException e) {
282 fail(e.getMessage());
285 // test generic JSON scenes response
288 String json = loadJson("scenes");
290 assertNotEquals("", json);
292 Scenes scenes = gson.fromJson(json, Scenes.class);
293 assertNotNull(scenes);
294 } catch (JsonParseException e) {
295 fail(e.getMessage());
298 // test the JSON parsing for a duette top down bottom up shade
301 ShadeData shadeData = null;
302 String json = loadJson("duette");
304 assertNotEquals("", json);
306 shades = gson.fromJson(json, Shades.class);
307 assertNotNull(shades);
309 List<ShadeData> shadesData = shades.shadeData;
310 assertNotNull(shadesData);
312 assertEquals(1, shadesData.size());
313 shadeData = shadesData.get(0);
314 assertNotNull(shadeData);
316 assertEquals("Gardin 1", shadeData.getName());
317 assertEquals(63778, shadeData.id);
319 ShadePosition shadePos = shadeData.positions;
320 assertNotNull(shadePos);
321 assertEquals(ZERO_IS_CLOSED, shadePos.getCoordinateSystem(PRIMARY_ACTUATOR));
323 State pos = shadePos.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
324 assertEquals(PercentType.class, pos.getClass());
325 assertEquals(59, ((PercentType) pos).intValue());
327 pos = shadePos.getState(SECONDARY_ACTUATOR, ZERO_IS_OPEN);
328 assertEquals(PercentType.class, pos.getClass());
329 assertEquals(65, ((PercentType) pos).intValue());
331 pos = shadePos.getState(PRIMARY_ACTUATOR, VANE_COORDS);
332 assertEquals(UnDefType.class, pos.getClass());
333 } catch (JsonParseException e) {
334 fail(e.getMessage());