From 809e53d3aff3383a81bed8dd49a50b61620303f5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 21 Jun 2021 22:07:17 +0200 Subject: [PATCH] [miio] Add support gateway lumi.gateway.mieu01 (#10893) Signed-off-by: Marcel Verpaalen --- bundles/org.openhab.binding.miio/README.md | 48 +++- .../binding/miio/internal/MiIoDevices.java | 3 +- .../miio/internal/basic/ActionConditions.java | 20 ++ .../miio/internal/basic/Conversions.java | 23 ++ .../internal/handler/MiIoBasicHandler.java | 34 ++- .../database/lumi.gateway.mieu01.json | 229 ++++++++++++++++++ 6 files changed, 346 insertions(+), 11 deletions(-) create mode 100644 bundles/org.openhab.binding.miio/src/main/resources/database/lumi.gateway.mieu01.json diff --git a/bundles/org.openhab.binding.miio/README.md b/bundles/org.openhab.binding.miio/README.md index 36fde80df..13af3ae71 100644 --- a/bundles/org.openhab.binding.miio/README.md +++ b/bundles/org.openhab.binding.miio/README.md @@ -244,7 +244,8 @@ Currently the miio binding supports more than 290 different models. | Mi Air Purifier virtual | miio:basic | [lumi.gateway.mgl03](#lumi-gateway-mgl03) | Yes | Used to control the gateway itself. Use the mihome binding to control devices connected to the Xiaomi gateway.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | Mi smart Home Gateway Hub v1 | miio:basic | [lumi.gateway.v1](#lumi-gateway-v1) | Yes | Used to control the gateway itself. Use the mihome binding to control devices connected to the Xiaomi gateway.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | | Mi smart Home GatewayHub v2 | miio:basic | [lumi.gateway.v2](#lumi-gateway-v2) | Yes | Used to control the gateway itself. Use the mihome binding to control devices connected to the Xiaomi gateway.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | -| Mi mart Home Gateway Hub v3 | miio:basic | [lumi.gateway.v3](#lumi-gateway-v3) | Yes | Used to control the gateway itself. Use the mihome binding to control devices connected to the Xiaomi gateway.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Mi smart Home Gateway Hub v3 | miio:basic | [lumi.gateway.v3](#lumi-gateway-v3) | Yes | Used to control the gateway itself. Use the mihome binding to control devices connected to the Xiaomi gateway.
Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses | +| Mi smart Home Gateway Hub | miio:basic | [lumi.gateway.mieu01](#lumi-gateway-mieu01) | Yes | Used to control the gateway itself. Controlling child devices currently only possible via rules | | Midea AC-i Youth | miio:unsupported | midea.aircondition.v1 | No | | | Midea Air Conditioner v2 | miio:unsupported | midea.aircondition.v2 | No | | | Midea AC-Cool Golden | miio:unsupported | midea.aircondition.xa1 | No | | @@ -1333,7 +1334,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | alarmingVol | Number | Alarming Volume | | | doorbellPush | String | Doorbell Push | | -### Mi mart Home Gateway Hub v3 (lumi.gateway.v3) Channels +### Mi smart Home Gateway Hub v3 (lumi.gateway.v3) Channels | Channel | Type | Description | Comment | |----------------------|----------------------|------------------------------------------|------------| @@ -1343,6 +1344,24 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl | alarmingVol | Number | Alarming Volume | | | doorbellPush | String | Doorbell Push | | +### Mi smart Home Gateway Hub (lumi.gateway.mieu01) Channels + +| Channel | Type | Description | Comment | +|----------------------|----------------------|------------------------------------------|------------| +| guard | Switch | Guard | | +| corridor | Switch | Automatic Night Light | | +| nightlight | Color | Night Light | | +| rgb | Color | Colored Light | | +| doorbell_volume | Number | Doorbell Volume | | +| alarming_volume | Number | Alarming Volume | | +| gateway_volume | Number | Gateway Volume | | +| arming_time | Number:Time | Arming Time | | +| corridor_on_time | Number:Time | Corridor on time | | +| language | String | Voice prompt Language | | +| zigbee_channel | String | Zigbee Channel | | +| lumi_bind | String | Lumi_bind info | | +| doorbell_push | String | Doorbell Push | | + ### Mi Robot Vacuum-Mop Essential (mijia.vacuum.v2) Channels | Channel | Type | Description | Comment | @@ -6058,12 +6077,12 @@ Number alarmingVol "Alarming Volume" (G_gateway) {channel="miio:basic:gateway:al String doorbellPush "Doorbell Push" (G_gateway) {channel="miio:basic:gateway:doorbellPush"} ``` -### Mi mart Home Gateway Hub v3 (lumi.gateway.v3) item file lines +### Mi smart Home Gateway Hub v3 (lumi.gateway.v3) item file lines note: Autogenerated example. Replace the id (gateway) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. ``` -Group G_gateway "Mi mart Home Gateway Hub v3" +Group G_gateway "Mi smart Home Gateway Hub v3" Switch telnetEnable "Enable Telnet" (G_gateway) {channel="miio:basic:gateway:telnetEnable"} Number doorbellVol "Doorbell Volume" (G_gateway) {channel="miio:basic:gateway:doorbellVol"} Number gatewayVol "Gateway Volume" (G_gateway) {channel="miio:basic:gateway:gatewayVol"} @@ -6071,6 +6090,27 @@ Number alarmingVol "Alarming Volume" (G_gateway) {channel="miio:basic:gateway:al String doorbellPush "Doorbell Push" (G_gateway) {channel="miio:basic:gateway:doorbellPush"} ``` +### Mi smart Home Gateway Hub (lumi.gateway.mieu01) item file lines + +note: Autogenerated example. Replace the id (gateway) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. + +``` +Group G_gateway "Mi smart Home Gateway Hub" +Switch guard "Guard" (G_gateway) {channel="miio:basic:gateway:guard"} +Switch corridor "Automatic Night Light" (G_gateway) {channel="miio:basic:gateway:corridor"} +Color nightlight "Night Light" (G_gateway) {channel="miio:basic:gateway:nightlight"} +Color rgb "Colored Light" (G_gateway) {channel="miio:basic:gateway:rgb"} +Number doorbell_volume "Doorbell Volume" (G_gateway) {channel="miio:basic:gateway:doorbell_volume"} +Number alarming_volume "Alarming Volume" (G_gateway) {channel="miio:basic:gateway:alarming_volume"} +Number gateway_volume "Gateway Volume" (G_gateway) {channel="miio:basic:gateway:gateway_volume"} +Number:Time arming_time "Arming Time" (G_gateway) {channel="miio:basic:gateway:arming_time"} +Number:Time corridor_on_time "Corridor on time" (G_gateway) {channel="miio:basic:gateway:corridor_on_time"} +String language "Voice prompt Language" (G_gateway) {channel="miio:basic:gateway:language"} +String zigbee_channel "Zigbee Channel" (G_gateway) {channel="miio:basic:gateway:zigbee_channel"} +String lumi_bind "Lumi_bind info" (G_gateway) {channel="miio:basic:gateway:lumi_bind"} +String doorbell_push "Doorbell Push" (G_gateway) {channel="miio:basic:gateway:doorbell_push"} +``` + ### Mi Robot Vacuum-Mop Essential (mijia.vacuum.v2) item file lines note: Autogenerated example. Replace the id (vacuum) in the channel with your own. Replace `basic` with `generic` in the thing UID depending on how your thing was discovered. diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java index 7c26ec915..0e3988b81 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/MiIoDevices.java @@ -94,7 +94,8 @@ public enum MiIoDevices { LUMI_GATEWAY_MGL03("lumi.gateway.mgl03", "Mi Air Purifier virtual", THING_TYPE_BASIC), LUMI_GATEWAY_V1("lumi.gateway.v1", "Mi smart Home Gateway Hub v1", THING_TYPE_BASIC), LUMI_GATEWAY_V2("lumi.gateway.v2", "Mi smart Home GatewayHub v2", THING_TYPE_BASIC), - LUMI_GATEWAY_V3("lumi.gateway.v3", "Mi mart Home Gateway Hub v3", THING_TYPE_BASIC), + LUMI_GATEWAY_V3("lumi.gateway.v3", "Mi smart Home Gateway Hub v3", THING_TYPE_BASIC), + LUMI_GATEWAY_MIEU01("lumi.gateway.mieu01", "Mi smart Home Gateway Hub", THING_TYPE_BASIC), MIDEA_AIRCONDITION_V1("midea.aircondition.v1", "Midea AC-i Youth", THING_TYPE_UNSUPPORTED), MIDEA_AIRCONDITION_V2("midea.aircondition.v2", "Midea Air Conditioner v2", THING_TYPE_UNSUPPORTED), MIDEA_AIRCONDITION_XA1("midea.aircondition.xa1", "Midea AC-Cool Golden", THING_TYPE_UNSUPPORTED), diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/ActionConditions.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/ActionConditions.java index 513123299..0b4cb553e 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/ActionConditions.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/basic/ActionConditions.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.miio.internal.basic; +import java.awt.Color; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -69,6 +70,23 @@ public class ActionConditions { return value; } + /** + * Convert HSV value to RGB+Brightness + * + * @param value + * @return RGB value + brightness as first byte + */ + private static @Nullable JsonElement HsvToBRGB(@Nullable Command command, @Nullable JsonElement value) { + if (command != null && command instanceof HSBType) { + HSBType hsb = (HSBType) command; + Color color = Color.getHSBColor(hsb.getHue().floatValue() / 360, hsb.getSaturation().floatValue() / 100, + hsb.getBrightness().floatValue() / 100); + return new JsonPrimitive((hsb.getBrightness().byteValue() << 24) + (color.getRed() << 16) + + (color.getGreen() << 8) + color.getBlue()); + } + return null; + } + /** * Check if the value is a valid brightness between 1-100 which can be send to brightness channel. * If not returns a null @@ -152,6 +170,8 @@ public class ActionConditions { return firmwareCheck(condition, deviceVariables, value); case "BRIGHTNESSEXISTING": return brightnessExists(value); + case "HSVTOBRGB": + return HsvToBRGB(command, value); case "BRIGHTNESSONOFF": return brightness(value); case "HSBONLY": 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 9812a38b7..60f07bcb7 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 @@ -12,7 +12,11 @@ */ package org.openhab.binding.miio.internal.basic; +import java.awt.Color; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.PercentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +32,23 @@ import com.google.gson.JsonPrimitive; public class Conversions { private static final Logger LOGGER = LoggerFactory.getLogger(Conversions.class); + /** + * Converts a RGB+brightness input to a HSV value. + * * + * + * @param RGB + brightness value (note brightness in the first byte) + * @return HSV + */ + public static JsonElement bRGBtoHSV(JsonElement bRGB) throws ClassCastException { + if (bRGB.isJsonPrimitive() && bRGB.getAsJsonPrimitive().isNumber()) { + Color rgb = new Color(bRGB.getAsInt()); + HSBType hsb = HSBType.fromRGB(rgb.getRed(), rgb.getGreen(), rgb.getBlue()); + hsb = new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(bRGB.getAsInt() >>> 24)); + return new JsonPrimitive(hsb.toFullString()); + } + return bRGB; + } + public static JsonElement secondsToHours(JsonElement seconds) throws ClassCastException { double value = seconds.getAsDouble() / 3600; return new JsonPrimitive(value); @@ -86,6 +107,8 @@ public class Conversions { return divideHundred(value); case "TANKLEVEL": return tankLevel(value); + case "BRGBTOHSV": + return bRGBtoHSV(value); default: LOGGER.debug("Transformation {} not found. Returning '{}'", transfortmation, value.toString()); return value; diff --git a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java index 72e5bdc58..394ef0414 100644 --- a/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java +++ b/bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoBasicHandler.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -322,6 +323,11 @@ public class MiIoBasicHandler extends MiIoAbstractHandler { private void refreshCustomProperties(MiIoBasicDevice midevice) { for (MiIoBasicChannel miChannel : refreshListCustomCommands.values()) { + if (!isLinked(miChannel.getChannel())) { + logger.debug("Skip refresh of channel {} for {} as it is not linked", miChannel.getChannel(), + getThing().getUID()); + continue; + } sendCommand(miChannel.getChannelCustomRefreshCommand()); } } @@ -380,6 +386,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler { } if (hasChannelStructure) { refreshList = new ArrayList<>(); + refreshListCustomCommands = new HashMap<>(); final MiIoBasicDevice miioDevice = this.miioDevice; if (miioDevice != null) { for (MiIoBasicChannel miChannel : miioDevice.getDevice().getChannels()) { @@ -475,7 +482,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler { ChannelTypeUID channelTypeUID = new ChannelTypeUID(miChannel.getChannelType()); if (channelTypeRegistry.getChannelType(channelTypeUID) != null) { newChannel = newChannel.withType(channelTypeUID); - final Set tags = miChannel.getTags(); + final LinkedHashSet tags = miChannel.getTags(); if (tags != null && !tags.isEmpty()) { newChannel.withDefaultTags(tags); } @@ -575,7 +582,11 @@ public class MiIoBasicHandler extends MiIoAbstractHandler { updateState(basicChannel.getChannel(), new PercentType(val.getAsBigDecimal())); break; case "string": - updateState(basicChannel.getChannel(), new StringType(val.getAsString())); + if (val.isJsonPrimitive()) { + updateState(basicChannel.getChannel(), new StringType(val.getAsString())); + } else { + updateState(basicChannel.getChannel(), new StringType(val.toString())); + } break; case "switch": if (val.getAsJsonPrimitive().isNumber()) { @@ -587,9 +598,19 @@ public class MiIoBasicHandler extends MiIoAbstractHandler { } break; case "color": - Color rgb = new Color(val.getAsInt()); - HSBType hsb = HSBType.fromRGB(rgb.getRed(), rgb.getGreen(), rgb.getBlue()); - updateState(basicChannel.getChannel(), hsb); + if (val.isJsonPrimitive() && val.getAsJsonPrimitive().isNumber()) { + Color rgb = new Color(val.getAsInt()); + HSBType hsb = HSBType.fromRGB(rgb.getRed(), rgb.getGreen(), rgb.getBlue()); + updateState(basicChannel.getChannel(), hsb); + } else { + try { + HSBType hsb = HSBType.valueOf(val.getAsString().replace("[", "").replace("]", "")); + updateState(basicChannel.getChannel(), hsb); + } catch (IllegalArgumentException e) { + logger.debug("Failed updating channel '{}'. Could not convert '{}' to color", + basicChannel.getChannel(), val.getAsString()); + } + } break; default: logger.debug("No update logic for channeltype '{}' ", basicChannel.getType()); @@ -658,7 +679,8 @@ public class MiIoBasicHandler extends MiIoAbstractHandler { break; default: if (refreshListCustomCommands.containsKey(response.getMethod())) { - logger.debug("Processing custom refresh command response for !{}", response.getMethod()); + logger.debug("Processing custom refresh command response for '{}' - {}", response.getMethod(), + response.getResult()); final MiIoBasicChannel ch = refreshListCustomCommands.get(response.getMethod()); if (ch != null) { if (response.getResult().isJsonArray()) { diff --git a/bundles/org.openhab.binding.miio/src/main/resources/database/lumi.gateway.mieu01.json b/bundles/org.openhab.binding.miio/src/main/resources/database/lumi.gateway.mieu01.json new file mode 100644 index 000000000..308b02678 --- /dev/null +++ b/bundles/org.openhab.binding.miio/src/main/resources/database/lumi.gateway.mieu01.json @@ -0,0 +1,229 @@ +{ + "deviceMapping": { + "id": [ + "lumi.gateway.mieu01" + ], + "propertyMethod": "get_prop", + "maxProperties": 4, + "channels": [ + { + "property": "get_arming", + "friendlyName": "Guard", + "channel": "guard", + "type": "Switch", + "refresh": true, + "customRefreshCommand": "get_arming", + "actions": [ + { + "command": "set_arming", + "parameterType": "ONOFF" + } + ], + "category": "alarm", + "tags": [ + "Alarm" + ] + }, + { + "property": "corridor_light", + "friendlyName": "Automatic Night Light", + "channel": "corridor", + "type": "Switch", + "refresh": true, + "actions": [ + { + "command": "set_corridor_light", + "parameterType": "ONOFF" + } + ], + "category": "light", + "tags": [ + "Switch" + ] + }, + { + "property": "night_light_rgb", + "friendlyName": "Night Light", + "channel": "nightlight", + "type": "Color", + "refresh": true, + "transformation": "bRGBtoHSV", + "actions": [ + { + "command": "set_night_light_rgb", + "parameterType": "COLOR", + "condition": { + "name": "HSVTOBRGB" + } + } + ], + "category": "colorpicker", + "tags": [ + "Control", + "Light" + ] + }, + { + "property": "rgb", + "friendlyName": "Colored Light", + "channel": "rgb", + "type": "Color", + "refresh": true, + "transformation": "bRGBtoHSV", + "actions": [ + { + "command": "set_rgb", + "parameterType": "NUMBER", + "condition": { + "name": "HSVTOBRGB" + } + } + ], + "category": "colorpicker", + "tags": [ + "Control", + "Light" + ] + }, + { + "property": "doorbell_volume", + "friendlyName": "Doorbell Volume", + "channel": "doorbell_volume", + "type": "Number", + "refresh": true, + "actions": [ + { + "command": "set_doorbell_volume", + "parameterType": "NUMBER" + } + ], + "category": "soundvolume", + "tags": [ + "Setpoint", + "SoundVolume" + ] + }, + { + "property": "alarming_volume", + "friendlyName": "Alarming Volume", + "channel": "alarming_volume", + "type": "Number", + "refresh": true, + "actions": [ + { + "command": "set_alarming_volume", + "parameterType": "NUMBER" + } + ], + "category": "soundvolume", + "tags": [ + "Setpoint", + "SoundVolume" + ] + }, + { + "property": "gateway_volume", + "friendlyName": "Gateway Volume", + "channel": "gateway_volume", + "type": "Number", + "refresh": true, + "actions": [ + { + "command": "set_gateway_volume", + "parameterType": "NUMBER" + } + ], + "category": "soundvolume", + "tags": [ + "Setpoint", + "SoundVolume" + ] + }, + { + "property": "get_arming_time", + "friendlyName": "Arming Time", + "channel": "arming_time", + "type": "Number:Time", + "unit": "seconds", + "refresh": true, + "customRefreshCommand": "get_arming_time", + "actions": [ + { + "command": "set_alarming_time", + "parameterType": "NUMBER" + } + ], + "category": "time", + "tags": [ + "Setpoint", + "Duration" + ] + }, + { + "property": "corridor_on_time", + "friendlyName": "Corridor on time", + "channel": "corridor_on_time", + "type": "Number:Time", + "unit": "seconds", + "refresh": true, + "actions": [ + { + "command": "set_corridor_on_time", + "parameterType": "NUMBER" + } + ], + "category": "time", + "tags": [ + "Setpoint", + "Duration" + ] + }, + { + "property": "language", + "friendlyName": "Voice prompt Language", + "channel": "language", + "type": "String", + "refresh": true, + "customRefreshCommand": "get_device_prop[\"lumi.0\",\"gateway_lang\"]", + "actions": [], + "category": "settings" + }, + { + "property": "get_zigbee_channel", + "friendlyName": "Zigbee Channel", + "channel": "zigbee_channel", + "type": "String", + "refresh": true, + "customRefreshCommand": "get_zigbee_channel", + "actions": [], + "category": "settings" + }, + { + "property": "lumi_bind", + "friendlyName": "Lumi_bind info", + "channel": "lumi_bind", + "type": "String", + "refresh": true, + "customRefreshCommand": "get_lumi_bind", + "actions": [], + "category": "settings" + }, + { + "property": "doorbell_push", + "friendlyName": "Doorbell Push", + "channel": "doorbell_push", + "type": "String", + "refresh": true, + "actions": [ + { + "command": "set_doorbell_push", + "parameterType": "STRING" + } + ], + "category": "settings" + } + ], + "readmeComment": "Used to control the gateway itself. Controlling child devices currently only possible via rules", + "experimental": false + } +}