| task-switch | Switch | Imilab Timer - Task Switch | |
| countdown-info | Switch | Imilab Timer - Countdown Info | |
| bt-gw | String | BT Gateway | Value mapping `["disable"="Disable","enable"="Enable"]` |
+| bt-gw-devices | String | Connected BT Gateway Devices | Note, refreshes every 2nd refresh. Channel requires cloud connectivity to function. Sample widget to visualise the (json) output available from the widget market |
### Mi Smart Plug WiFi (<a name="chuangmi-plug-hmi205">chuangmi.plug.hmi205</a>) Channels
Switch task_switch "Imilab Timer - Task Switch" (G_plug) {channel="miio:basic:plug:task-switch"}
Switch countdown_info "Imilab Timer - Countdown Info" (G_plug) {channel="miio:basic:plug:countdown-info"}
String bt_gw "BT Gateway" (G_plug) {channel="miio:basic:plug:bt-gw"}
+String bt_gw_devices "Connected BT Gateway Devices" (G_plug) {channel="miio:basic:plug:bt-gw-devices"}
```
### Mi Smart Plug WiFi (chuangmi.plug.hmi205) item file lines
}
public JsonElement getParams() {
- return commandJson.has("params") ? commandJson.get("params").getAsJsonArray() : new JsonArray();
+ return commandJson.has("params") ? commandJson.get("params") : new JsonArray();
}
public JsonObject getResponse() {
* @param RGB + brightness value (note brightness in the first byte)
* @return HSV
*/
- public static JsonElement bRGBtoHSV(JsonElement bRGB) throws ClassCastException {
+ private 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());
* @param map with device variables containing the brightness info
* @return HSV
*/
- public static JsonElement addBrightToHSV(JsonElement rgbValue, @Nullable Map<String, Object> deviceVariables)
+ private static JsonElement addBrightToHSV(JsonElement rgbValue, @Nullable Map<String, Object> deviceVariables)
throws ClassCastException, IllegalStateException {
int bright = 100;
if (deviceVariables != null) {
return rgbValue;
}
- public static JsonElement secondsToHours(JsonElement seconds) throws ClassCastException {
+ private static JsonElement secondsToHours(JsonElement seconds) throws ClassCastException {
double value = seconds.getAsDouble() / 3600;
return new JsonPrimitive(value);
}
- public static JsonElement yeelightSceneConversion(JsonElement intValue)
+ private static JsonElement yeelightSceneConversion(JsonElement intValue)
throws ClassCastException, IllegalStateException {
switch (intValue.getAsInt()) {
case 1:
}
}
- public static JsonElement divideTen(JsonElement value10) throws ClassCastException, IllegalStateException {
+ private static JsonElement divideTen(JsonElement value10) throws ClassCastException, IllegalStateException {
double value = value10.getAsDouble() / 10.0;
return new JsonPrimitive(value);
}
- public static JsonElement divideHundred(JsonElement value10) throws ClassCastException, IllegalStateException {
+ private static JsonElement divideHundred(JsonElement value10) throws ClassCastException, IllegalStateException {
double value = value10.getAsDouble() / 100.0;
return new JsonPrimitive(value);
}
- public static JsonElement tankLevel(JsonElement value12) throws ClassCastException, IllegalStateException {
+ private static JsonElement tankLevel(JsonElement value12) throws ClassCastException, IllegalStateException {
// 127 without water tank. 120 = 100% water
if (value12.getAsInt() == 127) {
return new JsonPrimitive(-1);
}
}
- public static JsonElement getJsonElement(String element, JsonElement responseValue) {
+ /**
+ * Returns the deviceId element value from the Json response. If not found, returns the input
+ *
+ * @param responseValue
+ * @param deviceVariables containing the deviceId
+ * @return
+ */
+ private static JsonElement getDidElement(JsonElement responseValue, Map<String, Object> deviceVariables) {
+ String did = (String) deviceVariables.get("deviceId");
+ if (did != null) {
+ return getJsonElement(did, responseValue);
+ }
+ LOGGER.debug("deviceId not Found, no conversion");
+ return responseValue;
+ }
+
+ /**
+ * Returns the element from the Json response. If not found, returns the input
+ *
+ * @param element to be found
+ * @param responseValue
+ * @return
+ */
+ private static JsonElement getJsonElement(String element, JsonElement responseValue) {
try {
if (responseValue.isJsonPrimitive() || responseValue.isJsonObject()) {
JsonElement jsonElement = responseValue.isJsonObject() ? responseValue
return responseValue;
}
- public static JsonElement execute(String transformation, JsonElement value,
- @Nullable Map<String, Object> deviceVariables) {
+ public static JsonElement execute(String transformation, JsonElement value, Map<String, Object> deviceVariables) {
try {
if (transformation.toUpperCase().startsWith("GETJSONELEMENT")) {
if (transformation.length() > 15) {
return addBrightToHSV(value, deviceVariables);
case "BRGBTOHSV":
return bRGBtoHSV(value);
+ case "GETDIDELEMENT":
+ return getDidElement(value, deviceVariables);
default:
LOGGER.debug("Transformation {} not found. Returning '{}'", transformation, value.toString());
return value;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import com.google.gson.JsonElement;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
@SerializedName("refresh")
@Expose
private @Nullable Boolean refresh;
+ @SerializedName("refreshInterval")
+ @Expose
+ private @Nullable Integer refreshInterval;
@SerializedName("customRefreshCommand")
@Expose
private @Nullable String channelCustomRefreshCommand;
+ @SerializedName("customRefreshParameters")
+ @Expose
+ private @Nullable JsonElement customRefreshParameters;
@SerializedName("transformation")
@Expose
private @Nullable String transformation;
+ @SerializedName("transformations")
+ @Expose
+ private @Nullable List<String> transformations;
@SerializedName("ChannelGroup")
@Expose
private @Nullable String channelGroup;
this.refresh = refresh;
}
+ public Integer getRefreshInterval() {
+ Integer refreshInterval = this.refreshInterval;
+ if (refreshInterval != null) {
+ return refreshInterval;
+ }
+ return 1;
+ }
+
+ public void setRefresh(@Nullable final Integer interval) {
+ final Integer refreshInterval = interval;
+ if (refreshInterval != null && refreshInterval.intValue() != 1) {
+ this.refreshInterval = refreshInterval;
+ } else {
+ this.refreshInterval = null;
+ }
+ }
+
public String getChannelCustomRefreshCommand() {
final @Nullable String channelCustomRefreshCommand = this.channelCustomRefreshCommand;
return channelCustomRefreshCommand != null ? channelCustomRefreshCommand : "";
this.channelCustomRefreshCommand = channelCustomRefreshCommand;
}
+ public @Nullable final JsonElement getCustomRefreshParameters() {
+ return customRefreshParameters;
+ }
+
+ public final void setCustomRefreshParameters(@Nullable JsonElement customRefreshParameters) {
+ this.customRefreshParameters = customRefreshParameters;
+ }
+
public String getChannelGroup() {
final @Nullable String channelGroup = this.channelGroup;
return channelGroup != null ? channelGroup : "";
this.transformation = transformation;
}
+ public final List<String> getTransformations() {
+ List<String> transformations = this.transformations;
+ if (transformations == null) {
+ transformations = new ArrayList<>();
+ }
+ String transformation = this.transformation;
+ if (transformation != null) {
+ List<String> allTransformation = new ArrayList<>(List.of(transformation));
+ allTransformation.addAll(transformations);
+ return allTransformation;
+ }
+ return transformations;
+ }
+
+ public final void setTransformations(@Nullable List<String> transformations) {
+ if (transformations != null && !transformations.isEmpty()) {
+ this.transformations = transformations;
+ } else {
+ this.transformations = null;
+ }
+ }
+
public @Nullable String getCategory() {
return category;
}
private Map<ChannelUID, MiIoBasicChannel> actions = new HashMap<>();
private ChannelTypeRegistry channelTypeRegistry;
private BasicChannelTypeProvider basicChannelTypeProvider;
+ private Map<String, Integer> customRefreshInterval = new HashMap<>();
public MiIoBasicHandler(Thing thing, MiIoDatabaseWatchService miIoDatabaseWatchService,
CloudConnector cloudConnector, ChannelTypeRegistry channelTypeRegistry,
}
}
+ private boolean customRefreshIntervalCheck(MiIoBasicChannel miChannel) {
+ if (miChannel.getRefreshInterval() > 1) {
+ int iteration = customRefreshInterval.getOrDefault(miChannel.getChannel(), 0);
+ if (iteration < 1) {
+ customRefreshInterval.put(miChannel.getChannel(), miChannel.getRefreshInterval() - 1);
+ } else {
+ logger.debug("Skip refresh of channel {} for {}. Next refresh in {} cycles.", miChannel.getChannel(),
+ getThing().getUID(), iteration);
+ customRefreshInterval.put(miChannel.getChannel(), iteration - 1);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean linkedChannelCheck(MiIoBasicChannel miChannel) {
+ if (!isLinked(miChannel.getChannel())) {
+ logger.debug("Skip refresh of channel {} for {} as it is not linked", miChannel.getChannel(),
+ getThing().getUID());
+ return false;
+ }
+ return true;
+ }
+
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());
+ if (customRefreshIntervalCheck(miChannel) || !linkedChannelCheck(miChannel)) {
continue;
}
- String cmd = miChannel.getChannelCustomRefreshCommand();
+ final JsonElement para = miChannel.getCustomRefreshParameters();
+ String cmd = miChannel.getChannelCustomRefreshCommand() + (para != null ? para.toString() : "");
if (!cmd.startsWith("/")) {
- cmds.put(sendCommand(miChannel.getChannelCustomRefreshCommand()), miChannel.getChannel());
+ cmds.put(sendCommand(cmd), miChannel.getChannel());
} else {
if (cloudServer.isBlank()) {
logger.debug("Cloudserver empty. Skipping refresh for {} channel '{}'", getThing().getUID(),
int maxProperties = device.getDevice().getMaxProperties();
JsonArray getPropString = new JsonArray();
for (MiIoBasicChannel miChannel : refreshList) {
- if (!isLinked(miChannel.getChannel())) {
- logger.debug("Skip refresh of channel {} for {} as it is not linked", miChannel.getChannel(),
- getThing().getUID());
+ if (customRefreshIntervalCheck(miChannel) || !linkedChannelCheck(miChannel)) {
continue;
}
JsonElement property;
miIoSendCommand.getCloudServer());
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
} else {
- String data = miIoSendCommand.getParams().isJsonArray()
- && miIoSendCommand.getParams().getAsJsonArray().size() > 0
- ? miIoSendCommand.getParams().getAsJsonArray().get(0).toString()
- : "";
+ String data = miIoSendCommand.getParams().toString();
logger.debug("Custom cloud request send to url '{}' with data '{}'", miIoSendCommand.getMethod(),
data);
decryptedResponse = cloudConnector.sendCloudCommand(miIoSendCommand.getMethod(),
ch.cgllc.airmonitor.s1.temperature = Temperature
ch.cgllc.airmonitor.s1.tvoc = tVOC
ch.chuangmi.plug.212a01-miot.bt-gw = BT Gateway
+ch.chuangmi.plug.212a01-miot.bt-gw-devices = Connected BT Gateway Devices
ch.chuangmi.plug.212a01-miot.countdown = Imilab Timer - Countdown
ch.chuangmi.plug.212a01-miot.countdown-info = Imilab Timer - Countdown Info
ch.chuangmi.plug.212a01-miot.electric-current = Power Consumption - Electric Current
}
}
],
+ "category": "bluetooth",
"readmeComment": "Value mapping `[\"disable\"\u003d\"Disable\",\"enable\"\u003d\"Enable\"]`"
+ },
+ {
+ "property": "",
+ "friendlyName": "Connected BT Gateway Devices",
+ "channel": "bt-gw-devices",
+ "type": "String",
+ "stateDescription": {
+ "readOnly": true
+ },
+ "refresh": true,
+ "refreshInterval": 2,
+ "customRefreshCommand": "/device/get_bledevice_by_gateway",
+ "customRefreshParameters": {
+ "dids": [
+ "$deviceId$"
+ ]
+ },
+ "transformation": "getDiDElement",
+ "actions": [],
+ "category": "bluetooth",
+ "readmeComment": "Note, refreshes every 2nd refresh. Channel requires cloud connectivity to function. Sample widget to visualise the (json) output available from the widget market"
}
],
"experimental": false
import static org.junit.jupiter.api.Assertions.*;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
@NonNullByDefault
public class ConversionsTest {
+ @Test
+ public void getDidElementTest() {
+ Map<String, Object> deviceVariables = new HashMap<>();
+ String transformation = "getDidElement";
+ JsonElement validInput = new JsonPrimitive(
+ "{\"361185596\":\"{\\\"C812105B04000400\\\":\\\"-92\\\",\\\"blt.3.17q3si5345k00\\\":\\\"-54\\\",\\\"blt.4.10heul64og400\\\":\\\"-73\\\"}\"}");
+
+ // test no did in deviceVariables
+ JsonElement value = validInput;
+ JsonElement transformedResponse = Conversions.execute(transformation, value, deviceVariables);
+ assertNotNull(transformedResponse);
+ assertEquals(value, transformedResponse);
+
+ // test valid input & response
+ deviceVariables.put("deviceId", "361185596");
+ value = validInput;
+ transformedResponse = Conversions.execute(transformation, value, deviceVariables);
+ assertNotNull(transformedResponse);
+ assertEquals(new JsonPrimitive(
+ "{\"C812105B04000400\":\"-92\",\"blt.3.17q3si5345k00\":\"-54\",\"blt.4.10heul64og400\":\"-73\"}"),
+ transformedResponse);
+
+ // test non json
+ value = new JsonPrimitive("some non json value");
+ transformedResponse = Conversions.execute(transformation, value, deviceVariables);
+ assertNotNull(transformedResponse);
+ assertEquals(value, transformedResponse);
+
+ // test different did in deviceVariables
+ deviceVariables.put("deviceId", "ABC185596");
+ value = validInput;
+ transformedResponse = Conversions.execute(transformation, value, deviceVariables);
+ assertNotNull(transformedResponse);
+ assertEquals(value, transformedResponse);
+
+ // test empty input
+ value = new JsonPrimitive("");
+ transformedResponse = Conversions.execute(transformation, value, deviceVariables);
+ assertNotNull(transformedResponse);
+ assertEquals(value, transformedResponse);
+ }
+
@Test
public void getJsonElementTest() {
transformation = "getJsonElement-test";
- // test without deviceVariables
- resp = Conversions.execute(transformation, value, null);
- assertNotNull(resp);
- assertEquals(new JsonPrimitive("testresponse"), resp);
-
// test non json
value = new JsonPrimitive("some non json value");
resp = Conversions.execute(transformation, value, deviceVariables);