diff --git a/bundles/org.openhab.binding.miio/README.md b/bundles/org.openhab.binding.miio/README.md
index 66f58a5329c13..59695ffb27196 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 ba79c31cac9fc..e20487d09a0b8 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 4827ef6cd6df4..9ee47e5e69f3e 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 0000000000000..2ee46a065f5d4
--- /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);
+ }
+}