2 * Copyright (c) 2010-2020 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.neohub.test;
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
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;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.junit.jupiter.api.Test;
26 import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData;
27 import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
28 import org.openhab.binding.neohub.internal.NeoHubGetEngineersData;
29 import org.openhab.binding.neohub.internal.NeoHubInfoResponse;
30 import org.openhab.binding.neohub.internal.NeoHubInfoResponse.InfoRecord;
31 import org.openhab.binding.neohub.internal.NeoHubLiveDeviceData;
32 import org.openhab.binding.neohub.internal.NeoHubReadDcbResponse;
33 import org.openhab.binding.neohub.internal.NeoHubSocket;
34 import org.openhab.core.library.unit.ImperialUnits;
35 import org.openhab.core.library.unit.SIUnits;
38 * The {@link NeoHubTestData} class defines common constants, which are used
39 * across the whole binding.
41 * @author Andrew Fiddian-Green - Initial contribution
44 public class NeoHubTestData {
47 * Load the test JSON payload string from a file
49 private String load(String fileName) {
50 try (FileReader file = new FileReader(String.format("src/test/resources/%s.json", fileName));
51 BufferedReader reader = new BufferedReader(file)) {
52 StringBuilder builder = new StringBuilder();
54 while ((line = reader.readLine()) != null) {
55 builder.append(line).append("\n");
57 return builder.toString();
58 } catch (IOException e) {
65 * Test an INFO JSON response string as produced by older firmware versions
68 public void testInfoJsonOld() {
69 // load INFO JSON response string in old JSON format
70 NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_old"));
71 assertNotNull(infoResponse);
74 AbstractRecord device = infoResponse.getDeviceRecord("Aardvark");
77 // existing type 12 thermostat device
78 device = infoResponse.getDeviceRecord("Dining Room");
79 assertNotNull(device);
80 assertEquals("Dining Room", device.getDeviceName());
81 assertEquals(new BigDecimal("22.0"), device.getTargetTemperature());
82 assertEquals(new BigDecimal("22.2"), device.getActualTemperature());
83 assertEquals(new BigDecimal("23"), device.getFloorTemperature());
84 assertTrue(device instanceof InfoRecord);
85 assertEquals(12, ((InfoRecord) device).getDeviceType());
86 assertFalse(device.isStandby());
87 assertFalse(device.isHeating());
88 assertFalse(device.isPreHeating());
89 assertFalse(device.isTimerOn());
90 assertFalse(device.offline());
91 assertFalse(device.stateManual());
92 assertTrue(device.stateAuto());
93 assertFalse(device.isWindowOpen());
94 assertFalse(device.isBatteryLow());
96 // existing type 6 plug device (MANUAL OFF)
97 device = infoResponse.getDeviceRecord("Plug South");
98 assertNotNull(device);
99 assertEquals("Plug South", device.getDeviceName());
100 assertTrue(device instanceof InfoRecord);
101 assertEquals(6, ((InfoRecord) device).getDeviceType());
102 assertFalse(device.isTimerOn());
103 assertTrue(device.stateManual());
105 // existing type 6 plug device (MANUAL ON)
106 device = infoResponse.getDeviceRecord("Plug North");
107 assertNotNull(device);
108 assertEquals("Plug North", device.getDeviceName());
109 assertTrue(device instanceof InfoRecord);
110 assertEquals(6, ((InfoRecord) device).getDeviceType());
111 assertTrue(device.isTimerOn());
112 assertTrue(device.stateManual());
114 // existing type 6 plug device (AUTO OFF)
115 device = infoResponse.getDeviceRecord("Watering System");
116 assertNotNull(device);
117 assertEquals("Watering System", device.getDeviceName());
118 assertTrue(device instanceof InfoRecord);
119 assertEquals(6, ((InfoRecord) device).getDeviceType());
120 assertFalse(device.isTimerOn());
121 assertFalse(device.stateManual());
125 * Test an INFO JSON response string as produced by newer firmware versions
128 public void testInfoJsonNew() {
129 // load INFO JSON response string in new JSON format
130 NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_new"));
131 assertNotNull(infoResponse);
133 // existing device (new JSON format)
134 AbstractRecord device = infoResponse.getDeviceRecord("Dining Room");
135 assertNotNull(device);
136 assertEquals("Dining Room", device.getDeviceName());
137 assertFalse(device.offline());
138 assertFalse(device.isWindowOpen());
140 // existing repeater device
141 device = infoResponse.getDeviceRecord("repeaternode54473");
142 assertNotNull(device);
143 assertEquals("repeaternode54473", device.getDeviceName());
144 assertEquals(new BigDecimal("127"), device.getFloorTemperature());
145 assertEquals(new BigDecimal("255.255"), device.getActualTemperature());
149 * Test for a READ_DCB JSON string that has valid CORF C response
152 public void testReadDcbJson() {
153 // load READ_DCB JSON response string with valid CORF C response
154 NeoHubReadDcbResponse dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_celsius"));
155 assertNotNull(dcbResponse);
156 assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
158 // load READ_DCB JSON response string with valid CORF F response
159 dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_fahrenheit"));
160 assertNotNull(dcbResponse);
161 assertEquals(ImperialUnits.FAHRENHEIT, dcbResponse.getTemperatureUnit());
163 // load READ_DCB JSON response string with missing CORF element
164 dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_corf_missing"));
165 assertNotNull(dcbResponse);
166 assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
168 // load READ_DCB JSON response string where CORF element is an empty string
169 dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_corf_empty"));
170 assertNotNull(dcbResponse);
171 assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
175 * Test an INFO JSON string that has a door contact and a temperature sensor
178 public void testInfoJsonWithSensors() {
180 * load an INFO JSON response string that has a closed door contact and a
183 // save("info_sensors_closed", NEOHUB_JSON_TEST_STRING_INFO_SENSORS_CLOSED);
184 NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_sensors_closed"));
185 assertNotNull(infoResponse);
187 // existing contact device type 5 (CLOSED)
188 AbstractRecord device = infoResponse.getDeviceRecord("Back Door");
189 assertNotNull(device);
190 assertEquals("Back Door", device.getDeviceName());
191 assertTrue(device instanceof InfoRecord);
192 assertEquals(5, ((InfoRecord) device).getDeviceType());
193 assertFalse(device.isWindowOpen());
194 assertFalse(device.isBatteryLow());
196 // existing temperature sensor type 14
197 device = infoResponse.getDeviceRecord("Master Bedroom");
198 assertNotNull(device);
199 assertEquals("Master Bedroom", device.getDeviceName());
200 assertTrue(device instanceof InfoRecord);
201 assertEquals(14, ((InfoRecord) device).getDeviceType());
202 assertEquals(new BigDecimal("19.5"), device.getActualTemperature());
204 // existing thermostat type 1
205 device = infoResponse.getDeviceRecord("Living Room Floor");
206 assertNotNull(device);
207 assertEquals("Living Room Floor", device.getDeviceName());
208 assertTrue(device instanceof InfoRecord);
209 assertEquals(1, ((InfoRecord) device).getDeviceType());
210 assertEquals(new BigDecimal("19.8"), device.getActualTemperature());
212 // load an INFO JSON response string that has an open door contact
213 // save("info_sensors_open", NEOHUB_JSON_TEST_STRING_INFO_SENSORS_OPEN);
214 infoResponse = NeoHubInfoResponse.createDeviceData(load("info_sensors_open"));
215 assertNotNull(infoResponse);
217 // existing contact device type 5 (OPEN)
218 device = infoResponse.getDeviceRecord("Back Door");
219 assertNotNull(device);
220 assertEquals("Back Door", device.getDeviceName());
221 assertTrue(device instanceof InfoRecord);
222 assertEquals(5, ((InfoRecord) device).getDeviceType());
223 assertTrue(device.isWindowOpen());
224 assertTrue(device.isBatteryLow());
228 * From NeoHub rev2.6 onwards the READ_DCB command is "deprecated" so we can
229 * also test the replacement GET_SYSTEM command (valid CORF response)
232 public void testGetSystemJson() {
233 // load GET_SYSTEM JSON response string
234 NeoHubReadDcbResponse dcbResponse;
235 dcbResponse = NeoHubReadDcbResponse.createSystemData(load("system"));
236 assertNotNull(dcbResponse);
237 assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
241 * From NeoHub rev2.6 onwards the INFO command is "deprecated" so we must test
242 * the replacement GET_LIVE_DATA command
245 public void testGetLiveDataJson() {
246 // load GET_LIVE_DATA JSON response string
247 NeoHubLiveDeviceData liveDataResponse = NeoHubLiveDeviceData.createDeviceData(load("live_data"));
248 assertNotNull(liveDataResponse);
250 // test the time stamps
251 assertEquals(1588494785, liveDataResponse.getTimestampEngineers());
252 assertEquals(0, liveDataResponse.getTimestampSystem());
255 AbstractRecord device = liveDataResponse.getDeviceRecord("Aardvark");
258 // test an existing thermostat device
259 device = liveDataResponse.getDeviceRecord("Dining Room");
260 assertNotNull(device);
261 assertEquals("Dining Room", device.getDeviceName());
262 assertEquals(new BigDecimal("22.0"), device.getTargetTemperature());
263 assertEquals(new BigDecimal("22.2"), device.getActualTemperature());
264 assertEquals(new BigDecimal("20.50"), device.getFloorTemperature());
265 assertFalse(device.isStandby());
266 assertFalse(device.isHeating());
267 assertFalse(device.isPreHeating());
268 assertFalse(device.isTimerOn());
269 assertFalse(device.offline());
270 assertFalse(device.stateManual());
271 assertTrue(device.stateAuto());
272 assertFalse(device.isWindowOpen());
273 assertFalse(device.isBatteryLow());
275 // test a plug device (MANUAL OFF)
276 device = liveDataResponse.getDeviceRecord("Living Room South");
277 assertNotNull(device);
278 assertEquals("Living Room South", device.getDeviceName());
279 assertFalse(device.isTimerOn());
280 assertTrue(device.stateManual());
282 // test a plug device (MANUAL ON)
283 device = liveDataResponse.getDeviceRecord("Living Room North");
284 assertNotNull(device);
285 assertEquals("Living Room North", device.getDeviceName());
286 assertTrue(device.isTimerOn());
287 assertTrue(device.stateManual());
289 // test a plug device (AUTO OFF)
290 device = liveDataResponse.getDeviceRecord("Green Wall Watering");
291 assertNotNull(device);
292 assertEquals("Green Wall Watering", device.getDeviceName());
293 assertFalse(device.isTimerOn());
294 assertFalse(device.stateManual());
296 // test a device that is offline
297 device = liveDataResponse.getDeviceRecord("Shower Room");
298 assertNotNull(device);
299 assertEquals("Shower Room", device.getDeviceName());
300 assertTrue(device.offline());
302 // test a device with a low battery
303 device = liveDataResponse.getDeviceRecord("Conservatory");
304 assertNotNull(device);
305 assertEquals("Conservatory", device.getDeviceName());
306 assertTrue(device.isBatteryLow());
308 // test a device with an open window alarm
309 device = liveDataResponse.getDeviceRecord("Door Contact");
310 assertNotNull(device);
311 assertEquals("Door Contact", device.getDeviceName());
312 assertTrue(device.isWindowOpen());
314 // test a wireless temperature sensor
315 device = liveDataResponse.getDeviceRecord("Room Sensor");
316 assertNotNull(device);
317 assertEquals("Room Sensor", device.getDeviceName());
318 assertEquals(new BigDecimal("21.5"), device.getActualTemperature());
320 // test a repeater node
321 device = liveDataResponse.getDeviceRecord("repeaternode54473");
322 assertNotNull(device);
323 assertEquals("repeaternode54473", device.getDeviceName());
324 assertTrue(MATCHER_HEATMISER_REPEATER.matcher(device.getDeviceName()).matches());
328 * From NeoHub rev2.6 onwards the INFO command is "deprecated" and the DEVICE_ID
329 * element is not returned in the GET_LIVE_DATA call so we must test the
330 * replacement GET_ENGINEERS command
333 public void testGetEngineersJson() {
334 // load GET_ENGINEERS JSON response string
335 NeoHubGetEngineersData engResponse = NeoHubGetEngineersData.createEngineersData(load("engineers"));
336 assertNotNull(engResponse);
338 // test device ID (type 12 thermostat device)
339 assertEquals(12, engResponse.getDeviceType("Dining Room"));
341 // test device ID (type 6 plug device)
342 assertEquals(6, engResponse.getDeviceType("Living Room South"));
346 * send JSON request to the socket and retrieve JSON response
348 private String testCommunicationInner(String requestJson) {
349 NeoHubSocket socket = new NeoHubSocket("192.168.1.109", 4242, 5);
350 String responseJson = "";
352 responseJson = socket.sendMessage(requestJson);
353 } catch (Exception e) {
360 * Test the communications
363 public void testCommunications() {
364 String responseJson = testCommunicationInner(CMD_CODE_INFO);
365 assertFalse(responseJson.isEmpty());
367 responseJson = testCommunicationInner(CMD_CODE_READ_DCB);
368 assertFalse(responseJson.isEmpty());
370 NeoHubReadDcbResponse dcbResponse = NeoHubReadDcbResponse.createSystemData(responseJson);
371 assertNotNull(dcbResponse);
373 long timeStamp = dcbResponse.timeStamp;
374 assertEquals(Instant.now().getEpochSecond(), timeStamp, 1);
376 responseJson = testCommunicationInner(CMD_CODE_GET_LIVE_DATA);
377 assertFalse(responseJson.isEmpty());
379 NeoHubLiveDeviceData liveDataResponse = NeoHubLiveDeviceData.createDeviceData(responseJson);
380 assertNotNull(liveDataResponse);
382 assertTrue(timeStamp > liveDataResponse.getTimestampEngineers());
383 assertTrue(timeStamp > liveDataResponse.getTimestampSystem());
385 responseJson = testCommunicationInner(CMD_CODE_GET_ENGINEERS);
386 assertFalse(responseJson.isEmpty());
388 responseJson = testCommunicationInner(CMD_CODE_GET_SYSTEM);
389 assertFalse(responseJson.isEmpty());
391 responseJson = testCommunicationInner(String.format(CMD_CODE_TEMP, "20", "Hallway"));
392 assertFalse(responseJson.isEmpty());