mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[shelly] Add support for Shelly BLU Motion ZB, BLU H&T Display ZB, BLU For/Window ZB, BLU Remote ZB (#20967)
* [shelly] BLU ZB step 0: move scalar/array handling from JS to Java Signed-off-by: Markus Michels <markus7017@gmail.com>
This commit is contained in:
@@ -151,10 +151,14 @@ See section [Discovery](#discovery) for details.
|
||||
| shellyblubutton | Shelly BLU Tough ZB | SBBT-102C |
|
||||
| shellybluwallswitch4 | Shelly BLU Wallswitch 4 | SBBT-EU5027 |
|
||||
| shellyblurcbutton4 | Shelly BLU RC Button 4 | SBBT-004CUS |
|
||||
| shellyblurcbutton4 | Shelly BLU RC Button 4 ZB | SBBT-104CUS |
|
||||
| shellybluht | Shelly BLU H&T | SBHT-003C |
|
||||
| shellybluht | Shelly BLU H&T ZB | SBHT-203C |
|
||||
| shellybluht | Shelly BLU H&T Display ZB | SBHT-103C |
|
||||
| shellybludw | Shelly BLU Door/Windows | SBDW-002C |
|
||||
| shellybludw | Shelly BLU Door/Window ZB | SBDW-103C |
|
||||
| shellyblumotion | Shelly BLU Motion | SBMO-003Z |
|
||||
| shellyblumotion | Shelly BLU Motion ZB | SBMO-103Z |
|
||||
| shellybludistance | Shelly BLU Distance | SBDI-003E |
|
||||
| shellybluremote | Shelly BLU Remote Control | SBRC-005B |
|
||||
|
||||
|
||||
+9
-1
@@ -165,10 +165,14 @@ public class ShellyDevices {
|
||||
public static final String SHELLYDT_BLUTOUGHZB = "SBBT-102C";
|
||||
public static final String SHELLYDT_BLUWALLSWITCH4 = "SBBT-004CEU";
|
||||
public static final String SHELLYDT_BLURCBUTTON4 = "SBBT-004CUS";
|
||||
public static final String SHELLYDT_BLURCBUTTON4ZB = "SBBT-104CUS";
|
||||
public static final String SHELLYDT_BLUHT = "SBHT-003C";
|
||||
public static final String SHELLYDT_BLUHTZB = "SBHT-203C";
|
||||
public static final String SHELLYDT_BLUHTDISPLAYZB = "SBHT-103C";
|
||||
public static final String SHELLYDT_BLUDW = "SBDW-002C";
|
||||
public static final String SHELLYDT_BLUDWZB = "SBDW-103C";
|
||||
public static final String SHELLYDT_BLUMOTION = "SBMO-003Z";
|
||||
public static final String SHELLYDT_BLUMOTIONZB = "SBMO-103Z";
|
||||
public static final String SHELLYDT_BLUDISTANCE = "SBDI-003E";
|
||||
public static final String SHELLYDT_BLUREMOTE = "SBRC-005B";
|
||||
|
||||
@@ -492,10 +496,14 @@ public class ShellyDevices {
|
||||
Map.entry(SHELLYDT_BLUTOUGHZB, THING_TYPE_SHELLYBLUBUTTON1),
|
||||
Map.entry(SHELLYDT_BLUWALLSWITCH4, THING_TYPE_SHELLYBLUWALLSWITCH4),
|
||||
Map.entry(SHELLYDT_BLURCBUTTON4, THING_TYPE_SHELLYBLURCBUTTON4),
|
||||
Map.entry(SHELLYDT_BLURCBUTTON4ZB, THING_TYPE_SHELLYBLURCBUTTON4),
|
||||
Map.entry(SHELLYDT_BLUHT, THING_TYPE_SHELLYBLUHT), //
|
||||
Map.entry(SHELLYDT_BLUHTZB, THING_TYPE_SHELLYBLUHT), //
|
||||
Map.entry(SHELLYDT_BLUDW, THING_TYPE_SHELLYBLUDW),
|
||||
Map.entry(SHELLYDT_BLUHTDISPLAYZB, THING_TYPE_SHELLYBLUHT), //
|
||||
Map.entry(SHELLYDT_BLUDW, THING_TYPE_SHELLYBLUDW), //
|
||||
Map.entry(SHELLYDT_BLUDWZB, THING_TYPE_SHELLYBLUDW),
|
||||
Map.entry(SHELLYDT_BLUMOTION, THING_TYPE_SHELLYBLUMOTION),
|
||||
Map.entry(SHELLYDT_BLUMOTIONZB, THING_TYPE_SHELLYBLUMOTION),
|
||||
Map.entry(SHELLYDT_BLUDISTANCE, THING_TYPE_SHELLYBLUDISTANCE),
|
||||
Map.entry(SHELLYDT_BLUREMOTE, THING_TYPE_SHELLYBLUREMOTE),
|
||||
|
||||
|
||||
-2
@@ -67,7 +67,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final Shelly1CoapServer coapServer;
|
||||
private final ShellyThingTable thingTable;
|
||||
private final WebSocketClient webSocketClient;
|
||||
private final NetworkAddressService networkAddressService;
|
||||
private final ShellyBindingRuntimeConfig bindingConfig;
|
||||
|
||||
/**
|
||||
@@ -83,7 +82,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
|
||||
@Reference HttpClientFactory httpClientFactory, @Reference WebSocketFactory webSocketFactory,
|
||||
ComponentContext componentContext, Map<String, Object> configProperties) {
|
||||
super.activate(componentContext);
|
||||
this.networkAddressService = networkAddressService;
|
||||
this.messages = translationProvider;
|
||||
this.thingTable = thingTable;
|
||||
WebSocketClient client = Shelly2RpcSocket.createWebSocketClient(webSocketFactory, "shelly2api");
|
||||
|
||||
+1
@@ -1004,6 +1004,7 @@ public class Shelly1ApiJsonDTO {
|
||||
public static class ShellySensorBat {
|
||||
public Double value; // estimated remaining battery capacity in %
|
||||
public Double voltage; // battery voltage
|
||||
public @Nullable Boolean batteryLow; // device-reported low-battery flag (BLU ZB devices)
|
||||
}
|
||||
|
||||
// Door/Window sensor
|
||||
|
||||
+25
-3
@@ -49,7 +49,20 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link ShellyBluApi} implementsBLU interface
|
||||
* {@link ShellyBluApi} handles the Shelly BLU Bluetooth Low Energy device protocol.
|
||||
*
|
||||
* <p>
|
||||
* BLU devices (buttons, motion sensors, door/window sensors, H&T sensors) are
|
||||
* battery-powered and communicate via a Shelly Gen2/3/4 gateway. The gateway forwards
|
||||
* BTHome-encoded advertisements as {@code NotifyBluGW} WebSocket events to the hub, which
|
||||
* dispatches them to the individual per-device thing handlers via {@link ShellyThingTable}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Sensor data initialization follows a two-pass pattern: {@link #initializeSensorData} creates
|
||||
* the sub-objects (bat, lux, tmp, …) whenever the corresponding BTHome fields are present,
|
||||
* so the event-processing block can dereference them unconditionally.
|
||||
* </p>
|
||||
*
|
||||
* @author Markus Michels - Initial contribution
|
||||
* @author Udo Hartmann - Add support for decoding multi button inputs
|
||||
@@ -216,6 +229,9 @@ public class ShellyBluApi extends Shelly2ApiRpc {
|
||||
if (e.blu.battery != null) {
|
||||
sensorData.bat.value = (double) e.blu.battery;
|
||||
}
|
||||
if (e.blu.batteryLow != null) {
|
||||
sensorData.bat.batteryLow = e.blu.batteryLow == 1;
|
||||
}
|
||||
if (e.blu.rssi != null) {
|
||||
deviceStatus.wifiSta.rssi = e.blu.rssi;
|
||||
}
|
||||
@@ -227,6 +243,12 @@ public class ShellyBluApi extends Shelly2ApiRpc {
|
||||
sensorData.lux.isValid = true;
|
||||
sensorData.lux.value = (double) e.blu.illuminance;
|
||||
}
|
||||
if (e.blu.lightLevel != null) {
|
||||
sensorData.lux.isValid = true;
|
||||
int ll = getInteger(e.blu.lightLevel);
|
||||
sensorData.lux.illumination = ll == 0 ? "dark"
|
||||
: ll == 1 ? "twilight" : ll == 2 ? "bright" : "unknown";
|
||||
}
|
||||
if (e.blu.temperatures != null) {
|
||||
if (e.blu.temperatures.length == 1) {
|
||||
sensorData.tmp.units = SHELLY_TEMP_CELSIUS;
|
||||
@@ -316,10 +338,10 @@ public class ShellyBluApi extends Shelly2ApiRpc {
|
||||
}
|
||||
|
||||
private static void initializeSensorData(ShellyStatusSensor sensorData, Shelly2NotifyBluEventData data) {
|
||||
if (data.battery != null && sensorData.bat == null) {
|
||||
if ((data.battery != null || data.batteryLow != null) && sensorData.bat == null) {
|
||||
sensorData.bat = new ShellySensorBat();
|
||||
}
|
||||
if (data.illuminance != null && sensorData.lux == null) {
|
||||
if ((data.illuminance != null || data.lightLevel != null) && sensorData.lux == null) {
|
||||
sensorData.lux = new ShellySensorLux();
|
||||
}
|
||||
if (data.temperatures != null && sensorData.tmp == null) {
|
||||
|
||||
+38
-1
@@ -12,10 +12,18 @@
|
||||
*/
|
||||
package org.openhab.binding.shelly.internal.api2;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* {@link ShellyBluJsonDTO} includes contans and structures used for BluApi's JSON mapping and processing.
|
||||
* {@link ShellyBluJsonDTO} includes constants and structures used for BluApi's JSON mapping and processing.
|
||||
*
|
||||
* @author Markus Michels - Initial contribution
|
||||
*/
|
||||
@@ -42,6 +50,28 @@ public class ShellyBluJsonDTO {
|
||||
// "data":{"encryption":false,"BTHome_version":2,"pid":38,"Battery":100,"Illuminance":0,"Window":1,"Rotation":0,"addr":"bc:02:6e:c3:a6:c7","rssi":-62},
|
||||
// "ts":1682877414.25}
|
||||
|
||||
// Handles BTHome fields that a single-button device sends as a scalar but a multi-button device sends as an array.
|
||||
// Without this adapter, a plain Gson instance would throw JsonSyntaxException on scalar payloads for T[] fields.
|
||||
static class IntegerArrayAdapter implements JsonDeserializer<Integer[]> {
|
||||
@Override
|
||||
public Integer[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) {
|
||||
if (json.isJsonArray()) {
|
||||
return ctx.deserialize(json, Integer[].class);
|
||||
}
|
||||
return new Integer[] { json.getAsInt() };
|
||||
}
|
||||
}
|
||||
|
||||
static class DoubleArrayAdapter implements JsonDeserializer<Double[]> {
|
||||
@Override
|
||||
public Double[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) {
|
||||
if (json.isJsonArray()) {
|
||||
return ctx.deserialize(json, Double[].class);
|
||||
}
|
||||
return new Double[] { json.getAsDouble() };
|
||||
}
|
||||
}
|
||||
|
||||
public static class Shelly2NotifyBluEventData {
|
||||
public static class Shelly2NotifyBluEventDimmer {
|
||||
public Integer direction;
|
||||
@@ -57,6 +87,7 @@ public class ShellyBluJsonDTO {
|
||||
public Integer pid;
|
||||
@SerializedName("Battery")
|
||||
public Integer battery;
|
||||
@JsonAdapter(IntegerArrayAdapter.class)
|
||||
@SerializedName("Button")
|
||||
public Integer[] buttons;
|
||||
@SerializedName("Illuminance")
|
||||
@@ -65,6 +96,7 @@ public class ShellyBluJsonDTO {
|
||||
public Integer windowState;
|
||||
@SerializedName("Motion")
|
||||
public Integer motionState;
|
||||
@JsonAdapter(DoubleArrayAdapter.class)
|
||||
@SerializedName("Temperature")
|
||||
public Double[] temperatures;
|
||||
@SerializedName("Humidity")
|
||||
@@ -75,12 +107,17 @@ public class ShellyBluJsonDTO {
|
||||
public Double distance;
|
||||
@SerializedName("Channel") // BLU Remote
|
||||
public Integer channel;
|
||||
@JsonAdapter(DoubleArrayAdapter.class)
|
||||
@SerializedName("Rotation") // BLU Remote
|
||||
public Double[] rotations;
|
||||
@SerializedName("Dimmer") // BLU Remote
|
||||
public Shelly2NotifyBluEventDimmer dimmer;
|
||||
@SerializedName("Firmware32")
|
||||
public Long firmware32;
|
||||
@SerializedName("LightLevel") // BLU ZB: 0=dark, 1=twilight, 2=bright
|
||||
public @Nullable Integer lightLevel;
|
||||
@SerializedName("BatteryLow") // BLU H&T Display ZB: 1=battery below 15%
|
||||
public @Nullable Integer batteryLow;
|
||||
|
||||
public Integer rssi;
|
||||
@SerializedName("tx_power")
|
||||
|
||||
+13
-6
@@ -475,8 +475,10 @@ public class ShellyComponents {
|
||||
}
|
||||
if ((sdata.lux != null) && getBool(sdata.lux.isValid)) {
|
||||
// “lux”:{“value”:30, “illumination”: “dark”, “is_valid”:true},
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_LUX,
|
||||
toQuantityType(getDouble(sdata.lux.value), DIGITS_LUX, Units.LUX));
|
||||
if (sdata.lux.value != null) {
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_LUX,
|
||||
toQuantityType(getDouble(sdata.lux.value), DIGITS_LUX, Units.LUX));
|
||||
}
|
||||
if (sdata.lux.illumination != null) {
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ILLUM,
|
||||
getStringType(sdata.lux.illumination));
|
||||
@@ -551,14 +553,19 @@ public class ShellyComponents {
|
||||
getOnOff(charger));
|
||||
}
|
||||
if (sdata.bat != null) { // no update for Sense
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL,
|
||||
toQuantityType(getDouble(sdata.bat.value), 0, Units.PERCENT));
|
||||
if (sdata.bat.value != null) {
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL,
|
||||
toQuantityType(getDouble(sdata.bat.value), 0, Units.PERCENT));
|
||||
}
|
||||
|
||||
int lowBattery = thingHandler.getThingConfig().getLowBattery();
|
||||
Boolean batteryLowFlag = sdata.bat.batteryLow;
|
||||
boolean isLow = batteryLowFlag != null ? batteryLowFlag.booleanValue()
|
||||
: (sdata.bat.value != null && !charger && getDouble(sdata.bat.value) < lowBattery);
|
||||
boolean changed = thingHandler.updateChannel(CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LOW,
|
||||
getOnOff(!charger && getDouble(sdata.bat.value) < lowBattery));
|
||||
getOnOff(isLow));
|
||||
updated |= changed;
|
||||
if (!charger && changed && getDouble(sdata.bat.value) < lowBattery) {
|
||||
if (changed && isLow) {
|
||||
thingHandler.postEvent(ALARM_TYPE_LOW_BATTERY, false);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -602,7 +602,8 @@ public class ShellyChannelDefinitions {
|
||||
addChannel(thing, newChannels, sdata.tmp != null || sdata.thermostats != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_TEMP);
|
||||
addChannel(thing, newChannels, sdata.hum != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM);
|
||||
addChannel(thing, newChannels, sdata.lux != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_LUX);
|
||||
addChannel(thing, newChannels, sdata.lux != null && sdata.lux.value != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_LUX);
|
||||
addChannel(thing, newChannels, sdata.lux != null && sdata.lux.illumination != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_ILLUM);
|
||||
addChannel(thing, newChannels, sdata.flood != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
|
||||
|
||||
@@ -31,7 +31,6 @@ let uint32 = 6;
|
||||
let int32 = 7;
|
||||
|
||||
let BTH_DIMMERSTEPS_INDEX = 0x3c; // Dimmer (Wheel) Steps object ID
|
||||
let FORCE_ARRAY_VALUES = ["Temperature", "Button", "Rotation"];
|
||||
|
||||
// BTHome object definitions: id => {name, type, optional scale factor}
|
||||
// https://bthome.io/format/
|
||||
@@ -53,7 +52,7 @@ BTH[0x11] = { n: "Opening", t: uint8 }; //
|
||||
BTH[0x12] = { n: "Co2", t: uint16 }; // CO2 concentration ppm
|
||||
BTH[0x13] = { n: "TVOC", t: uint16 }; // TVOC Air Quality ug/m3
|
||||
BTH[0x14] = { n: "Moisture16", t: uint16, f: 0.01 }; // Moisture (scaled by 0.01)
|
||||
BTH[0x15] = { n: "Battery", t: uint8 }; // Battery level normal/low status (boolean)
|
||||
BTH[0x15] = { n: "BatteryLow", t: uint8 }; // Battery low flag: 0=normal, 1=low
|
||||
BTH[0x16] = { n: "BatteryCharging", t: uint8 }; // Battery charging status (boolean)
|
||||
BTH[0x17] = { n: "CarbonMonoxide", t: uint8 }; // Carbon Monoxide not detected/detected status (boolean)
|
||||
BTH[0x18] = { n: "Cold", t: uint8 }; // Cold normal/cold status (boolean)
|
||||
@@ -115,6 +114,7 @@ BTH[0x5d] = { n: "Current", t: int16, f: 0.001 }; //
|
||||
BTH[0x5e] = { n: "Direction", t: uint16, f: 0.01 }; // Direction (scaled by 0.01)
|
||||
BTH[0x5f] = { n: "Precipitation", t: uint16, f: 0.1 }; // Precipitation (scaled by 0.1)
|
||||
BTH[0x60] = { n: "Channel", t: uint8 }; // Channel
|
||||
BTH[0x64] = { n: "LightLevel", t: uint8 }; // Light level 0=dark, 1=twilight, 2=bright
|
||||
BTH[0xF0] = { n: "DeviceId", t: uint16}; // Device type ID
|
||||
BTH[0xF1] = { n: "Firmware32", t: uint32}; // Firmware version in 1.2.3.4 format
|
||||
BTH[0xF2] = { n: "Firmware24", t: uint24}; // Firmware version in 1.2.3 format
|
||||
@@ -251,14 +251,6 @@ let BTHomeDecoder = {
|
||||
// Add events as arrays to the result
|
||||
if (dimmer.length > 0) result["Dimmer"] = dimmer;
|
||||
|
||||
// Special handling for values, which need to be converted to an array
|
||||
for (let i = 0; i < FORCE_ARRAY_VALUES.length; i++) {
|
||||
let key = FORCE_ARRAY_VALUES[i];
|
||||
if (typeof result[key] !== "undefined" && !Array.isArray(result[key])) {
|
||||
result[key] = [result[key]];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.shelly.internal.api2;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.openhab.binding.shelly.internal.ShellyDevices.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.shelly.internal.api2.ShellyBluJsonDTO.Shelly2NotifyBluEventData;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* Tests for BLU ZB device support: model registration, BTHome field deserialization,
|
||||
* and scalar/array adapter handling.
|
||||
*
|
||||
* @author Markus Michels - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ShellyBluZbTest {
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
@Test
|
||||
void bluRcButton4ZbMapsToRcButton4ThingType() {
|
||||
ThingTypeUID uid = THING_TYPE_BY_DEVICE_TYPE.get(SHELLYDT_BLURCBUTTON4ZB);
|
||||
assertNotNull(uid);
|
||||
assertThat(uid, is(THING_TYPE_SHELLYBLURCBUTTON4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void bluMotionZbMapsToMotionThingType() {
|
||||
ThingTypeUID uid = THING_TYPE_BY_DEVICE_TYPE.get(SHELLYDT_BLUMOTIONZB);
|
||||
assertNotNull(uid);
|
||||
assertThat(uid, is(THING_TYPE_SHELLYBLUMOTION));
|
||||
}
|
||||
|
||||
@Test
|
||||
void bluDwZbMapsToDwThingType() {
|
||||
ThingTypeUID uid = THING_TYPE_BY_DEVICE_TYPE.get(SHELLYDT_BLUDWZB);
|
||||
assertNotNull(uid);
|
||||
assertThat(uid, is(THING_TYPE_SHELLYBLUDW));
|
||||
}
|
||||
|
||||
@Test
|
||||
void bluHtDisplayZbMapsToHtThingType() {
|
||||
ThingTypeUID uid = THING_TYPE_BY_DEVICE_TYPE.get(SHELLYDT_BLUHTDISPLAYZB);
|
||||
assertNotNull(uid);
|
||||
assertThat(uid, is(THING_TYPE_SHELLYBLUHT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightLevelBrightDeserializes() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"LightLevel\":2,\"Battery\":85}",
|
||||
Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
assertThat(data.lightLevel, is(2));
|
||||
assertThat(data.battery, is(85));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightLevelDarkDeserializes() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"LightLevel\":0}", Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
assertThat(data.lightLevel, is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batteryLowSetDeserializes() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"Battery\":10,\"BatteryLow\":1}",
|
||||
Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
assertThat(data.battery, is(10));
|
||||
assertThat(data.batteryLow, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void batteryLowClearedDeserializes() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"Battery\":80,\"BatteryLow\":0}",
|
||||
Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
assertThat(data.batteryLow, is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void scalarButtonDeserializesToSingleElementArray() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"Button\":1}", Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
Integer[] buttons = data.buttons;
|
||||
assertNotNull(buttons);
|
||||
assertThat(buttons.length, is(1));
|
||||
assertThat(buttons[0], is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayButtonDeserializesToMultiElementArray() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"Button\":[1,0,1,0]}", Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
Integer[] buttons = data.buttons;
|
||||
assertNotNull(buttons);
|
||||
assertThat(buttons.length, is(4));
|
||||
assertThat(buttons[0], is(1));
|
||||
assertThat(buttons[2], is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void scalarTemperatureDeserializesToSingleElementArray() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"Temperature\":21.5}", Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
Double[] temperatures = data.temperatures;
|
||||
assertNotNull(temperatures);
|
||||
assertThat(temperatures.length, is(1));
|
||||
assertThat(temperatures[0], is(21.5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayTemperatureDeserializesToMultiElementArray() {
|
||||
Shelly2NotifyBluEventData data = gson.fromJson("{\"Temperature\":[21.5,22.0]}",
|
||||
Shelly2NotifyBluEventData.class);
|
||||
assertNotNull(data);
|
||||
Double[] temperatures = data.temperatures;
|
||||
assertNotNull(temperatures);
|
||||
assertThat(temperatures.length, is(2));
|
||||
assertThat(temperatures[0], is(21.5));
|
||||
assertThat(temperatures[1], is(22.0));
|
||||
}
|
||||
}
|
||||
+4
@@ -264,10 +264,14 @@ public class ShellyThingCreatorTest {
|
||||
Arguments.of(SHELLYDT_BLUTOUGHZB, "", THING_TYPE_SHELLYBLUBUTTON1), //
|
||||
Arguments.of(SHELLYDT_BLUWALLSWITCH4, "", THING_TYPE_SHELLYBLUWALLSWITCH4), //
|
||||
Arguments.of(SHELLYDT_BLURCBUTTON4, "", THING_TYPE_SHELLYBLURCBUTTON4), //
|
||||
Arguments.of(SHELLYDT_BLURCBUTTON4ZB, "", THING_TYPE_SHELLYBLURCBUTTON4), //
|
||||
Arguments.of(SHELLYDT_BLUHT, "", THING_TYPE_SHELLYBLUHT), //
|
||||
Arguments.of(SHELLYDT_BLUHTZB, "", THING_TYPE_SHELLYBLUHT), //
|
||||
Arguments.of(SHELLYDT_BLUHTDISPLAYZB, "", THING_TYPE_SHELLYBLUHT), //
|
||||
Arguments.of(SHELLYDT_BLUDW, "", THING_TYPE_SHELLYBLUDW), //
|
||||
Arguments.of(SHELLYDT_BLUDWZB, "", THING_TYPE_SHELLYBLUDW), //
|
||||
Arguments.of(SHELLYDT_BLUMOTION, "", THING_TYPE_SHELLYBLUMOTION), //
|
||||
Arguments.of(SHELLYDT_BLUMOTIONZB, "", THING_TYPE_SHELLYBLUMOTION), //
|
||||
Arguments.of(SHELLYDT_BLUDISTANCE, "", THING_TYPE_SHELLYBLUDISTANCE), //
|
||||
Arguments.of(SHELLYDT_BLUREMOTE, "", THING_TYPE_SHELLYBLUREMOTE), //
|
||||
|
||||
|
||||
Reference in New Issue
Block a user