2 * Copyright (c) 2010-2022 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.CoordinateSystem.*;
18 import java.io.IOException;
19 import java.nio.file.Files;
20 import java.nio.file.Paths;
21 import java.util.List;
22 import java.util.regex.Pattern;
23 import java.util.stream.Collectors;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jetty.client.HttpClient;
27 import org.junit.jupiter.api.Test;
28 import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
29 import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
30 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
31 import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
32 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
33 import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
34 import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
35 import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
36 import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase;
37 import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities;
38 import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
39 import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
40 import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
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
52 * @author Jacob Laursen - Add support for scene groups
55 public class HDPowerViewJUnitTests {
57 private static final Pattern VALID_IP_V4_ADDRESS = Pattern
58 .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
61 * load a test JSON string from a file.
63 private String loadJson(String fileName) {
65 return Files.readAllLines(Paths.get(String.format("src/test/resources/%s.json", fileName))).stream()
66 .collect(Collectors.joining());
67 } catch (IOException e) {
74 * Run a series of ONLINE tests on the communication with a hub.
76 * @param hubIPAddress must be a valid hub IP address to run the
77 * tests on; or an INVALID IP address to
79 * @param allowShadeMovementCommands set to true if you accept that the tests
80 * shall physically move the shades
83 public void testOnlineCommunication() {
85 * NOTE: in order to actually run these tests you must have a hub physically
86 * available, and its IP address must be correctly configured in the
87 * "hubIPAddress" string constant e.g. "192.168.1.123"
89 String hubIPAddress = "192.168.1.xxx";
92 * NOTE: set allowShadeMovementCommands = true if you accept physically moving
93 * the shades during these tests
95 boolean allowShadeMovementCommands = false;
97 if (VALID_IP_V4_ADDRESS.matcher(hubIPAddress).matches()) {
98 // ==== initialize stuff ====
99 HttpClient client = new HttpClient();
100 assertNotNull(client);
102 // ==== start the client ====
105 assertTrue(client.isStarted());
106 } catch (Exception e) {
107 fail(e.getMessage());
110 HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress);
111 assertNotNull(webTargets);
114 ShadePosition shadePos = null;
115 Shades shadesX = null;
117 // ==== get all shades ====
119 shadesX = webTargets.getShades();
120 assertNotNull(shadesX);
121 List<ShadeData> shadesData = shadesX.shadeData;
122 assertNotNull(shadesData);
124 assertTrue(!shadesData.isEmpty());
126 shadeData = shadesData.get(0);
127 assertNotNull(shadeData);
128 assertTrue(shadeData.getName().length() > 0);
129 shadePos = shadeData.positions;
130 assertNotNull(shadePos);
131 ShadeData shadeZero = shadesData.get(0);
132 assertNotNull(shadeZero);
133 shadeId = shadeZero.id;
134 assertNotEquals(0, shadeId);
136 for (ShadeData shadexData : shadesData) {
137 String shadeName = shadexData.getName();
138 assertNotNull(shadeName);
140 } catch (HubException e) {
141 fail(e.getMessage());
144 // ==== get all scenes ====
147 Scenes scenes = webTargets.getScenes();
148 assertNotNull(scenes);
150 List<Scene> scenesData = scenes.sceneData;
151 assertNotNull(scenesData);
153 assertTrue(!scenesData.isEmpty());
154 Scene sceneZero = scenesData.get(0);
155 assertNotNull(sceneZero);
156 sceneId = sceneZero.id;
157 assertTrue(sceneId > 0);
159 for (Scene scene : scenesData) {
160 String sceneName = scene.getName();
161 assertNotNull(sceneName);
163 } catch (HubException e) {
164 fail(e.getMessage());
167 // ==== refresh a specific shade ====
168 ShadeData shadeData = null;
170 assertNotEquals(0, shadeId);
171 shadeData = webTargets.refreshShadePosition(shadeId);
172 } catch (HubException e) {
173 fail(e.getMessage());
176 // ==== move a specific shade ====
178 assertNotEquals(0, shadeId);
180 if (shadeData != null) {
181 ShadePosition positions = shadeData.positions;
182 assertNotNull(positions);
183 Integer capabilitiesValue = shadeData.capabilities;
184 assertNotNull(capabilitiesValue);
186 Capabilities capabilities = new ShadeCapabilitiesDatabase()
187 .getCapabilities(capabilitiesValue.intValue());
189 State pos = positions.getState(capabilities, PRIMARY_POSITION);
190 assertEquals(PercentType.class, pos.getClass());
192 int position = ((PercentType) pos).intValue();
193 position = position + ((position <= 10) ? 5 : -5);
195 ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION,
197 assertNotNull(targetPosition);
199 if (allowShadeMovementCommands) {
200 webTargets.moveShade(shadeId, targetPosition);
202 ShadeData newData = webTargets.getShade(shadeId);
203 ShadePosition actualPosition = newData.positions;
204 assertNotNull(actualPosition);
205 assertEquals(targetPosition.getState(capabilities, PRIMARY_POSITION),
206 actualPosition.getState(capabilities, PRIMARY_POSITION));
209 } catch (HubException e) {
210 fail(e.getMessage());
213 // ==== activate a specific scene ====
214 if (allowShadeMovementCommands) {
216 assertNotNull(sceneId);
217 webTargets.activateScene(sceneId);
218 } catch (HubProcessingException | HubMaintenanceException e) {
219 fail(e.getMessage());
223 // ==== test stop command ====
224 if (allowShadeMovementCommands) {
226 assertNotNull(sceneId);
227 webTargets.stopShade(shadeId);
228 } catch (HubException e) {
229 fail(e.getMessage());
233 // ==== stop the client ====
234 if (client.isRunning()) {
237 } catch (Exception e) {
238 fail(e.getMessage());
245 * Test generic JSON shades response.
248 public void shadeResponseIsParsedCorrectly() throws JsonParseException {
249 final Gson gson = new Gson();
251 String json = loadJson("shades");
252 assertNotEquals("", json);
253 shades = gson.fromJson(json, Shades.class);
254 assertNotNull(shades);
258 * Test generic JSON scene response.
261 public void sceneResponseIsParsedCorrectly() throws JsonParseException {
262 final Gson gson = new Gson();
263 String json = loadJson("scenes");
264 assertNotEquals("", json);
266 Scenes scenes = gson.fromJson(json, Scenes.class);
267 assertNotNull(scenes);
268 List<Scene> sceneData = scenes.sceneData;
269 assertNotNull(sceneData);
270 assertEquals(4, sceneData.size());
271 Scene scene = sceneData.get(0);
272 assertEquals("Door Open", scene.getName());
273 assertEquals(18097, scene.id);
277 * Test generic JSON scene collection response.
280 public void sceneCollectionResponseIsParsedCorrectly() throws JsonParseException {
281 final Gson gson = new Gson();
282 String json = loadJson("sceneCollections");
283 assertNotEquals("", json);
285 SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
286 assertNotNull(sceneCollections);
288 List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
289 assertNotNull(sceneCollectionData);
290 assertEquals(1, sceneCollectionData.size());
292 SceneCollection sceneCollection = sceneCollectionData.get(0);
293 assertEquals("Børn op", sceneCollection.getName());
294 assertEquals(27119, sceneCollection.id);
298 * Test the JSON parsing for a duette top down bottom up shade.
301 public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws JsonParseException {
302 final Gson gson = new Gson();
303 String json = loadJson("duette");
304 assertNotEquals("", json);
306 Shades shades = gson.fromJson(json, Shades.class);
307 assertNotNull(shades);
308 List<ShadeData> shadesData = shades.shadeData;
309 assertNotNull(shadesData);
311 assertEquals(1, shadesData.size());
312 ShadeData shadeData = shadesData.get(0);
313 assertNotNull(shadeData);
315 assertEquals("Gardin 1", shadeData.getName());
316 assertEquals(63778, shadeData.id);
318 ShadePosition shadePos = shadeData.positions;
319 assertNotNull(shadePos);
321 Integer capabilitiesValue = shadeData.capabilities;
322 assertNotNull(capabilitiesValue);
323 assertEquals(7, capabilitiesValue.intValue());
324 ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
325 Capabilities capabilities = db.getCapabilities(capabilitiesValue);
327 State pos = shadePos.getState(capabilities, PRIMARY_POSITION);
328 assertEquals(PercentType.class, pos.getClass());
329 assertEquals(59, ((PercentType) pos).intValue());
331 pos = shadePos.getState(capabilities, SECONDARY_POSITION);
332 assertEquals(PercentType.class, pos.getClass());
333 assertEquals(35, ((PercentType) pos).intValue());
335 pos = shadePos.getState(capabilities, VANE_TILT_POSITION);
336 assertEquals(UnDefType.class, pos.getClass());
338 assertEquals(3, shadeData.batteryStatus);
340 assertEquals(4, shadeData.signalStrength);
342 assertEquals(8, shadeData.type);
344 assertTrue(db.isTypeInDatabase(shadeData.type));
345 assertTrue(db.isCapabilitiesInDatabase(capabilitiesValue.intValue()));
347 assertEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue());
349 assertTrue(db.getCapabilities(capabilitiesValue.intValue()).supportsSecondary());
350 assertNotEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue() + 1);
352 // ==== when changing position1, position2 value is not changed (vice-versa) ====
353 ShadePosition shadePosition = shadeData.positions;
354 assertNotNull(shadePosition);
355 // ==== position2 ====
356 State position2Old = shadePosition.getState(capabilities, SECONDARY_POSITION);
357 shadePosition.setPosition(capabilities, PRIMARY_POSITION, 99);
358 State position2New = shadePosition.getState(capabilities, SECONDARY_POSITION);
359 assertEquals(PercentType.class, position2Old.getClass());
360 assertEquals(PercentType.class, position2New.getClass());
361 assertEquals(((PercentType) position2Old).intValue(), ((PercentType) position2New).intValue());
363 // ==== position2 ====
364 State position1Old = shadePosition.getState(capabilities, PRIMARY_POSITION);
365 shadePosition.setPosition(capabilities, SECONDARY_POSITION, 99);
366 State position1New = shadePosition.getState(capabilities, PRIMARY_POSITION);
367 assertEquals(PercentType.class, position1Old.getClass());
368 assertEquals(PercentType.class, position1New.getClass());
369 assertEquals(((PercentType) position1Old).intValue(), ((PercentType) position1New).intValue());