From c3f37b5476683f0e71b364766f25b3faec38e02a Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 29 Nov 2021 12:34:09 +0100 Subject: [PATCH] [miio] add support for BT Gateway switch on chuangmi.plug.212a01 (#11657) * [miio] add support for BT Gateway switch on chuangmi.plug.212a01 Signed-off-by: Marcel Verpaalen * [miio] improve conversion & add test for it Signed-off-by: Marcel Verpaalen * [miio] add one empty string test Signed-off-by: Marcel Verpaalen * [miio] remove unnessesary exceptions Signed-off-by: Marcel Verpaalen * [miio] add one more test for different inputs Signed-off-by: Marcel Verpaalen * [miio] typo Signed-off-by: Marcel Verpaalen --- bundles/org.openhab.binding.miio/README.md | 4 +- .../miio/internal/basic/Conversions.java | 29 +++++ .../database/chuangmi.plug.212a01-miot.json | 51 +++++++- .../miio/internal/ConversionsTest.java | 123 ++++++++++++++++++ 4 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ConversionsTest.java diff --git a/bundles/org.openhab.binding.miio/README.md b/bundles/org.openhab.binding.miio/README.md index 66f58a5329c1..59695ffb2719 100644 --- a/bundles/org.openhab.binding.miio/README.md +++ b/bundles/org.openhab.binding.miio/README.md @@ -187,7 +187,7 @@ Currently the miio binding supports more than 300 different models. | Mi Multifunction Air Monitor | miio:basic | [cgllc.airmonitor.b1](#cgllc-airmonitor-b1) | Yes | | | Qingping Air Monitor | miio:basic | [cgllc.airmonitor.s1](#cgllc-airmonitor-s1) | Yes | | | Mi Universal Remote | miio:unsupported | chuangmi.ir.v2 | No | | -| Mi Smart Power Plug 2 (Wi-Fi and Bluetooth Gateway) | miio:basic | [chuangmi.plug.212a01](#chuangmi-plug-212a01) | Yes | Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Mi Smart Power Plug 2 (Wi-Fi and Bluetooth Gateway) | miio:basic | [chuangmi.plug.212a01](#chuangmi-plug-212a01) | Yes | | | Mi Smart Plug WiFi | miio:basic | [chuangmi.plug.hmi205](#chuangmi-plug-hmi205) | Yes | | | Mi Smart Plug (WiFi) | miio:basic | [chuangmi.plug.hmi206](#chuangmi-plug-hmi206) | Yes | | | Mi Smart Wi-Fi Plug (Bluetooth Gateway) | miio:basic | [chuangmi.plug.hmi208](#chuangmi-plug-hmi208) | Yes | | @@ -694,6 +694,7 @@ Note, not all the values need to be in the json file, e.g. a subset of the param | countdown | Number:Time | Imilab Timer - Countdown | | | task-switch | Switch | Imilab Timer - Task Switch | | | countdown-info | Switch | Imilab Timer - Countdown Info | | +| bt-gw | String | BT Gateway | Value mapping `["disable"="Disable","enable"="Enable"]` | ### Mi Smart Plug WiFi (chuangmi.plug.hmi205) Channels @@ -5488,6 +5489,7 @@ Number:Time off_duration "Imilab Timer - Off Duration" (G_plug) {channel="miio:b Number:Time countdown "Imilab Timer - Countdown" (G_plug) {channel="miio:basic:plug:countdown"} Switch task_switch "Imilab Timer - Task Switch" (G_plug) {channel="miio:basic:plug:task-switch"} Switch countdown_info "Imilab Timer - Countdown Info" (G_plug) {channel="miio:basic:plug:countdown-info"} +String bt_gw "BT Gateway" (G_plug) {channel="miio:basic:plug:bt-gw"} ``` ### Mi Smart Plug WiFi (chuangmi.plug.hmi205) item file lines diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/Conversions.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/Conversions.java index ba79c31cac9f..e20487d09a0b 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/Conversions.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/Conversions.java @@ -23,6 +23,9 @@ import org.slf4j.LoggerFactory; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; /** @@ -121,9 +124,35 @@ public static JsonElement tankLevel(JsonElement value12) throws ClassCastExcepti } } + public static JsonElement getJsonElement(String element, JsonElement responseValue) { + try { + if (responseValue.isJsonPrimitive() || responseValue.isJsonObject()) { + JsonElement jsonElement = responseValue.isJsonObject() ? responseValue + : JsonParser.parseString(responseValue.getAsString()); + if (jsonElement.isJsonObject()) { + JsonObject value = jsonElement.getAsJsonObject(); + if (value.has(element)) { + return value.get(element); + } + } + } + } catch (JsonParseException e) { + // ignore + } + LOGGER.debug("JsonElement '{}' not found in '{}'", element, responseValue); + return responseValue; + } + public static JsonElement execute(String transformation, JsonElement value, @Nullable Map deviceVariables) { try { + if (transformation.toUpperCase().startsWith("GETJSONELEMENT")) { + if (transformation.length() > 15) { + return getJsonElement(transformation.substring(15), value); + } else { + LOGGER.info("Transformation {} missing element. Returning '{}'", transformation, value.toString()); + } + } switch (transformation.toUpperCase()) { case "YEELIGHTSCENEID": return yeelightSceneConversion(value); diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json b/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json index 4827ef6cd6df..9ee47e5e69f3 100644 --- a/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/chuangmi.plug.212a01-miot.json @@ -274,8 +274,57 @@ }, "refresh": true, "actions": [] + }, + { + "property": "", + "friendlyName": "BT Gateway", + "channel": "bt-gw", + "type": "String", + "stateDescription": { + "readOnly": false, + "options": [ + { + "value": "disable", + "label": "Disable" + }, + { + "value": "enable", + "label": "Enable" + } + ] + }, + "refresh": true, + "customRefreshCommand": "bt_gateway_status", + "transformation": "getJsonElement-gateway_status", + "actions": [ + { + "command": "bt_gateway_enable", + "parameterType": "EMPTY", + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "enable" + } + ] + } + }, + { + "command": "bt_gateway_disable", + "parameterType": "EMPTY", + "condition": { + "name": "matchValue", + "parameters": [ + { + "matchValue": "disable" + } + ] + } + } + ], + "readmeComment": "Value mapping `[\"disable\"\u003d\"Disable\",\"enable\"\u003d\"Enable\"]`" } ], - "experimental": true + "experimental": false } } diff --git a/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ConversionsTest.java b/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ConversionsTest.java new file mode 100644 index 000000000000..2ee46a065f5d --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/test/java/org/openhab/binding/miio/internal/ConversionsTest.java @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2010-2021 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.miio.internal; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.miio.internal.basic.Conversions; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +/** + * Test case for {@link ConversionsTest} + * + * @author Marcel Verpaalen - Initial contribution + * + */ +@NonNullByDefault +public class ConversionsTest { + + @Test + public void getJsonElementTest() { + + Map deviceVariables = Collections.emptyMap(); + + // test invalid missing element + String transformation = "getJsonElement"; + JsonElement value = new JsonPrimitive(""); + JsonElement resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(value, resp); + + // test invalid missing element + value = new JsonPrimitive("{\"test\": \"testresponse\"}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(value, resp); + + transformation = "getJsonElement-test"; + + // test without deviceVariables + resp = Conversions.execute(transformation, value, null); + assertNotNull(resp); + assertEquals(new JsonPrimitive("testresponse"), resp); + + // test non json + value = new JsonPrimitive("some non json value"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(value, resp); + + // test non json empty string + value = new JsonPrimitive(""); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(value, resp); + + // test input as jsonString + value = new JsonPrimitive("{\"test\": \"testresponse\"}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(new JsonPrimitive("testresponse"), resp); + + // test input as jsonObject + value = JsonParser.parseString("{\"test\": \"testresponse\"}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(new JsonPrimitive("testresponse"), resp); + + // test input as jsonString for a number + value = new JsonPrimitive("{\"test\": 3}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(new JsonPrimitive(3), resp); + + // test input as jsonString for a array + value = new JsonPrimitive("{\"test\": []}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(new JsonArray(), resp); + + // test input as jsonString for a boolean + value = new JsonPrimitive("{\"test\": false}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(new JsonPrimitive(false), resp); + + // test input as jsonObject for a number + value = JsonParser.parseString("{\"test\": 3}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(new JsonPrimitive(3), resp); + + // test input as jsonString for non-existing element + value = new JsonPrimitive("{\"nottest\": \"testresponse\"}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(value, resp); + + // test input as jsonString for non-existing element + value = JsonParser.parseString("{\"nottest\": \"testresponse\"}"); + resp = Conversions.execute(transformation, value, deviceVariables); + assertNotNull(resp); + assertEquals(value, resp); + } +}