2 * Copyright (c) 2010-2023 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.internal.gen3;
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.Mockito.mock;
17 import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.charset.StandardCharsets;
22 import java.util.ArrayList;
23 import java.util.List;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.junit.jupiter.api.Test;
28 import org.openhab.binding.hdpowerview.internal.HDPowerViewJUnitTests;
29 import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase;
30 import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities;
31 import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem;
32 import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene;
33 import org.openhab.binding.hdpowerview.internal.dto.gen3.SceneEvent;
34 import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade;
35 import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadeEvent;
36 import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition;
37 import org.openhab.binding.hdpowerview.internal.handler.ShadeThingHandler;
38 import org.openhab.core.library.types.DecimalType;
39 import org.openhab.core.library.types.OnOffType;
40 import org.openhab.core.library.types.PercentType;
41 import org.openhab.core.library.types.QuantityType;
42 import org.openhab.core.library.unit.Units;
43 import org.openhab.core.thing.Channel;
44 import org.openhab.core.thing.ChannelUID;
45 import org.openhab.core.thing.Thing;
46 import org.openhab.core.thing.ThingTypeUID;
47 import org.openhab.core.thing.ThingUID;
48 import org.openhab.core.thing.binding.ThingHandlerCallback;
49 import org.openhab.core.thing.binding.builder.ChannelBuilder;
50 import org.openhab.core.thing.binding.builder.ThingBuilder;
51 import org.openhab.core.types.UnDefType;
53 import com.google.gson.Gson;
56 * Unit tests for Generation 3 DTO's.
58 * @author Andrew Fiddian-Green - Initial contribution
61 public class Generation3DtoTest {
63 private final Gson gson = new Gson();
64 private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
66 private String loadJson(String filename) throws IOException {
67 try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) {
68 if (inputStream == null) {
69 throw new IOException("inputstream is null");
71 byte[] bytes = inputStream.readAllBytes();
73 throw new IOException("Resulting byte-array empty");
75 return new String(bytes, StandardCharsets.UTF_8);
80 * Test JSON scene event response.
83 public void testSceneEventParsing() throws IOException {
84 String json = loadJson("gen3/scene-event.json");
85 SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class);
86 assertNotNull(sceneEvent);
87 Scene scene = sceneEvent.getScene();
89 assertEquals("Open All Office Shades\n Open All Office Shades", scene.getName());
90 assertEquals(234, scene.getId());
94 * Test JSON scenes response.
97 public void testScenesParsing() throws IOException {
98 String json = loadJson("gen3/scenes.json");
99 List<Scene> sceneList = List.of(gson.fromJson(json, Scene[].class));
100 assertNotNull(sceneList);
101 assertEquals(1, sceneList.size());
102 Scene scene = sceneList.get(0);
103 assertEquals("Open All Office Shades\n ABC", scene.getName());
104 assertEquals(234, scene.getId());
108 * Test JSON shade event response.
111 public void testShadeEventParsing() throws IOException {
112 String json = loadJson("gen3/shade-event.json");
113 ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class);
114 assertNotNull(shadeEvent);
115 ShadePosition position = shadeEvent.getCurrentPositions();
116 assertNotNull(position);
117 assertEquals(PercentType.valueOf("99"), position.getState(CoordinateSystem.PRIMARY_POSITION));
118 assertEquals(PercentType.valueOf("98"), position.getState(CoordinateSystem.SECONDARY_POSITION));
119 assertEquals(PercentType.ZERO, position.getState(CoordinateSystem.VANE_TILT_POSITION));
123 * Test JSON shade position setting.
126 public void testShadePositions() {
129 pos = new ShadePosition();
130 pos.setPosition(CoordinateSystem.PRIMARY_POSITION, new PercentType(11));
131 assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION));
132 assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.SECONDARY_POSITION));
133 assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.VANE_TILT_POSITION));
135 pos = new ShadePosition();
136 pos.setPosition(CoordinateSystem.PRIMARY_POSITION, new PercentType(11));
137 pos.setPosition(CoordinateSystem.SECONDARY_POSITION, new PercentType(22));
138 pos.setPosition(CoordinateSystem.VANE_TILT_POSITION, new PercentType(33));
139 assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION));
140 assertEquals(PercentType.valueOf("22"), pos.getState(CoordinateSystem.SECONDARY_POSITION));
141 assertEquals(PercentType.valueOf("33"), pos.getState(CoordinateSystem.VANE_TILT_POSITION));
145 * Test JSON shades response.
148 public void testShadesParsing() throws IOException {
149 String json = loadJson("gen3/shades.json");
150 List<Shade> shadeList = List.of(gson.fromJson(json, Shade[].class));
151 assertNotNull(shadeList);
152 assertEquals(2, shadeList.size());
153 Shade shadeData = shadeList.get(0);
154 assertEquals("Upper Left", shadeData.getName());
155 assertEquals(2, shadeData.getId());
156 assertFalse(shadeData.isMainsPowered());
157 assertEquals(new DecimalType(66), shadeData.getBatteryLevel());
158 assertEquals(OnOffType.OFF, shadeData.getLowBattery());
159 ShadePosition positions = shadeData.getShadePositions();
160 assertNotNull(positions);
161 Integer caps = shadeData.getCapabilities();
163 Capabilities capabilities = db.getCapabilities(caps);
164 assertTrue(capabilities.supportsPrimary());
165 assertFalse(capabilities.supportsSecondary());
166 assertFalse(capabilities.supportsTilt180());
167 assertFalse(capabilities.supportsTiltAnywhere());
168 assertFalse(capabilities.supportsTiltOnClosed());
170 shadeData = shadeList.get(1);
171 assertEquals(3, shadeData.getId());
172 assertTrue(shadeData.isMainsPowered());
173 positions = shadeData.getShadePositions();
174 assertNotNull(positions);
175 caps = shadeData.getCapabilities();
177 capabilities = db.getCapabilities(caps);
178 assertTrue(capabilities.supportsPrimary());
179 assertFalse(capabilities.supportsSecondary());
180 assertFalse(capabilities.supportsTilt180());
181 assertFalse(capabilities.supportsTiltAnywhere());
182 assertTrue(capabilities.supportsTiltOnClosed());
186 * Test sending properties and dynamic channel values to a shade handler.
189 public void testShadeHandlerPropertiesAndChannels() throws IOException {
190 ThingTypeUID thingTypeUID = new ThingTypeUID("hdpowerview:shade");
191 ThingUID thingUID = new ThingUID(thingTypeUID, "test");
193 List<Channel> channels = new ArrayList<Channel>();
194 for (String channelId : Set.of(CHANNEL_SHADE_POSITION, CHANNEL_SHADE_SECONDARY_POSITION, CHANNEL_SHADE_VANE,
195 CHANNEL_SHADE_BATTERY_LEVEL, CHANNEL_SHADE_LOW_BATTERY, CHANNEL_SHADE_SIGNAL_STRENGTH)) {
196 ChannelUID channelUID = new ChannelUID(thingUID, channelId);
197 channels.add(ChannelBuilder.create(channelUID).build());
200 String json = loadJson("gen3/shades.json");
201 List<Shade> shadeList = List.of(gson.fromJson(json, Shade[].class));
202 assertNotNull(shadeList);
203 assertEquals(2, shadeList.size());
205 Thing thing = ThingBuilder.create(thingTypeUID, thingUID).withChannels(channels).build();
206 ShadeThingHandler shadeThingHandler;
210 * Use the first JSON Shade entry.
211 * It should support 4 dynamic channels.
213 shadeThingHandler = new ShadeThingHandler(thing);
214 shadeThingHandler.setCallback(mock(ThingHandlerCallback.class));
215 shadeData = shadeList.get(0).setId(0);
216 assertTrue(shadeData.hasFullState());
217 shadeThingHandler.notify(shadeData);
218 Thing handlerThing = shadeThingHandler.getThing();
219 assertEquals("Duette (6)", handlerThing.getProperties().get("type"));
220 assertEquals("battery", handlerThing.getProperties().get("powerType"));
221 assertEquals("3.0.359", handlerThing.getProperties().get("firmwareVersion"));
222 assertEquals(new QuantityType<>(-50, Units.DECIBEL_MILLIWATTS), shadeData.getSignalStrength());
223 assertEquals(4, handlerThing.getChannels().size());
226 * Use the second JSON Shade entry.
227 * It should support only 3 dynamic channels.
229 shadeThingHandler = new ShadeThingHandler(thing);
230 shadeThingHandler.setCallback(mock(ThingHandlerCallback.class));
231 shadeData = shadeList.get(1).setId(0);
232 assertTrue(shadeData.hasFullState());
233 shadeThingHandler.notify(shadeData);
234 handlerThing = shadeThingHandler.getThing();
235 assertEquals("Silhouette (23)", handlerThing.getProperties().get("type"));
236 assertEquals("hardwired", handlerThing.getProperties().get("powerType"));
237 assertEquals("3.0.359", handlerThing.getProperties().get("firmwareVersion"));
238 assertEquals(new QuantityType<>(-51, Units.DECIBEL_MILLIWATTS), shadeData.getSignalStrength());
239 assertEquals(3, handlerThing.getChannels().size());
243 * Test sending state change events to shade handler.
246 public void testShadeHandlerEvents() throws IOException {
247 ThingTypeUID thingTypeUID = new ThingTypeUID("hdpowerview:shade");
248 ThingUID thingUID = new ThingUID(thingTypeUID, "test");
250 List<Channel> channels = new ArrayList<Channel>();
251 for (String channelId : Set.of(CHANNEL_SHADE_POSITION, CHANNEL_SHADE_SECONDARY_POSITION, CHANNEL_SHADE_VANE,
252 CHANNEL_SHADE_BATTERY_LEVEL, CHANNEL_SHADE_LOW_BATTERY, CHANNEL_SHADE_SIGNAL_STRENGTH)) {
253 ChannelUID channelUID = new ChannelUID(thingUID, channelId);
254 channels.add(ChannelBuilder.create(channelUID).build());
257 String json = loadJson("gen3/shades.json");
258 List<Shade> shadeList = List.of(gson.fromJson(json, Shade[].class));
259 assertNotNull(shadeList);
260 assertEquals(2, shadeList.size());
262 Thing thing = ThingBuilder.create(thingTypeUID, thingUID).withChannels(channels).build();
263 ShadeThingHandler shadeThingHandler;
267 * Use the second JSON Shade entry, which only has a primary channel.
269 shadeThingHandler = new ShadeThingHandler(thing);
270 shadeThingHandler.setCallback(mock(ThingHandlerCallback.class));
271 shadeData = shadeList.get(1).setId(0);
272 shadeThingHandler.notify(shadeData);
275 * And try to update it with an event that has all 3 channels.
277 json = loadJson("gen3/shade-event.json");
278 ShadeEvent event = gson.fromJson(json, ShadeEvent.class);
279 assertNotNull(event);
280 shadeData = new Shade().setId(0).setShadePosition(event.getCurrentPositions()).setPartialState();
281 assertFalse(shadeData.hasFullState());
282 shadeThingHandler.notify(shadeData);