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.mqtt.ruuvigateway.internal.parser;
15 import static org.junit.jupiter.api.Assertions.*;
17 import java.nio.ByteBuffer;
18 import java.nio.charset.StandardCharsets;
19 import java.time.Instant;
20 import java.util.Optional;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.junit.jupiter.api.Test;
24 import org.openhab.binding.mqtt.ruuvigateway.internal.parser.GatewayPayloadParser.GatewayPayload;
26 import com.google.gson.JsonSyntaxException;
29 * Tests for {@link GatewayPayloadParser}
31 * @author Sami Salonen - Initial Contribution
34 public class GatewayPayloadParserTests {
36 private byte[] bytes(String str) {
37 ByteBuffer buffer = StandardCharsets.UTF_8.encode(str);
39 byte[] bytes = new byte[buffer.remaining()];
45 * Test with valid data.
47 * See 'valid case' test vector from
48 * https://docs.ruuvi.com/communication/bluetooth-advertisements/data-format-5-rawv2
51 public void testValid() {
52 GatewayPayload parsed = GatewayPayloadParser.parse(bytes(//
53 "{\"gw_mac\": \"DE:AD:BE:EF:00:00\","//
56 + " \"gwts\": \"1659365438\","//
57 + " \"ts\": \"1659365439\","//
58 + " \"data\": \"0201061BFF99040512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F\","//
59 + " \"coords\": \"\"" + "}"));
60 assertNotNull(parsed);
61 assertEquals(-83, parsed.rssi);
62 assertEquals(Optional.of(Instant.ofEpochSecond(1659365438)), parsed.gwts);
63 assertEquals(Optional.of(Instant.ofEpochSecond(1659365439)), parsed.ts);
64 assertEquals(24.3, parsed.measurement.getTemperature());
65 assertEquals(100044, parsed.measurement.getPressure());
66 assertEquals(5, parsed.measurement.getDataFormat());
67 assertEquals(53.49, parsed.measurement.getHumidity());
68 assertEquals(0.004, parsed.measurement.getAccelerationX());
69 assertEquals(-0.004, parsed.measurement.getAccelerationY());
70 assertEquals(1.036, parsed.measurement.getAccelerationZ());
71 assertEquals(4, parsed.measurement.getTxPower());
72 assertEquals(2.9770000000000003, parsed.measurement.getBatteryVoltage());
73 assertEquals(66, parsed.measurement.getMovementCounter());
74 assertEquals(205, parsed.measurement.getMeasurementSequenceNumber());
78 public void testInvalidJSON() {
79 assertThrows(JsonSyntaxException.class, () -> {
80 GatewayPayloadParser.parse(bytes(//
86 public void testUnexpectedTypes() {
87 assertThrows(IllegalArgumentException.class, () -> {
88 GatewayPayloadParser.parse(bytes(//
89 "{\"gw_mac\": \"DE:AD:BE:EF:00:00\","//
92 + " \"gwts\": \"1659365438\","//
93 + " \"ts\": \"1659365438\","//
94 + " \"data\": 666," // should be hex-string of even length
95 + " \"coords\": \"\"" + "}"));
100 public void testInvalidHex() {
101 assertThrows(IllegalArgumentException.class, () -> {
102 GatewayPayloadParser.parse(bytes(//
103 "{\"gw_mac\": \"DE:AD:BE:EF:00:00\","//
104 + " \"rssi\": -83,"//
106 + " \"gwts\": \"1659365438\","//
107 + " \"ts\": \"1659365438\","//
108 + " \"data\": \"XYZZ\"," // should be hex string
109 + " \"coords\": \"\"" + "}"));
114 public void testUnexpectedTypes3() {
115 assertThrows(JsonSyntaxException.class, () -> {
116 GatewayPayloadParser.parse(bytes(//
117 "{\"gw_mac\": \"DE:AD:BE:EF:00:00\","//
118 + " \"rssi\": \"foobar\","// should be number
120 + " \"gwts\": \"1659365438\","//
121 + " \"ts\": \"1659365438\","//
122 + " \"data\": \"0201061BFF99040512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F\","
123 + " \"coords\": \"\"" + "}"));
128 public void testDataTooShort() {
129 assertThrows(IllegalArgumentException.class, () -> {
130 GatewayPayloadParser.parse(bytes(//
131 "{\"gw_mac\": \"DE:AD:BE:EF:00:00\","//
132 + " \"rssi\": -83," + " \"aoa\": [],"//
133 + " \"gwts\": \"1659365438\","//
134 + " \"ts\": \"1659365438\","//
135 + " \"data\": \"0201061BFF990405\"," // too short
136 + " \"coords\": \"\"" + "}"));
141 public void testUnexpectedManufacturer() {
142 assertThrows(IllegalArgumentException.class, () -> {
143 GatewayPayloadParser.parse(bytes(//
144 "{\"gw_mac\": \"DE:AD:BE:EF:00:00\","//
145 + " \"rssi\": -83,"//
147 + " \"gwts\": \"1659365438\","//
148 + " \"ts\": \"1659365438\","//
149 // manufacturer is not 99 04 (Ruuvi) but 99 99
150 + " \"data\": \"0201061BFF99990512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F\","
151 + " \"coords\": \"\"" + "}"));
156 public void testDataNotBluetoothAdvertisement() {
157 assertThrows(IllegalArgumentException.class, () -> {
158 GatewayPayloadParser.parse(bytes(//
159 "{\"gw_mac\": \"DE:AD:BE:EF:00:00\","//
160 + " \"rssi\": -83,"//
162 + " \"gwts\": \"1659365438\","//
163 + " \"ts\": \"1659365438\","//
164 // not advertisement (FF) but AA
165 + " \"data\": \"0201061BAA99040512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F\","
166 + " \"coords\": \"\"" + "}"));