]> git.basschouten.com Git - openhab-addons.git/blob
0417e15359b3911b016af96818035883f19e8ce0
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.neohub.test;
14
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
17
18 import java.io.BufferedReader;
19 import java.io.FileReader;
20 import java.io.IOException;
21 import java.math.BigDecimal;
22 import java.time.Instant;
23 import java.util.regex.Pattern;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.junit.jupiter.api.Test;
27 import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData;
28 import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
29 import org.openhab.binding.neohub.internal.NeoHubConfiguration;
30 import org.openhab.binding.neohub.internal.NeoHubGetEngineersData;
31 import org.openhab.binding.neohub.internal.NeoHubInfoResponse;
32 import org.openhab.binding.neohub.internal.NeoHubInfoResponse.InfoRecord;
33 import org.openhab.binding.neohub.internal.NeoHubLiveDeviceData;
34 import org.openhab.binding.neohub.internal.NeoHubReadDcbResponse;
35 import org.openhab.binding.neohub.internal.NeoHubSocket;
36 import org.openhab.core.library.unit.ImperialUnits;
37 import org.openhab.core.library.unit.SIUnits;
38
39 /**
40  * JUnit for testing JSON parsing.
41  *
42  * @author Andrew Fiddian-Green - Initial contribution
43  */
44 @NonNullByDefault
45 public class NeoHubJsonTests {
46
47     /*
48      * to actually run tests on a physical device you must have a hub physically available, and its IP address must be
49      * correctly configured in the "hubIPAddress" string constant e.g. "192.168.1.123"
50      * note: only run the test if such a device is actually available
51      */
52     private static final String HUB_IP_ADDRESS = "192.168.1.xxx";
53
54     public static final Pattern VALID_IP_V4_ADDRESS = Pattern
55             .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
56
57     /**
58      * Load the test JSON payload string from a file
59      */
60     private String load(String fileName) {
61         try (FileReader file = new FileReader(String.format("src/test/resources/%s.json", fileName));
62                 BufferedReader reader = new BufferedReader(file)) {
63             StringBuilder builder = new StringBuilder();
64             String line;
65             while ((line = reader.readLine()) != null) {
66                 builder.append(line).append("\n");
67             }
68             return builder.toString();
69         } catch (IOException e) {
70             fail(e.getMessage());
71         }
72         return "";
73     }
74
75     /**
76      * Test an INFO JSON response string as produced by older firmware versions
77      */
78     @Test
79     public void testInfoJsonOld() {
80         // load INFO JSON response string in old JSON format
81         NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_old"));
82         assertNotNull(infoResponse);
83
84         // missing device
85         AbstractRecord device = infoResponse.getDeviceRecord("Aardvark");
86         assertNull(device);
87
88         // existing type 12 thermostat device
89         device = infoResponse.getDeviceRecord("Dining Room");
90         assertNotNull(device);
91         assertEquals("Dining Room", device.getDeviceName());
92         assertEquals(new BigDecimal("22.0"), device.getTargetTemperature());
93         assertEquals(new BigDecimal("22.2"), device.getActualTemperature());
94         assertEquals(new BigDecimal("23"), device.getFloorTemperature());
95         assertTrue(device instanceof InfoRecord);
96         assertEquals(12, ((InfoRecord) device).getDeviceType());
97         assertFalse(device.isStandby());
98         assertFalse(device.isHeating());
99         assertFalse(device.isPreHeating());
100         assertFalse(device.isTimerOn());
101         assertFalse(device.offline());
102         assertFalse(device.stateManual());
103         assertTrue(device.stateAuto());
104         assertFalse(device.isWindowOpen());
105         assertFalse(device.isBatteryLow());
106
107         // existing type 6 plug device (MANUAL OFF)
108         device = infoResponse.getDeviceRecord("Plug South");
109         assertNotNull(device);
110         assertEquals("Plug South", device.getDeviceName());
111         assertTrue(device instanceof InfoRecord);
112         assertEquals(6, ((InfoRecord) device).getDeviceType());
113         assertFalse(device.isTimerOn());
114         assertTrue(device.stateManual());
115
116         // existing type 6 plug device (MANUAL ON)
117         device = infoResponse.getDeviceRecord("Plug North");
118         assertNotNull(device);
119         assertEquals("Plug North", device.getDeviceName());
120         assertTrue(device instanceof InfoRecord);
121         assertEquals(6, ((InfoRecord) device).getDeviceType());
122         assertTrue(device.isTimerOn());
123         assertTrue(device.stateManual());
124
125         // existing type 6 plug device (AUTO OFF)
126         device = infoResponse.getDeviceRecord("Watering System");
127         assertNotNull(device);
128         assertEquals("Watering System", device.getDeviceName());
129         assertTrue(device instanceof InfoRecord);
130         assertEquals(6, ((InfoRecord) device).getDeviceType());
131         assertFalse(device.isTimerOn());
132         assertFalse(device.stateManual());
133     }
134
135     /**
136      * Test an INFO JSON response string as produced by newer firmware versions
137      */
138     @Test
139     public void testInfoJsonNew() {
140         // load INFO JSON response string in new JSON format
141         NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_new"));
142         assertNotNull(infoResponse);
143
144         // existing device (new JSON format)
145         AbstractRecord device = infoResponse.getDeviceRecord("Dining Room");
146         assertNotNull(device);
147         assertEquals("Dining Room", device.getDeviceName());
148         assertFalse(device.offline());
149         assertFalse(device.isWindowOpen());
150
151         // existing repeater device
152         device = infoResponse.getDeviceRecord("repeaternode54473");
153         assertNotNull(device);
154         assertEquals("repeaternode54473", device.getDeviceName());
155         assertEquals(new BigDecimal("127"), device.getFloorTemperature());
156         assertEquals(new BigDecimal("255.255"), device.getActualTemperature());
157     }
158
159     /**
160      * Test for a READ_DCB JSON string that has valid CORF C response
161      */
162     @Test
163     public void testReadDcbJson() {
164         // load READ_DCB JSON response string with valid CORF C response
165         NeoHubReadDcbResponse dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_celsius"));
166         assertNotNull(dcbResponse);
167         assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
168         assertEquals("2134", dcbResponse.getFirmwareVersion());
169
170         // load READ_DCB JSON response string with valid CORF F response
171         dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_fahrenheit"));
172         assertNotNull(dcbResponse);
173         assertEquals(ImperialUnits.FAHRENHEIT, dcbResponse.getTemperatureUnit());
174
175         // load READ_DCB JSON response string with missing CORF element
176         dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_corf_missing"));
177         assertNotNull(dcbResponse);
178         assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
179
180         // load READ_DCB JSON response string where CORF element is an empty string
181         dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_corf_empty"));
182         assertNotNull(dcbResponse);
183         assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
184     }
185
186     /**
187      * Test an INFO JSON string that has a door contact and a temperature sensor
188      */
189     @Test
190     public void testInfoJsonWithSensors() {
191         /*
192          * load an INFO JSON response string that has a closed door contact and a
193          * temperature sensor
194          */
195         // save("info_sensors_closed", NEOHUB_JSON_TEST_STRING_INFO_SENSORS_CLOSED);
196         NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_sensors_closed"));
197         assertNotNull(infoResponse);
198
199         // existing contact device type 5 (CLOSED)
200         AbstractRecord device = infoResponse.getDeviceRecord("Back Door");
201         assertNotNull(device);
202         assertEquals("Back Door", device.getDeviceName());
203         assertTrue(device instanceof InfoRecord);
204         assertEquals(5, ((InfoRecord) device).getDeviceType());
205         assertFalse(device.isWindowOpen());
206         assertFalse(device.isBatteryLow());
207
208         // existing temperature sensor type 14
209         device = infoResponse.getDeviceRecord("Master Bedroom");
210         assertNotNull(device);
211         assertEquals("Master Bedroom", device.getDeviceName());
212         assertTrue(device instanceof InfoRecord);
213         assertEquals(14, ((InfoRecord) device).getDeviceType());
214         assertEquals(new BigDecimal("19.5"), device.getActualTemperature());
215
216         // existing thermostat type 1
217         device = infoResponse.getDeviceRecord("Living Room Floor");
218         assertNotNull(device);
219         assertEquals("Living Room Floor", device.getDeviceName());
220         assertTrue(device instanceof InfoRecord);
221         assertEquals(1, ((InfoRecord) device).getDeviceType());
222         assertEquals(new BigDecimal("19.8"), device.getActualTemperature());
223
224         // load an INFO JSON response string that has an open door contact
225         // save("info_sensors_open", NEOHUB_JSON_TEST_STRING_INFO_SENSORS_OPEN);
226         infoResponse = NeoHubInfoResponse.createDeviceData(load("info_sensors_open"));
227         assertNotNull(infoResponse);
228
229         // existing contact device type 5 (OPEN)
230         device = infoResponse.getDeviceRecord("Back Door");
231         assertNotNull(device);
232         assertEquals("Back Door", device.getDeviceName());
233         assertTrue(device instanceof InfoRecord);
234         assertEquals(5, ((InfoRecord) device).getDeviceType());
235         assertTrue(device.isWindowOpen());
236         assertTrue(device.isBatteryLow());
237     }
238
239     /**
240      * From NeoHub rev2.6 onwards the READ_DCB command is "deprecated" so we can
241      * also test the replacement GET_SYSTEM command (valid CORF response)
242      */
243     @Test
244     public void testGetSystemJson() {
245         // load GET_SYSTEM JSON response string
246         NeoHubReadDcbResponse dcbResponse;
247         dcbResponse = NeoHubReadDcbResponse.createSystemData(load("system"));
248         assertNotNull(dcbResponse);
249         assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
250         assertEquals("2134", dcbResponse.getFirmwareVersion());
251     }
252
253     /**
254      * From NeoHub rev2.6 onwards the INFO command is "deprecated" so we must test
255      * the replacement GET_LIVE_DATA command
256      */
257     @Test
258     public void testGetLiveDataJson() {
259         // load GET_LIVE_DATA JSON response string
260         NeoHubLiveDeviceData liveDataResponse = NeoHubLiveDeviceData.createDeviceData(load("live_data"));
261         assertNotNull(liveDataResponse);
262
263         // test the time stamps
264         assertEquals(1588494785, liveDataResponse.getTimestampEngineers());
265         assertEquals(0, liveDataResponse.getTimestampSystem());
266
267         // missing device
268         AbstractRecord device = liveDataResponse.getDeviceRecord("Aardvark");
269         assertNull(device);
270
271         // test an existing thermostat device
272         device = liveDataResponse.getDeviceRecord("Dining Room");
273         assertNotNull(device);
274         assertEquals("Dining Room", device.getDeviceName());
275         assertEquals(new BigDecimal("22.0"), device.getTargetTemperature());
276         assertEquals(new BigDecimal("22.2"), device.getActualTemperature());
277         assertEquals(new BigDecimal("20.50"), device.getFloorTemperature());
278         assertFalse(device.isStandby());
279         assertFalse(device.isHeating());
280         assertFalse(device.isPreHeating());
281         assertFalse(device.isTimerOn());
282         assertFalse(device.offline());
283         assertFalse(device.stateManual());
284         assertTrue(device.stateAuto());
285         assertFalse(device.isWindowOpen());
286         assertFalse(device.isBatteryLow());
287
288         // test a plug device (MANUAL OFF)
289         device = liveDataResponse.getDeviceRecord("Living Room South");
290         assertNotNull(device);
291         assertEquals("Living Room South", device.getDeviceName());
292         assertFalse(device.isTimerOn());
293         assertTrue(device.stateManual());
294
295         // test a plug device (MANUAL ON)
296         device = liveDataResponse.getDeviceRecord("Living Room North");
297         assertNotNull(device);
298         assertEquals("Living Room North", device.getDeviceName());
299         assertTrue(device.isTimerOn());
300         assertTrue(device.stateManual());
301
302         // test a plug device (AUTO OFF)
303         device = liveDataResponse.getDeviceRecord("Green Wall Watering");
304         assertNotNull(device);
305         assertEquals("Green Wall Watering", device.getDeviceName());
306         assertFalse(device.isTimerOn());
307         assertFalse(device.stateManual());
308
309         // test a device that is offline
310         device = liveDataResponse.getDeviceRecord("Shower Room");
311         assertNotNull(device);
312         assertEquals("Shower Room", device.getDeviceName());
313         assertTrue(device.offline());
314
315         // test a device with a low battery
316         device = liveDataResponse.getDeviceRecord("Conservatory");
317         assertNotNull(device);
318         assertEquals("Conservatory", device.getDeviceName());
319         assertTrue(device.isBatteryLow());
320
321         // test a device with an open window alarm
322         device = liveDataResponse.getDeviceRecord("Door Contact");
323         assertNotNull(device);
324         assertEquals("Door Contact", device.getDeviceName());
325         assertTrue(device.isWindowOpen());
326
327         // test a wireless temperature sensor
328         device = liveDataResponse.getDeviceRecord("Room Sensor");
329         assertNotNull(device);
330         assertEquals("Room Sensor", device.getDeviceName());
331         assertEquals(new BigDecimal("21.5"), device.getActualTemperature());
332
333         // test a repeater node
334         device = liveDataResponse.getDeviceRecord("repeaternode54473");
335         assertNotNull(device);
336         assertEquals("repeaternode54473", device.getDeviceName());
337         assertTrue(MATCHER_HEATMISER_REPEATER.matcher(device.getDeviceName()).matches());
338     }
339
340     /**
341      * From NeoHub rev2.6 onwards the INFO command is "deprecated" and the DEVICE_ID
342      * element is not returned in the GET_LIVE_DATA call so we must test the
343      * replacement GET_ENGINEERS command
344      */
345     @Test
346     public void testGetEngineersJson() {
347         // load GET_ENGINEERS JSON response string
348         NeoHubGetEngineersData engResponse = NeoHubGetEngineersData.createEngineersData(load("engineers"));
349         assertNotNull(engResponse);
350
351         // test device ID (type 12 thermostat device)
352         assertEquals(12, engResponse.getDeviceType("Dining Room"));
353
354         // test device ID (type 6 plug device)
355         assertEquals(6, engResponse.getDeviceType("Living Room South"));
356     }
357
358     /**
359      * send JSON request to the socket and retrieve JSON response
360      */
361     private String testCommunicationInner(String requestJson) {
362         NeoHubConfiguration config = new NeoHubConfiguration();
363         config.hostName = HUB_IP_ADDRESS;
364         config.socketTimeout = 5;
365         try {
366             NeoHubSocket socket = new NeoHubSocket(config);
367             String responseJson = socket.sendMessage(requestJson);
368             socket.close();
369             return responseJson;
370         } catch (Exception e) {
371             assertTrue(false);
372         }
373         return "";
374     }
375
376     /**
377      * Test the communications
378      */
379     @Test
380     public void testCommunications() {
381         /*
382          * tests the actual communication with a real physical device on 'hubIpAddress'
383          * note: only run the test if such a device is actually available
384          */
385         if (!VALID_IP_V4_ADDRESS.matcher(HUB_IP_ADDRESS).matches()) {
386             return;
387         }
388
389         String responseJson = testCommunicationInner(CMD_CODE_INFO);
390         assertFalse(responseJson.isEmpty());
391
392         responseJson = testCommunicationInner(CMD_CODE_READ_DCB);
393         assertFalse(responseJson.isEmpty());
394
395         NeoHubReadDcbResponse dcbResponse = NeoHubReadDcbResponse.createSystemData(responseJson);
396         assertNotNull(dcbResponse);
397
398         long timeStamp = dcbResponse.timeStamp;
399         assertEquals(Instant.now().getEpochSecond(), timeStamp, 1);
400
401         responseJson = testCommunicationInner(CMD_CODE_GET_LIVE_DATA);
402         assertFalse(responseJson.isEmpty());
403
404         NeoHubLiveDeviceData liveDataResponse = NeoHubLiveDeviceData.createDeviceData(responseJson);
405         assertNotNull(liveDataResponse);
406
407         assertTrue(timeStamp > liveDataResponse.getTimestampEngineers());
408         assertTrue(timeStamp > liveDataResponse.getTimestampSystem());
409
410         responseJson = testCommunicationInner(CMD_CODE_GET_ENGINEERS);
411         assertFalse(responseJson.isEmpty());
412
413         responseJson = testCommunicationInner(CMD_CODE_GET_SYSTEM);
414         assertFalse(responseJson.isEmpty());
415
416         responseJson = testCommunicationInner(String.format(CMD_CODE_TEMP, "20", "Hallway"));
417         assertFalse(responseJson.isEmpty());
418     }
419 }