[miio] Implement lumi devices support for gateways (#11688)
* [miio] Implement lumi devices support for gateways v3 WIP Adding support for the following models: * Aqara LED Light Bulb (Tunable White) (modelId: lumi.light.aqcn02) * IKEA E27 white spectrum opal (modelId: ikea.light.led1545g12) * IKEA E27 white spectrum clear (modelId: ikea.light.led1546g12) * IKEA E14 white spectrum (modelId: ikea.light.led1536g5) * IKEA GU10 white spectrum (modelId: ikea.light.led1537r6) * IKEA E27 warm white (modelId: ikea.light.led1623g12) * IKEA GU10 warm white (modelId: ikea.light.led1650r5) * IKEA E14 warm white (modelId: ikea.light.led1649c5) * Door lock (modelId: lumi.lock.v1) * Aqara Door Lock (modelId: lumi.lock.aq1) * Aqara Door Lock S2 (modelId: lumi.lock.acn02) * Aqara Door lock S2 Pro (modelId: lumi.lock.acn03) * Mi Smart Plug (Zigbee) (modelId: lumi.plug.mmeu01) * Mi Temperature and Humidity Sensor (modelId: lumi.sensor_ht.v1) * Mi Window and Door Sensor (modelId: lumi.sensor_magnet.v2) * Mi Motion Sensor (modelId: lumi.sensor_motion.v2) * Water Leak Sensor (modelId: lumi.sensor_wleak.aq1) * Aqara Temperature and Humidity Sensor (modelId: lumi.weather.v1) Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * Work in progress support plug Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] cleanup, improve messages and initialization Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] Cleanup to prepare for PR Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] add missing placeholder Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] resolve merge issue Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] update readme after rebase Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] Update from review comments and warnings/checkstyle cleanup Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] update readme after merge and update json to updated format Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] Improve online indication Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * reset Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * Update readme Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] update from review comments Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com> * [miio] feedback codereview Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
This commit is contained in:
parent
03b53475ba
commit
d196dc2c92
|
@ -15,6 +15,8 @@ The following things types are available:
|
|||
| miio:generic | Generic type for discovered devices. Once the token is available and the device model is determined, this ThingType will automatically change to the appropriate ThingType |
|
||||
| miio:vacuum | For Xiaomi/RoboRock Robot Vacuum products |
|
||||
| miio:basic | For most other devices like yeelights, airpurifiers. Channels and commands are determined by database configuration |
|
||||
| miio:gateway | Similar to basic, but with the Bridge feature, it can support to forward commands for connected devices |
|
||||
| miio:lumi | Thing type for subdevices connected to the gateway. Note, these devices require a defined gateway to function |
|
||||
| miio:unsupported | For experimenting with other devices which use the Mi IO protocol or to build experimental support |
|
||||
|
||||
# Discovery
|
||||
|
@ -86,6 +88,7 @@ However, for devices that are unsupported, you may override the value and try to
|
|||
|
||||
Note: Suggest to use the cloud communication only for devices that require it.
|
||||
It is unknown at this time if Xiaomi has a rate limit or other limitations on the cloud usage. e.g. if having many devices would trigger some throttling from the cloud side.
|
||||
Note2: communications parameter is not available for lumi devices. Lumi devices communicate using the bridge/gateway.
|
||||
|
||||
### Example Thing file
|
||||
|
||||
|
@ -95,6 +98,11 @@ or in case of unknown models include the model information of a similar device t
|
|||
|
||||
`Thing miio:vacuum:s50 "vacuum" @ "livingroom" [ host="192.168.15.20", token="xxxxxxx", deviceId="326xxxx", model="roborock.vacuum.s4", communication="direct", cloudServer="de" ]`
|
||||
|
||||
in case of gateway, instead of defining it as a Thing, use Bridge
|
||||
|
||||
`Bridge miio:gateway:lumigateway "Mi Smarter Gateway" [ host="10.10.x.x", token="put here your token", deviceId="326xxxx", model="lumi.gateway.mieu01", communication="direct", cloudServer="de" ]`
|
||||
|
||||
|
||||
# Advanced: Unsupported devices
|
||||
|
||||
Newer devices may not yet be supported.
|
||||
|
@ -177,6 +185,9 @@ This will change the communication method and the Mi IO binding can communicate
|
|||
# Mi IO Devices
|
||||
|
||||
!!!devices
|
||||
note: Supported means we received feedback from users this device is working with the binding.
|
||||
For devices with experimental support, we did not yet confirmation that channels are correctly working.
|
||||
Please feedback your findings for these devices (e.g. Are all channels working, do they contain the right information, is controlling the devices working etc.)
|
||||
|
||||
# Channels
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -36,15 +36,18 @@ public final class MiIoBindingConstants {
|
|||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_MIIO = new ThingTypeUID(BINDING_ID, "generic");
|
||||
public static final ThingTypeUID THING_TYPE_BASIC = new ThingTypeUID(BINDING_ID, "basic");
|
||||
public static final ThingTypeUID THING_TYPE_LUMI = new ThingTypeUID(BINDING_ID, "lumi");
|
||||
public static final ThingTypeUID THING_TYPE_GATEWAY = new ThingTypeUID(BINDING_ID, "gateway");
|
||||
public static final ThingTypeUID THING_TYPE_VACUUM = new ThingTypeUID(BINDING_ID, "vacuum");
|
||||
public static final ThingTypeUID THING_TYPE_UNSUPPORTED = new ThingTypeUID(BINDING_ID, "unsupported");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_MIIO, THING_TYPE_BASIC, THING_TYPE_VACUUM, THING_TYPE_UNSUPPORTED)
|
||||
.collect(Collectors.toSet()));
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_MIIO, THING_TYPE_BASIC, THING_TYPE_LUMI, THING_TYPE_GATEWAY,
|
||||
THING_TYPE_VACUUM, THING_TYPE_UNSUPPORTED).collect(Collectors.toSet()));
|
||||
|
||||
public static final Set<ThingTypeUID> NONGENERIC_THING_TYPES_UIDS = Collections.unmodifiableSet(
|
||||
Stream.of(THING_TYPE_BASIC, THING_TYPE_VACUUM, THING_TYPE_UNSUPPORTED).collect(Collectors.toSet()));
|
||||
Stream.of(THING_TYPE_BASIC, THING_TYPE_LUMI, THING_TYPE_GATEWAY, THING_TYPE_VACUUM, THING_TYPE_UNSUPPORTED)
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
// List of all Channel IDs
|
||||
public static final String CHANNEL_BATTERY = "status#battery";
|
||||
|
|
|
@ -97,6 +97,24 @@ public enum MiIoCommand {
|
|||
GET_MULTI_MAP_LIST("get_multi_maps_list"),
|
||||
GET_ROOM_MAPPING("get_room_mapping"),
|
||||
|
||||
// Gateway & child device commands
|
||||
GET_ARMING("get_arming"),
|
||||
GET_ARMING_TIME("get_arming_time"),
|
||||
GET_DOORBEL_VOLUME("get_doorbell_volume"),
|
||||
GET_GATEWAY_VOLUME("get_gateway_volume"),
|
||||
GET_ALARMING_VOLUME("get_alarming_volume"),
|
||||
GET_CLOCK_VOLUME("get_clock_volume"),
|
||||
GET_DOORBELL_VOLUME("get_doorbell_volume"),
|
||||
GET_ARM_WAIT_TIME("get_arm_wait_time"),
|
||||
ALARM_TIME_LEN("alarm_time_len"),
|
||||
EN_ALARM_LIGHT("en_alarm_light"),
|
||||
GET_CORRIDOR_ON_TIME("get_corridor_on_time"),
|
||||
GET_ZIGBEE_CHANNEL("get_zigbee_channel"),
|
||||
GET_RGB("get_rgb"),
|
||||
GET_NIGHTLIGHT_RGB("get_night_light_rgb"),
|
||||
GET_LUMI_BIND("get_lumi_bind"),
|
||||
GET_PROP_PLUG("get_prop_plug"),
|
||||
|
||||
UNKNOWN("");
|
||||
|
||||
private final String command;
|
||||
|
|
|
@ -95,16 +95,35 @@ public enum MiIoDevices {
|
|||
HUAYI_LIGHT_ZW131("huayi.light.zw131", "HUIZUO ZIWEI Ceiling Lamp", THING_TYPE_BASIC),
|
||||
HUNMI_COOKER_NORMAL3("hunmi.cooker.normal3", "MiJia Rice Cooker", THING_TYPE_UNSUPPORTED),
|
||||
IDELAN_AIRCONDITION_V1("idelan.aircondition.v1", "Jinxing Smart Air Conditioner", THING_TYPE_UNSUPPORTED),
|
||||
IKEA_LIGHT_LED1545G12("ikea.light.led1545g12", "IKEA E27 white spectrum opal", THING_TYPE_LUMI),
|
||||
IKEA_LIGHT_LED1546G12("ikea.light.led1546g12", "IKEA E27 white spectrum clear", THING_TYPE_LUMI),
|
||||
IKEA_LIGHT_LED1536G5("ikea.light.led1536g5", "IKEA E14 white spectrum", THING_TYPE_LUMI),
|
||||
IKEA_LIGHT_LED1537R6("ikea.light.led1537r6", "IKEA GU10 white spectrum", THING_TYPE_LUMI),
|
||||
IKEA_LIGHT_LED1623G12("ikea.light.led1623g12", "IKEA E27 warm white", THING_TYPE_LUMI),
|
||||
IKEA_LIGHT_LED1650R5("ikea.light.led1650r5", "IKEA GU10 warm white", THING_TYPE_LUMI),
|
||||
IKEA_LIGHT_LED1649C5("ikea.light.led1649c5", "IKEA E14 warm white", THING_TYPE_LUMI),
|
||||
LUMI_CTRL_NEUTRAL1_V1("lumi.ctrl_neutral1.v1", "Aqara Wall Switch(No Neutral, Single Rocker)",
|
||||
THING_TYPE_UNSUPPORTED),
|
||||
LUMI_CTRL_NEUTRAL2_V1("lumi.ctrl_neutral2.v1", "Aqara Wall Switch (No Neutral, Double Rocker)",
|
||||
THING_TYPE_UNSUPPORTED),
|
||||
LUMI_CURTAIN_HAGL05("lumi.curtain.hagl05", "Xiaomiyoupin Curtain Controller (Wi-Fi)", THING_TYPE_BASIC),
|
||||
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 smart Home Gateway Hub v3", THING_TYPE_BASIC),
|
||||
LUMI_GATEWAY_MIEU01("lumi.gateway.mieu01", "Mi smart Home Gateway Hub", THING_TYPE_BASIC),
|
||||
LUMI_GATEWAY_MGL03("lumi.gateway.mgl03", "Mi Air Purifier virtual", THING_TYPE_GATEWAY),
|
||||
LUMI_GATEWAY_MIEU01("lumi.gateway.mieu01", "Mi smart Home Gateway Hub", THING_TYPE_GATEWAY),
|
||||
LUMI_GATEWAY_V1("lumi.gateway.v1", "Mi smart Home Gateway Hub v1", THING_TYPE_GATEWAY),
|
||||
LUMI_GATEWAY_V2("lumi.gateway.v2", "Mi smart Home GatewayHub v2", THING_TYPE_GATEWAY),
|
||||
LUMI_GATEWAY_V3("lumi.gateway.v3", "Mi smart Home Gateway Hub v3", THING_TYPE_GATEWAY),
|
||||
LUMI_LIGHT_AQCN02("lumi.light.aqcn02", "Aqara LED Light Bulb (Tunable White)", THING_TYPE_LUMI),
|
||||
LUMI_LOCK_V1("lumi.lock.v1", "Door lock", THING_TYPE_LUMI),
|
||||
LUMI_LOCK_AQ1("lumi.lock.aq1", "Aqara Door Lock", THING_TYPE_LUMI),
|
||||
LUMI_LOCK_ACN02("lumi.lock.acn02", "Aqara Door Lock S2", THING_TYPE_LUMI),
|
||||
LUMI_LOCK_ACN03("lumi.lock.acn03", "Aqara Door lock S2 Pro", THING_TYPE_LUMI),
|
||||
LUMI_PLUG_MMEU01("lumi.plug.mmeu01", "Mi Smart Plug (Zigbee)", THING_TYPE_LUMI),
|
||||
LUMI_SENSOR_MAGNET_V2("lumi.sensor_magnet.v2", "Mi Window and Door Sensor", THING_TYPE_LUMI),
|
||||
LUMI_SENSOR_MOTION_AQ2("lumi.sensor_motion.aq2", "Mi Motion Sensor", THING_TYPE_LUMI),
|
||||
LUMI_SENSOR_MOTION_V2("lumi.sensor_motion.v2", "Mi Motion Sensor", THING_TYPE_LUMI),
|
||||
LUMI_SENSOR_HT_V1("lumi.sensor_ht.v1", "Mi Temperature and Humidity Sensor", THING_TYPE_LUMI),
|
||||
LUMI_SENSOR_WLEAK_AQ1("lumi.sensor_wleak.aq1", "Water Leak Sensor", THING_TYPE_LUMI),
|
||||
LUMI_WEATHER_V1("lumi.weather.v1", "Aqara Temperature and Humidity Sensor", THING_TYPE_LUMI),
|
||||
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),
|
||||
|
@ -139,7 +158,7 @@ public enum MiIoDevices {
|
|||
PHILIPS_LIGHT_MONO1("philips.light.mono1", "Philips Smart Lamp", THING_TYPE_BASIC),
|
||||
PHILIPS_LIGHT_MOONLIGHT("philips.light.moonlight", "Philips ZhiRui Bedside Lamp", THING_TYPE_BASIC),
|
||||
PHILIPS_LIGHT_OBCEIL("philips.light.obceil", "Zhirui Ceiling Lamp Black 80W", THING_TYPE_BASIC),
|
||||
PHILIPS_LIGHT_OBCEIM("philips.light.obceim", " Zhirui Ceiling Lamp Black 40W", THING_TYPE_BASIC),
|
||||
PHILIPS_LIGHT_OBCEIM("philips.light.obceim", "Zhirui Ceiling Lamp Black 40W", THING_TYPE_BASIC),
|
||||
PHILIPS_LIGHT_OBCEIS("philips.light.obceis", "Zhirui Ceiling Lamp Black 28W", THING_TYPE_BASIC),
|
||||
PHILIPS_LIGHT_RWREAD("philips.light.rwread", "Mijia Philips Study Desk Lamp", THING_TYPE_BASIC),
|
||||
PHILIPS_LIGHT_SCEIL("philips.light.sceil", "Zhirui Ceiling Lamp Starry 80W", THING_TYPE_BASIC),
|
||||
|
@ -404,7 +423,7 @@ public enum MiIoDevices {
|
|||
|
||||
public static MiIoDevices getType(String modelString) {
|
||||
for (MiIoDevices mioDev : MiIoDevices.values()) {
|
||||
if (mioDev.getModel().equals(modelString)) {
|
||||
if (mioDev.getModel().equalsIgnoreCase(modelString)) {
|
||||
return mioDev;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,16 @@ import org.openhab.binding.miio.internal.basic.BasicChannelTypeProvider;
|
|||
import org.openhab.binding.miio.internal.basic.MiIoDatabaseWatchService;
|
||||
import org.openhab.binding.miio.internal.cloud.CloudConnector;
|
||||
import org.openhab.binding.miio.internal.handler.MiIoBasicHandler;
|
||||
import org.openhab.binding.miio.internal.handler.MiIoGatewayHandler;
|
||||
import org.openhab.binding.miio.internal.handler.MiIoGenericHandler;
|
||||
import org.openhab.binding.miio.internal.handler.MiIoLumiHandler;
|
||||
import org.openhab.binding.miio.internal.handler.MiIoUnsupportedHandler;
|
||||
import org.openhab.binding.miio.internal.handler.MiIoVacuumHandler;
|
||||
import org.openhab.core.common.ThreadPoolManager;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
|
@ -122,6 +125,14 @@ public class MiIoHandlerFactory extends BaseThingHandlerFactory {
|
|||
return new MiIoBasicHandler(thing, miIoDatabaseWatchService, cloudConnector, channelTypeRegistry,
|
||||
basicChannelTypeProvider, i18nProvider, localeProvider);
|
||||
}
|
||||
if (thingTypeUID.equals(THING_TYPE_LUMI)) {
|
||||
return new MiIoLumiHandler(thing, miIoDatabaseWatchService, cloudConnector, channelTypeRegistry,
|
||||
basicChannelTypeProvider, i18nProvider, localeProvider);
|
||||
}
|
||||
if (thingTypeUID.equals(THING_TYPE_GATEWAY)) {
|
||||
return new MiIoGatewayHandler((Bridge) thing, miIoDatabaseWatchService, cloudConnector, channelTypeRegistry,
|
||||
basicChannelTypeProvider, i18nProvider, localeProvider);
|
||||
}
|
||||
if (thingTypeUID.equals(THING_TYPE_VACUUM)) {
|
||||
return new MiIoVacuumHandler(thing, miIoDatabaseWatchService, cloudConnector, channelTypeRegistry,
|
||||
i18nProvider, localeProvider);
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.openhab.core.library.types.PercentType;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
@ -79,6 +80,33 @@ public class Conversions {
|
|||
return rgbValue;
|
||||
}
|
||||
|
||||
public static JsonElement deviceDataTab(JsonElement deviceLog, @Nullable Map<String, Object> deviceVariables)
|
||||
throws ClassCastException, IllegalStateException {
|
||||
if (!deviceLog.isJsonObject() && !deviceLog.isJsonPrimitive()) {
|
||||
return deviceLog;
|
||||
}
|
||||
JsonObject deviceLogJsonObj = deviceLog.isJsonObject() ? deviceLog.getAsJsonObject()
|
||||
: (JsonObject) JsonParser.parseString(deviceLog.getAsString());
|
||||
JsonArray resultLog = new JsonArray();
|
||||
if (deviceLogJsonObj.has("data") && deviceLogJsonObj.get("data").isJsonArray()) {
|
||||
for (JsonElement element : deviceLogJsonObj.get("data").getAsJsonArray()) {
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject dataObject = element.getAsJsonObject();
|
||||
if (dataObject.has("value")) {
|
||||
String value = dataObject.get("value").getAsString();
|
||||
JsonElement val = JsonParser.parseString(value);
|
||||
if (val.isJsonArray()) {
|
||||
resultLog.add(JsonParser.parseString(val.getAsString()));
|
||||
} else {
|
||||
resultLog.add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultLog;
|
||||
}
|
||||
|
||||
private static JsonElement secondsToHours(JsonElement seconds) throws ClassCastException {
|
||||
double value = seconds.getAsDouble() / 3600;
|
||||
return new JsonPrimitive(value);
|
||||
|
@ -190,6 +218,8 @@ public class Conversions {
|
|||
return addBrightToHSV(value, deviceVariables);
|
||||
case "BRGBTOHSV":
|
||||
return bRGBtoHSV(value);
|
||||
case "DEVICEDATATAB":
|
||||
return deviceDataTab(value, deviceVariables);
|
||||
case "GETDIDELEMENT":
|
||||
return getDidElement(value, deviceVariables);
|
||||
default:
|
||||
|
|
|
@ -114,6 +114,9 @@ public class MiIoDatabaseWatchService extends AbstractWatchService {
|
|||
try {
|
||||
JsonObject deviceMapping = Utils.convertFileToJSON(db);
|
||||
MiIoBasicDevice devdb = GSON.fromJson(deviceMapping, MiIoBasicDevice.class);
|
||||
if (devdb == null) {
|
||||
continue;
|
||||
}
|
||||
for (String id : devdb.getDevice().getId()) {
|
||||
workingDatabaseList.put(id, db);
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ public class MiCloudConnector {
|
|||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("data", "{\"obj_name\":\"" + vacuumMap + "\"}");
|
||||
String mapResponse = request(url, map);
|
||||
logger.trace("response: {}", mapResponse);
|
||||
logger.trace("Response: {}", mapResponse);
|
||||
String errorMsg = "";
|
||||
try {
|
||||
JsonElement response = JsonParser.parseString(mapResponse);
|
||||
|
@ -181,7 +181,6 @@ public class MiCloudConnector {
|
|||
|
||||
public String getDeviceStatus(String device, String country) throws MiCloudException {
|
||||
final String response = request("/home/device_list", country, "{\"dids\":[\"" + device + "\"]}");
|
||||
logger.debug("response: {}", response);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -201,7 +200,6 @@ public class MiCloudConnector {
|
|||
throw new MiCloudException(err, e);
|
||||
}
|
||||
final String response = request("/home/rpc/" + id, country, command);
|
||||
logger.debug("response: {}", response);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
|
|||
}
|
||||
|
||||
private String getCloudDiscoveryMode() {
|
||||
final Configuration miioConfig = this.miioConfig;
|
||||
if (miioConfig != null) {
|
||||
try {
|
||||
Dictionary<String, @Nullable Object> properties = miioConfig.getProperties();
|
||||
|
@ -185,20 +186,23 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
|
|||
List<CloudDeviceDTO> dv = cloudConnector.getDevicesList();
|
||||
for (CloudDeviceDTO device : dv) {
|
||||
String id = device.getDid();
|
||||
if (cloudDiscoveryMode.contentEquals(SUPPORTED)) {
|
||||
if (SUPPORTED.contentEquals(cloudDiscoveryMode)) {
|
||||
if (MiIoDevices.getType(device.getModel()).getThingType().equals(THING_TYPE_UNSUPPORTED)) {
|
||||
logger.warn("Discovered from cloud, but ignored because not supported: {} {}", id, device);
|
||||
logger.debug("Discovered from cloud, but ignored because not supported: {} {}", id, device);
|
||||
}
|
||||
}
|
||||
if (device.getIsOnline()) {
|
||||
if (device.getIsOnline() || ALL.contentEquals(cloudDiscoveryMode)) {
|
||||
logger.debug("Discovered from cloud: {} {}", id, device);
|
||||
cloudDevices.put(id, device.getLocalip());
|
||||
String token = device.getToken();
|
||||
String label = device.getName() + " " + id + " (" + Utils.getHexId(id) + ")";
|
||||
String label = device.getName() + " (" + id + (id.contains(".") ? "" : " / " + Utils.getHexId(id))
|
||||
+ ")";
|
||||
String model = device.getModel();
|
||||
String country = device.getServer();
|
||||
boolean isOnline = device.getIsOnline();
|
||||
String parent = device.getParentId();
|
||||
String ip = device.getLocalip();
|
||||
submitDiscovery(ip, token, id, label, country, isOnline);
|
||||
submitDiscovery(ip, token, id, label, model, country, isOnline, parent);
|
||||
} else {
|
||||
logger.debug("Discovered from cloud, but ignored because not online: {} {}", id, device);
|
||||
}
|
||||
|
@ -212,8 +216,10 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
|
|||
String token = Utils.getHex(msg.getChecksum());
|
||||
String hexId = Utils.getHex(msg.getDeviceId());
|
||||
String id = Utils.fromHEX(hexId);
|
||||
String label = "Xiaomi Mi Device " + id + " (" + Utils.getHexId(id) + ")";
|
||||
String label = "Xiaomi Mi Device " + " (" + id + (id.contains(".") ? "" : " / " + Utils.getHexId(id)) + ")";
|
||||
String model = "";
|
||||
String country = "";
|
||||
String parent = "";
|
||||
boolean isOnline = false;
|
||||
if (ip.equals(cloudDevices.get(id))) {
|
||||
logger.debug("Skipped adding local found {}. Already discovered by cloud.", label);
|
||||
|
@ -226,18 +232,27 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
|
|||
logger.debug("Cloud Info: {}", cloudInfo);
|
||||
token = cloudInfo.getToken();
|
||||
label = cloudInfo.getName() + " " + id + " (" + Utils.getHexId(id) + ")";
|
||||
model = cloudInfo.getModel();
|
||||
country = cloudInfo.getServer();
|
||||
isOnline = cloudInfo.getIsOnline();
|
||||
parent = cloudInfo.getParentId();
|
||||
}
|
||||
}
|
||||
submitDiscovery(ip, token, id, label, country, isOnline);
|
||||
submitDiscovery(ip, token, id, label, model, country, isOnline, parent);
|
||||
}
|
||||
|
||||
private void submitDiscovery(String ip, String token, String id, String label, String country, boolean isOnline) {
|
||||
ThingUID uid = new ThingUID(THING_TYPE_MIIO, Utils.getHexId(id).replace(".", "_"));
|
||||
private void submitDiscovery(String ip, String token, String id, String label, String model, String country,
|
||||
boolean isOnline, String parent) {
|
||||
ThingUID uid;
|
||||
ThingTypeUID thingType = MiIoDevices.getType(model).getThingType();
|
||||
if (id.startsWith("lumi.") || THING_TYPE_GATEWAY.equals(thingType) || THING_TYPE_LUMI.equals(thingType)) {
|
||||
uid = new ThingUID(thingType, Utils.getHexId(id).replace(".", "_"));
|
||||
} else {
|
||||
uid = new ThingUID(THING_TYPE_MIIO, Utils.getHexId(id).replace(".", "_"));
|
||||
}
|
||||
DiscoveryResultBuilder dr = DiscoveryResultBuilder.create(uid).withProperty(PROPERTY_HOST_IP, ip)
|
||||
.withProperty(PROPERTY_DID, id);
|
||||
if (IGNORED_TOKENS.contains(token)) {
|
||||
if (IGNORED_TOKENS.contains(token) || token.isBlank()) {
|
||||
logger.debug("Discovered Mi Device {} ({}) at {} as {}", id, Utils.getHexId(id), ip, uid);
|
||||
logger.debug(
|
||||
"No token discovered for device {}. For options how to get the token, check the binding readme.",
|
||||
|
@ -249,6 +264,9 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
|
|||
dr = dr.withProperty(PROPERTY_TOKEN, token).withRepresentationProperty(PROPERTY_DID)
|
||||
.withLabel(label + " with token");
|
||||
}
|
||||
if (!model.isEmpty()) {
|
||||
dr = dr.withProperty(PROPERTY_MODEL, model);
|
||||
}
|
||||
if (!country.isEmpty() && isOnline) {
|
||||
dr = dr.withProperty(PROPERTY_CLOUDSERVER, country);
|
||||
}
|
||||
|
@ -321,9 +339,10 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
|
|||
* Stops the {@link ReceiverThread} thread
|
||||
*/
|
||||
private synchronized void stopReceiverThreat() {
|
||||
final Thread socketReceiveThread = this.socketReceiveThread;
|
||||
if (socketReceiveThread != null) {
|
||||
socketReceiveThread.interrupt();
|
||||
socketReceiveThread = null;
|
||||
this.socketReceiveThread = null;
|
||||
}
|
||||
closeSocket();
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
|
|||
protected final Bundle bundle;
|
||||
protected final TranslationProvider i18nProvider;
|
||||
protected final LocaleProvider localeProvider;
|
||||
protected final Map<Thing, MiIoLumiHandler> childDevices = new ConcurrentHashMap<>();
|
||||
|
||||
protected ScheduledExecutorService miIoScheduler = new ScheduledThreadPoolExecutor(3,
|
||||
new NamedThreadFactory("binding-" + getThing().getUID().getAsString(), true));
|
||||
|
@ -159,14 +160,17 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
|
|||
|
||||
final MiIoBindingConfiguration configuration = getConfigAs(MiIoBindingConfiguration.class);
|
||||
this.configuration = configuration;
|
||||
if (configuration.host.isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.config-error-ip");
|
||||
return;
|
||||
}
|
||||
if (!tokenCheckPass(configuration.token)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.config-error-token");
|
||||
return;
|
||||
if (!getThing().getThingTypeUID().equals(THING_TYPE_LUMI)) {
|
||||
if (configuration.host.isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.config-error-ip");
|
||||
return;
|
||||
}
|
||||
if (!tokenCheckPass(configuration.token)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.config-error-token");
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.cloudServer = configuration.cloudServer;
|
||||
this.deviceId = configuration.deviceId;
|
||||
|
@ -303,6 +307,10 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
|
|||
*/
|
||||
protected int sendCommand(String command, String params, String cloudServer, String sender) {
|
||||
try {
|
||||
if (!sender.isBlank()) {
|
||||
logger.debug("Received child command from {} : {} - {} (via: {})", sender, command, params,
|
||||
getThing().getUID());
|
||||
}
|
||||
final MiIoAsyncCommunication connection = getConnection();
|
||||
return (connection != null) ? connection.queueCommand(command, params, cloudServer, sender) : 0;
|
||||
} catch (MiIoCryptoException | IOException e) {
|
||||
|
@ -623,8 +631,25 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
|
|||
|
||||
@Override
|
||||
public void onMessageReceived(MiIoSendCommand response) {
|
||||
if (!response.getSender().isBlank() && !response.getSender().contentEquals(getThing().getUID().getAsString())) {
|
||||
for (Entry<Thing, MiIoLumiHandler> entry : childDevices.entrySet()) {
|
||||
if (entry.getKey().getUID().getAsString().contentEquals(response.getSender())) {
|
||||
logger.trace("Submit response to to child {} -> {}", response.getSender(), entry.getKey().getUID());
|
||||
entry.getValue().onMessageReceived(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
logger.debug("{} Could not find match in {} child devices for submitter {}", getThing().getUID(),
|
||||
childDevices.size(), response.getSender());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Received response for device {} type: {}, result: {}, fullresponse: {}",
|
||||
getThing().getUID().getId(), response.getCommand(), response.getResult(), response.getResponse());
|
||||
getThing().getUID().getId(),
|
||||
MiIoCommand.UNKNOWN.equals(response.getCommand())
|
||||
? response.getCommand().toString() + "(" + response.getCommandString() + ")"
|
||||
: response.getCommand(),
|
||||
response.getResult(), response.getResponse());
|
||||
if (response.isError()) {
|
||||
logger.debug("Error received for command '{}': {}.", response.getCommandString(),
|
||||
response.getResponse().get("error"));
|
||||
|
|
|
@ -90,21 +90,21 @@ import com.google.gson.JsonSyntaxException;
|
|||
*/
|
||||
@NonNullByDefault
|
||||
public class MiIoBasicHandler extends MiIoAbstractHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(MiIoBasicHandler.class);
|
||||
private boolean hasChannelStructure;
|
||||
protected final Logger logger = LoggerFactory.getLogger(MiIoBasicHandler.class);
|
||||
protected boolean hasChannelStructure;
|
||||
|
||||
private final ExpiringCache<Boolean> updateDataCache = new ExpiringCache<>(CACHE_EXPIRY, () -> {
|
||||
protected final ExpiringCache<Boolean> updateDataCache = new ExpiringCache<>(CACHE_EXPIRY, () -> {
|
||||
miIoScheduler.schedule(this::updateData, 0, TimeUnit.SECONDS);
|
||||
return true;
|
||||
});
|
||||
|
||||
List<MiIoBasicChannel> refreshList = new ArrayList<>();
|
||||
private Map<String, MiIoBasicChannel> refreshListCustomCommands = new HashMap<>();
|
||||
protected List<MiIoBasicChannel> refreshList = new ArrayList<>();
|
||||
protected Map<String, MiIoBasicChannel> refreshListCustomCommands = new HashMap<>();
|
||||
|
||||
private @Nullable MiIoBasicDevice miioDevice;
|
||||
private Map<ChannelUID, MiIoBasicChannel> actions = new HashMap<>();
|
||||
private ChannelTypeRegistry channelTypeRegistry;
|
||||
private BasicChannelTypeProvider basicChannelTypeProvider;
|
||||
protected @Nullable MiIoBasicDevice miioDevice;
|
||||
protected Map<ChannelUID, MiIoBasicChannel> actions = new HashMap<>();
|
||||
protected ChannelTypeRegistry channelTypeRegistry;
|
||||
protected BasicChannelTypeProvider basicChannelTypeProvider;
|
||||
private Map<String, Integer> customRefreshInterval = new HashMap<>();
|
||||
|
||||
public MiIoBasicHandler(Thing thing, MiIoDatabaseWatchService miIoDatabaseWatchService,
|
||||
|
@ -301,14 +301,14 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void forceStatusUpdate() {
|
||||
protected void forceStatusUpdate() {
|
||||
updateDataCache.invalidateValue();
|
||||
miIoScheduler.schedule(() -> {
|
||||
updateData();
|
||||
}, 3000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private @Nullable JsonElement miotTransform(MiIoBasicChannel miIoBasicChannel, @Nullable JsonElement value) {
|
||||
protected @Nullable JsonElement miotTransform(MiIoBasicChannel miIoBasicChannel, @Nullable JsonElement value) {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("did", miIoBasicChannel.getChannel());
|
||||
json.addProperty("siid", miIoBasicChannel.getSiid());
|
||||
|
@ -317,7 +317,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
return json;
|
||||
}
|
||||
|
||||
private @Nullable JsonElement miotActionTransform(MiIoDeviceAction action, MiIoBasicChannel miIoBasicChannel,
|
||||
protected @Nullable JsonElement miotActionTransform(MiIoDeviceAction action, MiIoBasicChannel miIoBasicChannel,
|
||||
@Nullable JsonElement value) {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("did", miIoBasicChannel.getChannel());
|
||||
|
@ -344,8 +344,8 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
final MiIoBasicDevice midevice = miioDevice;
|
||||
if (midevice != null) {
|
||||
deviceVariables.put(TIMESTAMP, Instant.now().getEpochSecond());
|
||||
refreshProperties(midevice);
|
||||
refreshCustomProperties(midevice);
|
||||
refreshProperties(midevice, "");
|
||||
refreshCustomProperties(midevice, false);
|
||||
refreshNetwork();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -377,14 +377,16 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void refreshCustomProperties(MiIoBasicDevice midevice) {
|
||||
protected void refreshCustomProperties(MiIoBasicDevice midevice, boolean cloudOnly) {
|
||||
logger.debug("Custom Refresh for device '{}': {} channels ", getThing().getUID(),
|
||||
refreshListCustomCommands.size());
|
||||
for (MiIoBasicChannel miChannel : refreshListCustomCommands.values()) {
|
||||
if (customRefreshIntervalCheck(miChannel) || !linkedChannelCheck(miChannel)) {
|
||||
continue;
|
||||
}
|
||||
final JsonElement para = miChannel.getCustomRefreshParameters();
|
||||
String cmd = miChannel.getChannelCustomRefreshCommand() + (para != null ? para.toString() : "");
|
||||
if (!cmd.startsWith("/")) {
|
||||
if (!cmd.startsWith("/") && !cloudOnly) {
|
||||
cmds.put(sendCommand(cmd), miChannel.getChannel());
|
||||
} else {
|
||||
if (cloudServer.isBlank()) {
|
||||
|
@ -397,10 +399,14 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean refreshProperties(MiIoBasicDevice device) {
|
||||
MiIoCommand command = MiIoCommand.getCommand(device.getDevice().getPropertyMethod());
|
||||
protected boolean refreshProperties(MiIoBasicDevice device, String childId) {
|
||||
String command = device.getDevice().getPropertyMethod();
|
||||
int maxProperties = device.getDevice().getMaxProperties();
|
||||
JsonArray getPropString = new JsonArray();
|
||||
if (!childId.isBlank()) {
|
||||
getPropString.add(childId);
|
||||
maxProperties++;
|
||||
}
|
||||
for (MiIoBasicChannel miChannel : refreshList) {
|
||||
if (customRefreshIntervalCheck(miChannel) || !linkedChannelCheck(miChannel)) {
|
||||
continue;
|
||||
|
@ -419,22 +425,32 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
if (getPropString.size() >= maxProperties) {
|
||||
sendRefreshProperties(command, getPropString);
|
||||
getPropString = new JsonArray();
|
||||
if (!childId.isBlank()) {
|
||||
getPropString.add(childId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getPropString.size() > 0) {
|
||||
if (getPropString.size() > (childId.isBlank() ? 0 : 1)) {
|
||||
sendRefreshProperties(command, getPropString);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendRefreshProperties(MiIoCommand command, JsonArray getPropString) {
|
||||
sendCommand(command, getPropString.toString());
|
||||
protected void sendRefreshProperties(String command, JsonArray getPropString) {
|
||||
JsonArray para = getPropString;
|
||||
if (MiIoCommand.GET_DEVICE_PROPERTY_EXP.getCommand().contentEquals(command)) {
|
||||
logger.debug("This seems a subdevice propery refresh for {}... ({} {})", getThing().getUID(), command,
|
||||
getPropString.toString());
|
||||
para = new JsonArray();
|
||||
para.add(getPropString);
|
||||
}
|
||||
sendCommand(command, para.toString(), getCloudServer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the channel structure has been build already based on the model data. If not build it.
|
||||
*/
|
||||
private void checkChannelStructure() {
|
||||
protected void checkChannelStructure() {
|
||||
final MiIoBindingConfiguration configuration = this.configuration;
|
||||
if (configuration == null) {
|
||||
return;
|
||||
|
@ -457,17 +473,16 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
if (miChannel.getChannelCustomRefreshCommand().isBlank()) {
|
||||
refreshList.add(miChannel);
|
||||
} else {
|
||||
String i = miChannel.getChannelCustomRefreshCommand().split("\\[")[0];
|
||||
refreshListCustomCommands.put(i.trim(), miChannel);
|
||||
String cm = miChannel.getChannelCustomRefreshCommand();
|
||||
refreshListCustomCommands.put(cm.trim(), miChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private boolean buildChannelStructure(String deviceName) {
|
||||
protected boolean buildChannelStructure(String deviceName) {
|
||||
logger.debug("Building Channel Structure for {} - Model: {}", getThing().getUID().toString(), deviceName);
|
||||
URL fn = miIoDatabaseWatchService.getDatabaseUrl(deviceName);
|
||||
if (fn == null) {
|
||||
|
@ -531,7 +546,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
private @Nullable ChannelUID addChannel(ThingBuilder thingBuilder, MiIoBasicChannel miChannel, String model,
|
||||
protected @Nullable ChannelUID addChannel(ThingBuilder thingBuilder, MiIoBasicChannel miChannel, String model,
|
||||
String key) {
|
||||
String channel = miChannel.getChannel();
|
||||
String dataType = miChannel.getType();
|
||||
|
@ -571,7 +586,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
return channelUID;
|
||||
}
|
||||
|
||||
private @Nullable MiIoBasicChannel getChannel(String parameter) {
|
||||
protected @Nullable MiIoBasicChannel getChannel(String parameter) {
|
||||
for (MiIoBasicChannel refreshEntry : refreshList) {
|
||||
if (refreshEntry.getProperty().equals(parameter)) {
|
||||
return refreshEntry;
|
||||
|
@ -591,13 +606,22 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
return null;
|
||||
}
|
||||
|
||||
private void updatePropsFromJsonArray(MiIoSendCommand response) {
|
||||
protected void updatePropsFromJsonArray(MiIoSendCommand response) {
|
||||
boolean isSubdeviceUpdate = false;
|
||||
JsonArray res = response.getResult().getAsJsonArray();
|
||||
JsonArray para = JsonParser.parseString(response.getCommandString()).getAsJsonObject().get("params")
|
||||
.getAsJsonArray();
|
||||
if (para.get(0).isJsonArray()) {
|
||||
isSubdeviceUpdate = true;
|
||||
para = para.get(0).getAsJsonArray();
|
||||
para.remove(0);
|
||||
if (res.get(0).isJsonArray()) {
|
||||
res = res.get(0).getAsJsonArray();
|
||||
}
|
||||
}
|
||||
if (res.size() != para.size()) {
|
||||
logger.debug("Unexpected size different. Request size {}, response size {}. (Req: {}, Resp:{})",
|
||||
para.size(), res.size(), para, res);
|
||||
logger.debug("Unexpected size different{}. Request size {}, response size {}. (Req: {}, Resp:{})",
|
||||
isSubdeviceUpdate ? " for childdevice refresh" : "", para.size(), res.size(), para, res);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < para.size(); i++) {
|
||||
|
@ -621,7 +645,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void updatePropsFromJsonObject(MiIoSendCommand response) {
|
||||
protected void updatePropsFromJsonObject(MiIoSendCommand response) {
|
||||
JsonObject res = response.getResult().getAsJsonObject();
|
||||
for (Object k : res.keySet()) {
|
||||
String param = (String) k;
|
||||
|
@ -635,7 +659,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void updateChannel(@Nullable MiIoBasicChannel basicChannel, String param, JsonElement value) {
|
||||
protected void updateChannel(@Nullable MiIoBasicChannel basicChannel, String param, JsonElement value) {
|
||||
JsonElement val = value;
|
||||
deviceVariables.put(param, val);
|
||||
if (basicChannel == null) {
|
||||
|
@ -712,7 +736,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void quantityTypeUpdate(MiIoBasicChannel basicChannel, JsonElement val, String type) {
|
||||
protected void quantityTypeUpdate(MiIoBasicChannel basicChannel, JsonElement val, String type) {
|
||||
if (!basicChannel.getUnit().isBlank()) {
|
||||
Unit<?> unit = MiIoQuantiyTypes.get(basicChannel.getUnit());
|
||||
if (unit != null) {
|
||||
|
@ -751,7 +775,10 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
@Override
|
||||
public void onMessageReceived(MiIoSendCommand response) {
|
||||
super.onMessageReceived(response);
|
||||
if (response.isError()) {
|
||||
if (response.isError() || (!response.getSender().isBlank()
|
||||
&& !response.getSender().contentEquals(getThing().getUID().getAsString()))) {
|
||||
logger.trace("Device {} is not processing command {} as no match. Sender id:'{}'", getThing().getUID(),
|
||||
response.getId(), response.getSender());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
@ -790,6 +817,9 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
|
|||
}
|
||||
}
|
||||
cmds.remove(response.getId());
|
||||
} else {
|
||||
logger.debug("Could not identify channel for {}. Device {} has {} commands in queue.",
|
||||
response.getMethod(), getThing().getUID(), cmds.size());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2022 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.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.miio.internal.basic.BasicChannelTypeProvider;
|
||||
import org.openhab.binding.miio.internal.basic.MiIoDatabaseWatchService;
|
||||
import org.openhab.binding.miio.internal.cloud.CloudConnector;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.builder.BridgeBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link MiIoGatewayHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Marcel Verpaalen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MiIoGatewayHandler extends MiIoBasicHandler implements BridgeHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MiIoGatewayHandler.class);
|
||||
|
||||
public MiIoGatewayHandler(Bridge thing, MiIoDatabaseWatchService miIoDatabaseWatchService,
|
||||
CloudConnector cloudConnector, ChannelTypeRegistry channelTypeRegistry,
|
||||
BasicChannelTypeProvider basicChannelTypeProvider, TranslationProvider i18nProvider,
|
||||
LocaleProvider localeProvider) {
|
||||
super(thing, miIoDatabaseWatchService, cloudConnector, channelTypeRegistry, basicChannelTypeProvider,
|
||||
i18nProvider, localeProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bridge getThing() {
|
||||
return (Bridge) super.getThing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bridge builder, which allows to modify the bridge. The method
|
||||
* {@link BaseThingHandler#updateThing(Thing)} must be called to persist the changes.
|
||||
*
|
||||
* @return {@link BridgeBuilder} which builds an exact copy of the bridge
|
||||
*/
|
||||
@Override
|
||||
protected BridgeBuilder editThing() {
|
||||
return BridgeBuilder.create(thing.getThingTypeUID(), thing.getUID()).withBridge(thing.getBridgeUID())
|
||||
.withChannels(thing.getChannels()).withConfiguration(thing.getConfiguration())
|
||||
.withLabel(thing.getLabel()).withLocation(thing.getLocation()).withProperties(thing.getProperties());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
|
||||
logger.debug("Child registered with gateway: {} {} -> {} {}", childThing.getUID(), childThing.getLabel(),
|
||||
getThing().getUID(), getThing().getLabel());
|
||||
childDevices.put(childThing, (MiIoLumiHandler) childHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
|
||||
logger.debug("Child released from gateway: {} {} -> {} {}", childThing.getUID(), childThing.getLabel(),
|
||||
getThing().getUID(), getThing().getLabel());
|
||||
childDevices.remove(childThing);
|
||||
}
|
||||
|
||||
public @Nullable BridgeHandler getHandler() {
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2022 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.handler;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.miio.internal.MiIoBindingConfiguration;
|
||||
import org.openhab.binding.miio.internal.MiIoSendCommand;
|
||||
import org.openhab.binding.miio.internal.basic.BasicChannelTypeProvider;
|
||||
import org.openhab.binding.miio.internal.basic.MiIoBasicDevice;
|
||||
import org.openhab.binding.miio.internal.basic.MiIoDatabaseWatchService;
|
||||
import org.openhab.binding.miio.internal.cloud.CloudConnector;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link MiIoLumiHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Marcel Verpaalen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MiIoLumiHandler extends MiIoBasicHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MiIoLumiHandler.class);
|
||||
private @Nullable MiIoGatewayHandler bridgeHandler;
|
||||
|
||||
public MiIoLumiHandler(Thing thing, MiIoDatabaseWatchService miIoDatabaseWatchService,
|
||||
CloudConnector cloudConnector, ChannelTypeRegistry channelTypeRegistry,
|
||||
BasicChannelTypeProvider basicChannelTypeProvider, TranslationProvider i18nProvider,
|
||||
LocaleProvider localeProvider) {
|
||||
super(thing, miIoDatabaseWatchService, cloudConnector, channelTypeRegistry, basicChannelTypeProvider,
|
||||
i18nProvider, localeProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
isIdentified = false;
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
final MiIoBindingConfiguration config = this.configuration;
|
||||
if (config != null && config.deviceId.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing required deviceId");
|
||||
return;
|
||||
}
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"No device bridge has been configured");
|
||||
return;
|
||||
} else {
|
||||
logger.debug("Bridge for {} {} = {} {} ({})", getThing().getUID(), getThing().getLabel(),
|
||||
bridge.getBridgeUID(), bridge.getLabel(), bridge.getHandler());
|
||||
}
|
||||
bridgeHandler = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MiIoGatewayHandler getBridgeHandler() {
|
||||
if (bridgeHandler == null) {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
final MiIoGatewayHandler bridgeHandler = (MiIoGatewayHandler) bridge.getHandler();
|
||||
if (bridgeHandler != null) {
|
||||
if (!bridgeHandler.childDevices.containsKey(getThing())) {
|
||||
logger.warn(("Child device {} missing at bridge {}. We should not see this"),
|
||||
getThing().getUID(), bridgeHandler.getThing().getUID());
|
||||
bridgeHandler.childDevices.forEach((k, v) -> logger.debug("Devices in bridge: {} : {}", k, v));
|
||||
}
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
return bridgeHandler;
|
||||
} else {
|
||||
logger.debug("Bridge is defined, but bridge handler not found for {} {}.", getThing().getUID(),
|
||||
getThing().getLabel());
|
||||
}
|
||||
}
|
||||
logger.debug("Bridge is missing for {} {}", getThing().getUID(), getThing().getLabel());
|
||||
}
|
||||
return this.bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCloudServer() {
|
||||
final MiIoGatewayHandler bh = this.bridgeHandler;
|
||||
if (bh != null) {
|
||||
return bh.getCloudServer();
|
||||
} else {
|
||||
final MiIoBindingConfiguration config = this.configuration;
|
||||
return config != null ? config.cloudServer : "";
|
||||
}
|
||||
}
|
||||
|
||||
// Override to inject the sender
|
||||
@Override
|
||||
protected int sendCommand(String command, String params, String cloudServer, String sender) {
|
||||
final MiIoGatewayHandler bridge = getBridgeHandler();
|
||||
if (bridge != null) {
|
||||
logger.debug("Send via bridge {} {} (Cloudserver {})", command, params, cloudServer);
|
||||
return bridge.sendCommand(command, params, cloudServer, getThing().getUID().getAsString());
|
||||
} else {
|
||||
logger.debug("Bridge handler is null. This is unexpected and prevents sending the update");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void updateData() {
|
||||
logger.debug("Periodic update for '{}' ({})", getThing().getUID(), getThing().getThingTypeUID());
|
||||
try {
|
||||
checkChannelStructure();
|
||||
final MiIoBindingConfiguration config = this.configuration;
|
||||
final MiIoBasicDevice midevice = miioDevice;
|
||||
if (midevice != null && configuration != null && config != null) {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null || !bridge.getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Bridge {} offline, skipping regular refresh for {}", getThing().getBridgeUID(),
|
||||
getThing().getUID());
|
||||
refreshCustomProperties(midevice, true);
|
||||
return;
|
||||
}
|
||||
deviceVariables.put(TIMESTAMP, Instant.now().getEpochSecond());
|
||||
logger.debug("Refresh properties for child device {}", getThing().getLabel());
|
||||
refreshProperties(midevice, config.deviceId);
|
||||
logger.debug("Refresh custom commands for child device {}", getThing().getLabel());
|
||||
refreshCustomProperties(midevice, false);
|
||||
} else {
|
||||
logger.debug("Null value occured for device {}: {}", midevice, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Error while performing periodic refresh for '{}': {}", getThing().getUID(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(MiIoSendCommand response) {
|
||||
super.onMessageReceived(response);
|
||||
if (!response.isError() && (!response.getSender().isBlank()
|
||||
&& response.getSender().contentEquals(getThing().getUID().getAsString()))) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -301,7 +301,9 @@ public class MiIoUnsupportedHandler extends MiIoAbstractHandler {
|
|||
JsonObject deviceMapping = Utils.convertFileToJSON(fn);
|
||||
logger.debug("Using device database: {} for device {}", fn.getFile(), deviceName);
|
||||
final MiIoBasicDevice device = GSONP.fromJson(deviceMapping, MiIoBasicDevice.class);
|
||||
return device.getDevice().getChannels();
|
||||
if (device != null) {
|
||||
return device.getDevice().getChannels();
|
||||
}
|
||||
} catch (JsonIOException | JsonSyntaxException e) {
|
||||
logger.warn("Error parsing database Json", e);
|
||||
} catch (IOException e) {
|
||||
|
|
|
@ -279,6 +279,9 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
|
|||
|
||||
private boolean updateVacuumStatus(JsonObject statusData) {
|
||||
StatusDTO statusInfo = GSON.fromJson(statusData, StatusDTO.class);
|
||||
if (statusInfo == null) {
|
||||
return false;
|
||||
}
|
||||
safeUpdateState(CHANNEL_BATTERY, statusInfo.getBattery());
|
||||
if (statusInfo.getCleanArea() != null) {
|
||||
updateState(CHANNEL_CLEAN_AREA,
|
||||
|
|
|
@ -66,7 +66,6 @@ public class MiotParser {
|
|||
private static final String BASEURL = "https://miot-spec.org/miot-spec-v2/";
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final boolean SKIP_SIID_1 = true;
|
||||
private static final boolean INCLUDE_MANUAL_ACTIONS_COMMENT = false;
|
||||
|
||||
private String model;
|
||||
private @Nullable String urn;
|
||||
|
@ -98,7 +97,7 @@ public class MiotParser {
|
|||
* @param device
|
||||
* @return
|
||||
*/
|
||||
static public String toJson(MiIoBasicDevice device) {
|
||||
public static String toJson(MiIoBasicDevice device) {
|
||||
String usersJson = GSON.toJson(device);
|
||||
usersJson = usersJson.replace(".0,\n", ",\n");
|
||||
usersJson = usersJson.replace("\n", "\r\n").replace(" ", "\t");
|
||||
|
@ -329,11 +328,6 @@ public class MiotParser {
|
|||
}
|
||||
deviceMapping.setChannels(miIoBasicChannels);
|
||||
device.setDevice(deviceMapping);
|
||||
if (actionText.length() > 35 && INCLUDE_MANUAL_ACTIONS_COMMENT) {
|
||||
deviceMapping.setReadmeComment(
|
||||
"Identified " + actionText.toString().replace("Manual", "manual").replace("\r\n", "<br />")
|
||||
+ "Please test and feedback if they are working so they can be linked to a channel.");
|
||||
}
|
||||
logger.info(channelConfigText.toString());
|
||||
if (actionText.length() > 30) {
|
||||
logger.info("{}", actionText);
|
||||
|
@ -422,6 +416,9 @@ public class MiotParser {
|
|||
.send();
|
||||
JsonElement json = JsonParser.parseString(response.getContentAsString());
|
||||
UrnsDTO data = GSON.fromJson(json, UrnsDTO.class);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
for (ModelUrnsDTO device : data.getInstances()) {
|
||||
if (device.getModel().contentEquals(model)) {
|
||||
this.urn = device.getType();
|
||||
|
@ -433,7 +430,6 @@ public class MiotParser {
|
|||
} catch (JsonParseException e) {
|
||||
logger.debug("Failed parsing downloading models: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<options>
|
||||
<option value="disabled">Local discovery only (Default)</option>
|
||||
<option value="supportedOnly">Discover online supported devices from Xiaomi cloud</option>
|
||||
<option value="all">Discover all online devices from Xiaomi cloud</option>
|
||||
<option value="all">Discover all on & offline devices from Xiaomi cloud (advanced, see readme for usage)</option>
|
||||
</options>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
|
|
@ -46,7 +46,9 @@
|
|||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="cloudServer" type="text" required="false">
|
||||
<label>Xiaomi cloud Server (county code)</label>
|
||||
<label>Cloud Server Country Code</label>
|
||||
<description>Country code (2 characters) of the Xiaomi cloud server. See binding documentation for mapping of the
|
||||
country to cloud server</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:miio:configGatewayDevices">
|
||||
<parameter name="deviceId" type="text" required="true">
|
||||
<label>Device ID</label>
|
||||
<description>Device ID number for communication (in Hex)</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
<parameter name="model" type="text" required="false">
|
||||
<label>Device Model String</label>
|
||||
<description>Device model string, used to determine the subtype.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" min="0" max="9999" required="false">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh interval for refreshing the data in seconds. (0=disabled)</description>
|
||||
<default>30</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="timeout" type="integer" min="1000" max="60000" required="false">
|
||||
<label>Timeout</label>
|
||||
<description>Timeout time in milliseconds</description>
|
||||
<default>15000</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="cloudServer" type="text" required="false">
|
||||
<label>Cloud Server Country Code</label>
|
||||
<description>Country code (2 characters) of the Xiaomi cloud server. See binding documentation for mapping of the
|
||||
country to cloud server</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
|
@ -72,14 +72,33 @@ thing.huayi.light.wyheat = HUIZUO Heating Lamp
|
|||
thing.huayi.light.zw131 = HUIZUO ZIWEI Ceiling Lamp
|
||||
thing.hunmi.cooker.normal3 = MiJia Rice Cooker
|
||||
thing.idelan.aircondition.v1 = Jinxing Smart Air Conditioner
|
||||
thing.ikea.light.led1545g12 = IKEA E27 white spectrum opal
|
||||
thing.ikea.light.led1546g12 = IKEA E27 white spectrum clear
|
||||
thing.ikea.light.led1536g5 = IKEA E14 white spectrum
|
||||
thing.ikea.light.led1537r6 = IKEA GU10 white spectrum
|
||||
thing.ikea.light.led1623g12 = IKEA E27 warm white
|
||||
thing.ikea.light.led1650r5 = IKEA GU10 warm white
|
||||
thing.ikea.light.led1649c5 = IKEA E14 warm white
|
||||
thing.lumi.ctrl_neutral1.v1 = Aqara Wall Switch(No Neutral, Single Rocker)
|
||||
thing.lumi.ctrl_neutral2.v1 = Aqara Wall Switch (No Neutral, Double Rocker)
|
||||
thing.lumi.curtain.hagl05 = Xiaomiyoupin Curtain Controller (Wi-Fi)
|
||||
thing.lumi.gateway.mgl03 = Mi Air Purifier virtual
|
||||
thing.lumi.gateway.mieu01 = Mi smart Home Gateway Hub
|
||||
thing.lumi.gateway.v1 = Mi smart Home Gateway Hub v1
|
||||
thing.lumi.gateway.v2 = Mi smart Home GatewayHub v2
|
||||
thing.lumi.gateway.v3 = Mi smart Home Gateway Hub v3
|
||||
thing.lumi.gateway.mieu01 = Mi smart Home Gateway Hub
|
||||
thing.lumi.light.aqcn02 = Aqara LED Light Bulb (Tunable White)
|
||||
thing.lumi.lock.v1 = Door lock
|
||||
thing.lumi.lock.aq1 = Aqara Door Lock
|
||||
thing.lumi.lock.acn02 = Aqara Door Lock S2
|
||||
thing.lumi.lock.acn03 = Aqara Door lock S2 Pro
|
||||
thing.lumi.plug.mmeu01 = Mi Smart Plug (Zigbee)
|
||||
thing.lumi.sensor_magnet.v2 = Mi Window and Door Sensor
|
||||
thing.lumi.sensor_motion.aq2 = Mi Motion Sensor
|
||||
thing.lumi.sensor_motion.v2 = Mi Motion Sensor
|
||||
thing.lumi.sensor_ht.v1 = Mi Temperature and Humidity Sensor
|
||||
thing.lumi.sensor_wleak.aq1 = Water Leak Sensor
|
||||
thing.lumi.weather.v1 = Aqara Temperature and Humidity Sensor
|
||||
thing.midea.aircondition.v1 = Midea AC-i Youth
|
||||
thing.midea.aircondition.v2 = Midea Air Conditioner v2
|
||||
thing.midea.aircondition.xa1 = Midea AC-Cool Golden
|
||||
|
@ -114,7 +133,7 @@ thing.philips.light.mceils = Zhirui Ceiling Lamp Nordic 28W
|
|||
thing.philips.light.mono1 = Philips Smart Lamp
|
||||
thing.philips.light.moonlight = Philips ZhiRui Bedside Lamp
|
||||
thing.philips.light.obceil = Zhirui Ceiling Lamp Black 80W
|
||||
thing.philips.light.obceim = Zhirui Ceiling Lamp Black 40W
|
||||
thing.philips.light.obceim = Zhirui Ceiling Lamp Black 40W
|
||||
thing.philips.light.obceis = Zhirui Ceiling Lamp Black 28W
|
||||
thing.philips.light.rwread = Mijia Philips Study Desk Lamp
|
||||
thing.philips.light.sceil = Zhirui Ceiling Lamp Starry 80W
|
||||
|
@ -896,6 +915,26 @@ ch.lumi.gateway.mieu01.nightlight = Night Light
|
|||
ch.lumi.gateway.mieu01.rgb = Colored Light
|
||||
ch.lumi.gateway.mieu01.zigbee_channel = Zigbee Channel
|
||||
ch.lumi.gateway.telnetEnable = Enable Telnet
|
||||
ch.lumi.light.aqcn02.brightness = Brightness
|
||||
ch.lumi.light.aqcn02.colour_temperature = Color Temperature
|
||||
ch.lumi.light.aqcn02.power = Power
|
||||
ch.lumi.lock.log = Device Log
|
||||
ch.lumi.lock.status = Status
|
||||
ch.lumi.plug.mmeu01.en_night_tip_light = Led Light
|
||||
ch.lumi.plug.mmeu01.load_power = Load Power
|
||||
ch.lumi.plug.mmeu01.log = Device Log
|
||||
ch.lumi.plug.mmeu01.max_power = Max Power
|
||||
ch.lumi.plug.mmeu01.power = Power
|
||||
ch.lumi.plug.mmeu01.poweroff_memory = Poweroff Memory
|
||||
ch.lumi.sensor_ht.v1.humidity = Humidity
|
||||
ch.lumi.sensor_ht.v1.temperature = Temperature
|
||||
ch.lumi.sensor_magnet.v2.log = Device Log
|
||||
ch.lumi.sensor_motion.v2.log = Device Log
|
||||
ch.lumi.sensor_wleak.aq1.leak = Leaking
|
||||
ch.lumi.sensor_wleak.aq1.log = Device Log
|
||||
ch.lumi.weather.v1.humidity = Humidity
|
||||
ch.lumi.weather.v1.pressure = pressure
|
||||
ch.lumi.weather.v1.temperature = Temperature
|
||||
ch.mijia.vacuum.v2-miot.alarm = Alarm - Alarm
|
||||
ch.mijia.vacuum.v2-miot.battery-level = Battery - Battery Level
|
||||
ch.mijia.vacuum.v2-miot.brush-left-time = Brush Cleaner - Brush Left Time
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miio"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="gateway">
|
||||
<label>Xiaomi Mi Gateway</label>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="network" typeId="network"/>
|
||||
<channel-group id="actions" typeId="basicactions"/>
|
||||
</channel-groups>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">Xiaomi</property>
|
||||
</properties>
|
||||
|
||||
<config-description-ref uri="thing-type:miio:config"/>
|
||||
</bridge-type>
|
||||
|
||||
<channel-group-type id="basicactions">
|
||||
<label>Actions</label>
|
||||
<channels>
|
||||
<channel id="commands" typeId="commands"/>
|
||||
<channel id="rpc" typeId="rpc"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="miio"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="lumi">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Xiaomi Mi Lumi Device</label>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="actions" typeId="basicactions"/>
|
||||
</channel-groups>
|
||||
|
||||
<properties>
|
||||
<property name="vendor">Xiaomi</property>
|
||||
</properties>
|
||||
|
||||
<config-description-ref uri="thing-type:miio:configGatewayDevices"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="basicactions">
|
||||
<label>Actions</label>
|
||||
<channels>
|
||||
<channel id="commands" typeId="commands"/>
|
||||
<channel id="rpc" typeId="rpc"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
|
@ -76,7 +76,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"readmeComment": "Used to control the gateway itself. Use the mihome binding to control devices connected to the Xiaomi gateway.",
|
||||
"readmeComment": "Used to control the gateway itself. Use the mihome binding to control devices connected to the Xiaomi gateway if you have the developer key. Otherwise this binding provides experimental support for lumi subdevices",
|
||||
"experimental": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@
|
|||
"category": "settings"
|
||||
}
|
||||
],
|
||||
"readmeComment": "Used to control the gateway itself. Controlling child devices currently only possible via rules",
|
||||
"readmeComment": "Used to control the gateway itself. Experimental support for controlling lumi subdevices",
|
||||
"experimental": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.light.aqcn02",
|
||||
"ikea.light.led1545g12",
|
||||
"ikea.light.led1546g12",
|
||||
"ikea.light.led1536g5",
|
||||
"ikea.light.led1537r6",
|
||||
"ikea.light.led1623g12",
|
||||
"ikea.light.led1650r5",
|
||||
"ikea.light.led1649c5"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 3,
|
||||
"channels": [
|
||||
{
|
||||
"property": "power_status",
|
||||
"friendlyName": "Power",
|
||||
"channel": "power",
|
||||
"type": "Switch",
|
||||
"refresh": true,
|
||||
"actions": [],
|
||||
"category": "switch",
|
||||
"tags": [
|
||||
"Switch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "light_level",
|
||||
"friendlyName": "Brightness",
|
||||
"channel": "brightness",
|
||||
"type": "Dimmer",
|
||||
"refresh": true,
|
||||
"actions": [],
|
||||
"category": "lightbulb",
|
||||
"tags": [
|
||||
"Setpoint",
|
||||
"Light"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "colour_temperature",
|
||||
"friendlyName": "Color Temperature",
|
||||
"channel": "colour_temperature",
|
||||
"type": "Number",
|
||||
"refresh": true,
|
||||
"actions": [],
|
||||
"category": "colorlight",
|
||||
"tags": [
|
||||
"Setpoint",
|
||||
"Temperature"
|
||||
]
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge.",
|
||||
"experimental": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.lock.v1",
|
||||
"lumi.lock.aq1",
|
||||
"lumi.lock.acn02",
|
||||
"lumi.lock.acn03"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 3,
|
||||
"channels": [
|
||||
{
|
||||
"property": "status",
|
||||
"friendlyName": "Status",
|
||||
"channel": "status",
|
||||
"type": "String",
|
||||
"refresh": true,
|
||||
"actions": [],
|
||||
"category": "lock",
|
||||
"tags": [
|
||||
"Lock"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "log",
|
||||
"friendlyName": "Device Log",
|
||||
"channel": "log",
|
||||
"type": "String",
|
||||
"refresh": true,
|
||||
"customRefreshCommand": "/v2/user/getuserdevicedatatab",
|
||||
"customRefreshParameters": {
|
||||
"limit": 10,
|
||||
"timestamp": "$timestamp$",
|
||||
"did": "$deviceId$",
|
||||
"type": "prop",
|
||||
"key": "device_log"
|
||||
},
|
||||
"transformation": "deviceDataTab",
|
||||
"actions": [],
|
||||
"category": "setting",
|
||||
"tags": [
|
||||
"Point"
|
||||
],
|
||||
"readmeComment": "This channel uses cloud to get data. See widget market place for suitable widget to display the data"
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge.",
|
||||
"experimental": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.plug.mmeu01"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 5,
|
||||
"channels": [
|
||||
{
|
||||
"property": "power",
|
||||
"friendlyName": "Power",
|
||||
"channel": "power",
|
||||
"type": "Switch",
|
||||
"refresh": true,
|
||||
"customRefreshCommand": "{\"sid\":$deviceId$,\"method\":\"get_prop_plug\"} [\"channel_0\"]",
|
||||
"actions": [
|
||||
{
|
||||
"command": "{\"sid\":$deviceId$,\"method\":\"toggle_plug\"}",
|
||||
"parameterType": "ONOFF",
|
||||
"parameters": [
|
||||
"channel_0",
|
||||
"$value$"
|
||||
]
|
||||
}
|
||||
],
|
||||
"category": "switch",
|
||||
"tags": [
|
||||
"Switch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "load_power",
|
||||
"friendlyName": "Load Power",
|
||||
"channel": "load_power",
|
||||
"type": "Number",
|
||||
"refresh": true,
|
||||
"customRefreshCommand": "{\"sid\":$deviceId$,\"method\":\"get_prop_plug\"} [\"load_power\"]",
|
||||
"actions": [],
|
||||
"category": "switch",
|
||||
"tags": [
|
||||
"Measurement"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "en_night_tip_light",
|
||||
"friendlyName": "Led Light",
|
||||
"channel": "en_night_tip_light",
|
||||
"type": "Switch",
|
||||
"refresh": true,
|
||||
"actions": [],
|
||||
"category": "light",
|
||||
"tags": [
|
||||
"Switch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "poweroff_memory",
|
||||
"friendlyName": "Poweroff Memory",
|
||||
"channel": "poweroff_memory",
|
||||
"type": "Switch",
|
||||
"refresh": true,
|
||||
"actions": [
|
||||
{
|
||||
"command": "set_device_prop",
|
||||
"parameterType": "EMPTY",
|
||||
"parameters": [
|
||||
{
|
||||
"sid": "$deviceId$",
|
||||
"poweroff_memory": "$value$"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"category": "setting",
|
||||
"tags": [
|
||||
"Switch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "max_power",
|
||||
"friendlyName": "Max Power",
|
||||
"channel": "max_power",
|
||||
"type": "Number",
|
||||
"refresh": true,
|
||||
"actions": [],
|
||||
"category": "power",
|
||||
"tags": [
|
||||
"Setpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "log",
|
||||
"friendlyName": "Device Log",
|
||||
"channel": "log",
|
||||
"type": "String",
|
||||
"refresh": true,
|
||||
"customRefreshCommand": "/v2/user/getuserdevicedatatab",
|
||||
"customRefreshParameters": {
|
||||
"limit": 10,
|
||||
"timestamp": "$timestamp$",
|
||||
"did": "$deviceId$",
|
||||
"type": "prop",
|
||||
"key": "device_log"
|
||||
},
|
||||
"transformation": "deviceDataTab",
|
||||
"actions": [],
|
||||
"category": "setting",
|
||||
"tags": [
|
||||
"Point"
|
||||
],
|
||||
"readmeComment": "This channel uses cloud to get data. See widget market place for suitable widget to display the data."
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge.",
|
||||
"experimental": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.sensor_ht.v1"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 3,
|
||||
"channels": [
|
||||
{
|
||||
"property": "temperature",
|
||||
"friendlyName": "Temperature",
|
||||
"channel": "temperature",
|
||||
"type": "Number:Temperature",
|
||||
"unit": "CELSIUS",
|
||||
"stateDescription": {
|
||||
"pattern": "%.1f %unit%"
|
||||
},
|
||||
"refresh": true,
|
||||
"transformation": "/100",
|
||||
"actions": [],
|
||||
"category": "temperature",
|
||||
"tags": [
|
||||
"Measurement",
|
||||
"Temperature"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "humidity",
|
||||
"friendlyName": "Humidity",
|
||||
"channel": "humidity",
|
||||
"type": "Number:Dimensionless",
|
||||
"unit": "PERCENT",
|
||||
"stateDescription": {
|
||||
"pattern": "%.0f%%"
|
||||
},
|
||||
"refresh": true,
|
||||
"transformation": "/100",
|
||||
"actions": [],
|
||||
"category": "humidity",
|
||||
"tags": [
|
||||
"Measurement",
|
||||
"Humidity"
|
||||
]
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge.",
|
||||
"experimental": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.sensor_magnet.v2"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 5,
|
||||
"channels": [
|
||||
{
|
||||
"property": "log",
|
||||
"friendlyName": "Device Log",
|
||||
"channel": "log",
|
||||
"type": "String",
|
||||
"refresh": true,
|
||||
"customRefreshCommand": "/v2/user/getuserdevicedatatab",
|
||||
"customRefreshParameters": {
|
||||
"limit": 10,
|
||||
"timestamp": "$timestamp$",
|
||||
"did": "$deviceId$",
|
||||
"type": "prop",
|
||||
"key": "device_log"
|
||||
},
|
||||
"transformation": "deviceDataTab",
|
||||
"actions": [],
|
||||
"category": "setting",
|
||||
"tags": [
|
||||
"Point"
|
||||
],
|
||||
"readmeComment": "This channel uses cloud to get data. See widget market place for suitable widget to display the data."
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge. Note: Won\u0027t display the current status. Log only\u0027",
|
||||
"experimental": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.sensor_motion.v2",
|
||||
"lumi.sensor_motion.aq2"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 5,
|
||||
"channels": [
|
||||
{
|
||||
"property": "log",
|
||||
"friendlyName": "Device Log",
|
||||
"channel": "log",
|
||||
"type": "String",
|
||||
"refresh": true,
|
||||
"customRefreshCommand": "/v2/user/getuserdevicedatatab",
|
||||
"customRefreshParameters": {
|
||||
"limit": 10,
|
||||
"timestamp": "$timestamp$",
|
||||
"did": "$deviceId$",
|
||||
"type": "prop",
|
||||
"key": "device_log"
|
||||
},
|
||||
"transformation": "deviceDataTab",
|
||||
"actions": [],
|
||||
"category": "setting",
|
||||
"tags": [
|
||||
"Point"
|
||||
],
|
||||
"readmeComment": "This channel uses cloud to get data. See widget market place for suitable widget to display the data"
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge.Note: Won\u0027t display the current status, nor trigger events. Log only",
|
||||
"experimental": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.sensor_wleak.aq1"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 5,
|
||||
"channels": [
|
||||
{
|
||||
"property": "leak",
|
||||
"friendlyName": "Leaking",
|
||||
"channel": "leak",
|
||||
"type": "Switch",
|
||||
"refresh": true,
|
||||
"actions": [],
|
||||
"category": "water",
|
||||
"tags": [
|
||||
"Switch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "log",
|
||||
"friendlyName": "Device Log",
|
||||
"channel": "log",
|
||||
"type": "String",
|
||||
"refresh": true,
|
||||
"customRefreshCommand": "/v2/user/getuserdevicedatatab [{\"limit\":10,\"timestamp\": $timestamp$,\"did\":\"$deviceId$\",\"type\":\"prop\",\"key\":\"device_log\"}]",
|
||||
"transformation": "deviceDataTab",
|
||||
"actions": [],
|
||||
"category": "setting",
|
||||
"tags": [
|
||||
"Point"
|
||||
],
|
||||
"readmeComment": "This channel uses cloud to get data. See widget market place for suitable widget to display the data"
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge.",
|
||||
"experimental": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"deviceMapping": {
|
||||
"id": [
|
||||
"lumi.weather.v1"
|
||||
],
|
||||
"propertyMethod": "get_device_prop_exp",
|
||||
"maxProperties": 3,
|
||||
"channels": [
|
||||
{
|
||||
"property": "temperature",
|
||||
"friendlyName": "Temperature",
|
||||
"channel": "temperature",
|
||||
"type": "Number:Temperature",
|
||||
"unit": "CELSIUS",
|
||||
"stateDescription": {
|
||||
"pattern": "%.1f %unit%"
|
||||
},
|
||||
"refresh": true,
|
||||
"transformation": "/100",
|
||||
"actions": [],
|
||||
"category": "temperature",
|
||||
"tags": [
|
||||
"Measurement",
|
||||
"Temperature"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "humidity",
|
||||
"friendlyName": "Humidity",
|
||||
"channel": "humidity",
|
||||
"type": "Number:Dimensionless",
|
||||
"unit": "PERCENT",
|
||||
"stateDescription": {
|
||||
"pattern": "%.0f%%"
|
||||
},
|
||||
"refresh": true,
|
||||
"transformation": "/100",
|
||||
"actions": [],
|
||||
"category": "humidity",
|
||||
"tags": [
|
||||
"Measurement",
|
||||
"Humidity"
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "pressure",
|
||||
"friendlyName": "pressure",
|
||||
"channel": "pressure",
|
||||
"type": "Number:Pressure",
|
||||
"unit": "hPa",
|
||||
"stateDescription": {
|
||||
"pattern": "%.1f %unit%"
|
||||
},
|
||||
"refresh": true,
|
||||
"transformation": "/100",
|
||||
"actions": [],
|
||||
"category": "pressure",
|
||||
"tags": [
|
||||
"Measurement",
|
||||
"Pressure"
|
||||
]
|
||||
}
|
||||
],
|
||||
"readmeComment": "Needs to have the Xiaomi gateway configured in the binding as bridge.",
|
||||
"experimental": false
|
||||
}
|
||||
}
|
|
@ -80,7 +80,6 @@ public class ConversionsTest {
|
|||
|
||||
@Test
|
||||
public void getJsonElementTest() {
|
||||
|
||||
Map<String, Object> deviceVariables = Collections.emptyMap();
|
||||
|
||||
// test invalid missing element
|
||||
|
|
|
@ -28,21 +28,19 @@ import org.openhab.binding.miio.internal.miot.MiIoQuantiyTypesConversion;
|
|||
public class MiIoQuantiyTypesConversionTest {
|
||||
|
||||
@Test
|
||||
public void UnknownUnitTest() {
|
||||
|
||||
public void unknownUnitTest() {
|
||||
String unitName = "some none existent unit";
|
||||
assertNull(MiIoQuantiyTypesConversion.getType(unitName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void NullUnitTest() {
|
||||
public void nullUnitTest() {
|
||||
String unitName = null;
|
||||
assertNull(MiIoQuantiyTypesConversion.getType(unitName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regularsUnitTest() {
|
||||
|
||||
String unitName = "minute";
|
||||
assertEquals("Time", MiIoQuantiyTypesConversion.getType(unitName));
|
||||
|
||||
|
|
|
@ -57,7 +57,6 @@ public class MiotJsonFileCreator {
|
|||
|
||||
@Disabled
|
||||
public static void main(String[] args) {
|
||||
|
||||
LinkedHashMap<String, String> checksums = new LinkedHashMap<>();
|
||||
LinkedHashSet<String> models = new LinkedHashSet<>();
|
||||
if (args.length > 0) {
|
||||
|
|
|
@ -25,11 +25,15 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
@ -38,6 +42,7 @@ import org.openhab.binding.miio.internal.basic.MiIoBasicChannel;
|
|||
import org.openhab.binding.miio.internal.basic.MiIoBasicDevice;
|
||||
import org.openhab.binding.miio.internal.basic.OptionsValueListDTO;
|
||||
import org.openhab.binding.miio.internal.basic.StateDescriptionDTO;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -68,15 +73,19 @@ public class ReadmeHelper {
|
|||
private static final String I18N_CHANNEL_FILE = "./src/main/resources/OH-INF/i18n/basic.properties";
|
||||
private static final boolean UPDATE_OPTION_MAPPING_README_COMMENTS = true;
|
||||
|
||||
public static final Set<ThingTypeUID> DATABASE_THING_TYPES = Collections
|
||||
.unmodifiableSet(Stream.of(MiIoBindingConstants.THING_TYPE_BASIC, MiIoBindingConstants.THING_TYPE_LUMI,
|
||||
MiIoBindingConstants.THING_TYPE_GATEWAY).collect(Collectors.toSet()));
|
||||
|
||||
@Disabled
|
||||
public static void main(String[] args) {
|
||||
ReadmeHelper rm = new ReadmeHelper();
|
||||
LOGGER.info("## Creating device list");
|
||||
StringWriter deviceList = rm.deviceList();
|
||||
rm.checkDatabaseEntrys();
|
||||
LOGGER.info("## Creating channel list for basic devices");
|
||||
LOGGER.info("## Creating channel list for json database driven devices");
|
||||
StringWriter channelList = rm.channelList();
|
||||
LOGGER.info("## Creating Item Files for miio:basic devices");
|
||||
LOGGER.info("## Creating Item Files for json database driven devices");
|
||||
StringWriter itemFileExamples = rm.itemFileExamples();
|
||||
try {
|
||||
String baseDoc = new String(Files.readAllBytes(Paths.get(BASEFILE)), StandardCharsets.UTF_8);
|
||||
|
@ -125,35 +134,37 @@ public class ReadmeHelper {
|
|||
sw.write(devicesCount);
|
||||
sw.write("\n\n");
|
||||
sw.write(
|
||||
"| Device | ThingType | Device Model | Supported | Remark |\n");
|
||||
"| Device | ThingType | Device Model | Supported | Remark |\n");
|
||||
sw.write(
|
||||
"|------------------------------|------------------|------------------------|-----------|------------|\n");
|
||||
"|------------------------------------|------------------|------------------------|--------------|------------|\n");
|
||||
|
||||
Arrays.asList(MiIoDevices.values()).forEach(device -> {
|
||||
if (!device.getModel().equals("unknown")) {
|
||||
String link = device.getModel().replace(".", "-");
|
||||
boolean isSupported = device.getThingType().equals(MiIoBindingConstants.THING_TYPE_UNSUPPORTED);
|
||||
Boolean experimental = false;
|
||||
String remark = "";
|
||||
if (device.getThingType().equals(MiIoBindingConstants.THING_TYPE_BASIC)) {
|
||||
if (DATABASE_THING_TYPES.contains(device.getThingType())) {
|
||||
MiIoBasicDevice dev = findDatabaseEntry(device.getModel());
|
||||
if (dev != null) {
|
||||
remark = dev.getDevice().getReadmeComment();
|
||||
final Boolean experimental = dev.getDevice().getExperimental();
|
||||
if (experimental != null && experimental.booleanValue()) {
|
||||
final Boolean experimentalDev = dev.getDevice().getExperimental();
|
||||
experimental = experimentalDev != null && experimentalDev.booleanValue();
|
||||
if (experimental) {
|
||||
remark += (remark.isBlank() ? "" : "<br />")
|
||||
+ "Experimental support. Please report back if all channels are functional. Preferably share the debug log of property refresh and command responses";
|
||||
}
|
||||
}
|
||||
}
|
||||
sw.write("| ");
|
||||
sw.write(minLengthString(device.getDescription(), 28));
|
||||
sw.write(minLengthString(device.getDescription(), 34));
|
||||
sw.write(" | ");
|
||||
sw.write(minLengthString(device.getThingType().toString(), 16));
|
||||
sw.write(" | ");
|
||||
String model = isSupported ? device.getModel() : "[" + device.getModel() + "](#" + link + ")";
|
||||
sw.write(minLengthString(model, 22));
|
||||
sw.write(" | ");
|
||||
sw.write(isSupported ? "No " : "Yes ");
|
||||
sw.write(isSupported ? "No " : (experimental ? "Experimental" : "Yes "));
|
||||
sw.write(" | ");
|
||||
sw.write(minLengthString(remark, 10));
|
||||
sw.write(" |\n");
|
||||
|
@ -165,7 +176,7 @@ public class ReadmeHelper {
|
|||
private StringWriter channelList() {
|
||||
StringWriter sw = new StringWriter();
|
||||
Arrays.asList(MiIoDevices.values()).forEach(device -> {
|
||||
if (device.getThingType().equals(MiIoBindingConstants.THING_TYPE_BASIC)) {
|
||||
if (DATABASE_THING_TYPES.contains(device.getThingType())) {
|
||||
MiIoBasicDevice dev = findDatabaseEntry(device.getModel());
|
||||
if (dev != null) {
|
||||
String link = device.getModel().replace(".", "-");
|
||||
|
@ -217,7 +228,7 @@ public class ReadmeHelper {
|
|||
private StringWriter itemFileExamples() {
|
||||
StringWriter sw = new StringWriter();
|
||||
Arrays.asList(MiIoDevices.values()).forEach(device -> {
|
||||
if (device.getThingType().equals(MiIoBindingConstants.THING_TYPE_BASIC)) {
|
||||
if (DATABASE_THING_TYPES.contains(device.getThingType())) {
|
||||
MiIoBasicDevice dev = findDatabaseEntry(device.getModel());
|
||||
if (dev != null) {
|
||||
sw.write("### " + device.getDescription() + " (" + device.getModel() + ") item file lines\n\n");
|
||||
|
@ -231,7 +242,8 @@ public class ReadmeHelper {
|
|||
|
||||
for (MiIoBasicChannel ch : dev.getDevice().getChannels()) {
|
||||
sw.write(ch.getType() + " " + ch.getChannel().replace("-", "_") + " \"" + ch.getFriendlyName()
|
||||
+ "\" (" + gr + ") {channel=\"miio:basic:" + id + ":" + ch.getChannel() + "\"}\n");
|
||||
+ "\" (" + gr + ") {channel=\"" + device.getThingType().toString() + ":" + id + ":"
|
||||
+ ch.getChannel() + "\"}\n");
|
||||
}
|
||||
sw.write("```\n\n");
|
||||
}
|
||||
|
@ -242,6 +254,7 @@ public class ReadmeHelper {
|
|||
|
||||
private void checkDatabaseEntrys() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringBuilder commentSb = new StringBuilder("Adding support for the following models:\r\n");
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
HashMap<String, String> names = new HashMap<String, String>();
|
||||
try {
|
||||
|
@ -253,7 +266,12 @@ public class ReadmeHelper {
|
|||
|
||||
for (MiIoBasicDevice entry : findDatabaseEntrys()) {
|
||||
for (String id : entry.getDevice().getId()) {
|
||||
if (!MiIoDevices.getType(id).getThingType().equals(MiIoBindingConstants.THING_TYPE_BASIC)) {
|
||||
if (!DATABASE_THING_TYPES.contains(MiIoDevices.getType(id).getThingType())) {
|
||||
commentSb.append("* ");
|
||||
commentSb.append(names.get(id));
|
||||
commentSb.append(" (modelId: ");
|
||||
commentSb.append(id);
|
||||
commentSb.append(")\r\n");
|
||||
sb.append(id.toUpperCase().replace(".", "_"));
|
||||
sb.append("(\"");
|
||||
sb.append(id);
|
||||
|
@ -267,12 +285,17 @@ public class ReadmeHelper {
|
|||
"id: {} not found in MiIoDevices.java and name unavilable in the device names list.",
|
||||
id);
|
||||
}
|
||||
sb.append("\", THING_TYPE_BASIC),\r\n");
|
||||
sb.append("\", ");
|
||||
sb.append(id.startsWith("lumi.")
|
||||
? (id.startsWith("lumi.gateway") ? "THING_TYPE_GATEWAY" : "THING_TYPE_LUMI")
|
||||
: "THING_TYPE_BASIC");
|
||||
sb.append("),\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
LOGGER.info("Model(s) not found. Suggested lines to add to MiIoDevices.java\r\n{}", sb.toString());
|
||||
LOGGER.info("Model(s) not found. Suggested lines to add to MiIoDevices.java\r\n{}", sb);
|
||||
LOGGER.info("Model(s) not found. Suggested lines to add to the change log\r\n{}", commentSb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,7 +385,7 @@ public class ReadmeHelper {
|
|||
return i18nEntries;
|
||||
}
|
||||
|
||||
public static <K extends Comparable, V> Map<K, V> sortByKeys(Map<K, V> map) {
|
||||
public static <K extends Comparable<?>, V> Map<K, V> sortByKeys(Map<K, V> map) {
|
||||
return new TreeMap<>(map);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue