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