Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[miio] add miot protocol & conditions #7404

Merged
merged 4 commits into from
Apr 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
380 changes: 290 additions & 90 deletions bundles/org.openhab.binding.miio/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public enum MiIoCommand {
SET_BRIGHT("set_bright"),
SET_WIFI_LET("set_wifi_led"),
SET_FAVORITE("set_level_favorite"),
ACTION("action"),

// vacuum commands
START_VACUUM("app_start"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public enum MiIoDevices {
AIR_PURIFIERMA1("zhimi.airpurifier.ma1", "Mi Air Purifier MS1", THING_TYPE_BASIC),
AIR_PURIFIERMA2("zhimi.airpurifier.ma2", "Mi Air Purifier MS2", THING_TYPE_BASIC),
AIR_PURIFIERMA4("zhimi.airpurifier.ma4", "Mi Air Purifier 3", THING_TYPE_BASIC),
AIR_PURIFIERMMB3("zhimi.airpurifier.mb3", "Mi Air Purifier 3", THING_TYPE_BASIC),
AIR_PURIFIERSA1("zhimi.airpurifier.sa1", "Mi Air Purifier Super", THING_TYPE_BASIC),
AIR_PURIFIERSA2("zhimi.airpurifier.sa2", "Mi Air Purifier Super 2", THING_TYPE_BASIC),
AIRFRESH_T2017("dmaker.airfresh.t2017", "Mi Fresh Air Ventilator", THING_TYPE_BASIC),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Copyright (c) 2010-2020 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.basic;

import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;

/**
* Conditional Execution of rules
*
* @author Marcel Verpaalen - Initial contribution
*/
@NonNullByDefault
public class ActionConditions {
private static final Logger LOGGER = LoggerFactory.getLogger(ActionConditions.class);

/**
* Check if it matches the firmware version.
*
* @param condition
* @param deviceVariables
* @param value
* @return value in case firmware is matching, return null if not
*/
private static @Nullable JsonElement firmwareCheck(MiIoDeviceActionCondition condition,
Map<String, Object> deviceVariables, @Nullable JsonElement value) {
// TODO: placeholder for firmware version check to allow for firmware dependent actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a todo or a comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It indeed is a placeholder for now for something I want to implement in a further version.
It does require still bit of researching on the format of the various firmware version strings Xiaomi is using.

if it is disturbing I can remove the whole function until it is actually used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can leave this as is.

return value;
}

/**
* Check if the value is a valid brightness between 1-99.
* If <1 returns Off, if >99 returns On to activate the power On/Off switch
*
* @param value
* @return
*/
private static @Nullable JsonElement brightness(@Nullable JsonElement value) {
if (value != null && value.isJsonPrimitive() && value.getAsJsonPrimitive().isNumber()) {
int intVal = value.getAsInt();
if (intVal > 99) {
return new JsonPrimitive("on");
}
if (intVal < 1) {
return new JsonPrimitive("off");
}
} else {
LOGGER.debug("Could not parse brightness. Value '{}' is not an int", value);
}
return null;
}

/**
* Check if the value is a valid brightness between 1-99 which can be send to brightness channel.
* If not returns a null
*
* @param value
* @return
*/
private static @Nullable JsonElement brightnessExists(@Nullable JsonElement value) {
if (value != null && value.isJsonPrimitive() && value.getAsJsonPrimitive().isNumber()) {
int intVal = value.getAsInt();
if (intVal > 0 && intVal < 99) {
return value;
}
} else {
LOGGER.debug("Could not parse brightness. Value '{}' is not an int", value);
}
return null;
}

public static @Nullable JsonElement executeAction(MiIoDeviceActionCondition condition,
Map<String, Object> deviceVariables, @Nullable JsonElement value) {
switch (condition.getName().toUpperCase()) {
case "FIRMWARE":
return firmwareCheck(condition, deviceVariables, value);
case "BRIGHTNESSEXISTING":
return brightnessExists(value);
case "BRIGHTNESSONOFF":
return brightness(value);
default:
LOGGER.debug("Condition {} not found. Returning '{}'", condition,
value != null ? value.toString() : "");
return value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public class MiIoBasicChannel {
@SerializedName("property")
@Expose
private @Nullable String property;
@SerializedName("siid")
@Expose
private @Nullable Integer siid;
@SerializedName("piid")
@Expose
private @Nullable Integer piid;
@SerializedName("friendlyName")
@Expose
private @Nullable String friendlyName;
Expand Down Expand Up @@ -69,6 +75,40 @@ public void setProperty(String property) {
this.property = property;
}

public int getSiid() {
final Integer siid = this.siid;
if (siid != null) {
return siid.intValue();
} else {
return 0;
}
}

public void setSiid(Integer siid) {
this.siid = siid;
}

public int getPiid() {
final Integer piid = this.piid;
if (piid != null) {
return piid.intValue();
} else {
return 0;
}
}

public void setPiid(Integer piid) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is non-null by default why not make this a primitive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These siid & piid Integers are only used for miot protocol devices, I use it to determine if it is a miot device by comparing to null in the isMiot function.

in the further functions I don't want to do this checking anymore hence the conversion here to primitive

this.piid = piid;
}

public boolean isMiOt() {
if (piid != null && siid != null && (getPiid() != 0 || getSiid() != 0)) {
return true;
} else {
return false;
}
}

public String getFriendlyName() {
final String fn = friendlyName;
return (fn == null || type == null || fn.isEmpty()) ? getChannel() : fn;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,12 @@ public class MiIoDeviceAction {
@SerializedName("parameterType")
@Expose
private CommandParameterType commandParameterType = CommandParameterType.EMPTY;
@SerializedName("preCommandParameter1")
@SerializedName("parameters")
@Expose
private @Nullable String preCommandParameter1;
@SerializedName("parameter1")
@Expose
private @Nullable String parameter1;
@SerializedName("parameter2")
@Expose
private @Nullable String parameter2;
@SerializedName("parameter3")
@Expose
private @Nullable String parameter3;
private @Nullable JsonArray parameters;
@SerializedName("condition")
@Expose
private @Nullable MiIoDeviceActionCondition condition;

public JsonArray getParameters() {
final @Nullable JsonArray parameter = this.parameters;
Expand Down Expand Up @@ -77,42 +70,17 @@ public void setparameterType(String type) {
this.commandParameterType = org.openhab.binding.miio.internal.basic.CommandParameterType.fromString(type);
}

public @Nullable String getPreCommandParameter1() {
return preCommandParameter1;
}

public void setPreCommandParameter1(String preCommandParameter1) {
this.preCommandParameter1 = preCommandParameter1;
}

public @Nullable String getParameter1() {
return parameter1;
}

public void setParameter1(String parameter1) {
this.parameter1 = parameter1;
}

public @Nullable String getParameter2() {
return parameter2;
}

public void setParameter2(String parameter2) {
this.parameter1 = parameter2;
}

public @Nullable String getParameter3() {
return parameter3;
public @Nullable MiIoDeviceActionCondition getCondition() {
return condition;
}

public void setParameter3(String parameter3) {
this.parameter1 = parameter3;
public void setCondition(@Nullable MiIoDeviceActionCondition condition) {
this.condition = condition;
}

@Override
public String toString() {
return "MiIoDeviceAction [command=" + command + ", commandParameterType=" + commandParameterType
+ ", preCommandParameter1=" + preCommandParameter1 + ", parameter1=" + parameter1 + ", parameter2="
+ parameter2 + ", parameter3=" + parameter3 + "]";
+ (parameters != null ? ", parameters=" + getParameters().toString() : "") + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2020 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.basic;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

/**
* Mapping actions conditions
*
* @author Marcel Verpaalen - Initial contribution
*/
@NonNullByDefault
public class MiIoDeviceActionCondition {

@SerializedName("name")
@Expose
private @Nullable String name;
@SerializedName("parameters")
@Expose
private @Nullable JsonElement parameters;

public String getName() {
final @Nullable String command = this.name;
return command != null ? command : "";
}

public void setName(String command) {
this.name = command;
}

public JsonElement getParameters() {
final JsonElement parameter = this.parameters;
return parameter != null ? parameter : JsonNull.INSTANCE;
}

public void setParameters(JsonArray parameters) {
this.parameters = parameters;
}

@Override
public String toString() {
return "MiIoDeviceActionCondition [condition=" + name + ",parameters=" + getParameters().toString() + "]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public void setCredentials(@Nullable String username, @Nullable String password,

private boolean logon() {
if (username.isEmpty() || password.isEmpty()) {
logger.info("No Xiaomi cloud credentials. Cloud connectivity diabled");
logger.info("No Xiaomi cloud credentials. Cloud connectivity disabled");
logger.debug("Logon details: username: '{}', pass: '{}', country: '{}'", username,
password.replaceAll(".", "*"), country);
return connected;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ private void updateProperties(JsonObject miioInfo) {
if (info.mcuFwVer != null) {
properties.put("mcuFirmware", info.mcuFwVer);
}
deviceVariables.putAll(properties);
updateProperties(properties);
}

Expand Down
Loading