[miio] Allow for quantity Type (#8756)

* [miio] Allow for quantity Type in the basic handler

This extends the miio:basic handler with the option to define channels
with QuantityType e.g. to use number:temperature

* [miio] add missing breaks

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
This commit is contained in:
Marcel 2020-11-09 17:39:50 +01:00 committed by GitHub
parent 9f22e64ffd
commit 918408eac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 153 additions and 13 deletions

View File

@ -3122,13 +3122,13 @@ e.g. `smarthome:send actionCommand 'upd_timer["1498595904821", "on"]'` would ena
| Channel | Type | Description | Comment | | Channel | Type | Description | Comment |
|------------------|---------|-------------------------------------|------------| |------------------|---------|-------------------------------------|------------|
| power | Switch | Power | | | power | Switch | Power | |
| target_temperature | Number | Target Temperature | | | target_temperature | Number:Temperature | Target Temperature | |
| brightness | Number | Brightness | | | brightness | Number | Brightness | |
| buzzer | Switch | Buzzer Status | | | buzzer | Switch | Buzzer Status | |
| relative_humidity | Number | Relative Humidity | | | relative_humidity | Number | Relative Humidity | |
| childlock | Switch | Child Lock | | | childlock | Switch | Child Lock | |
| HWSwitch | Switch | HW Switch | | | HWSwitch | Switch | HW Switch | |
| temperature | Number | Temperature | | | temperature | Number:Temperature | Temperature | |
| usedhours | Number | Run Time | | | usedhours | Number | Run Time | |
@ -6429,13 +6429,13 @@ note: Autogenerated example. Replace the id (heater) in the channel with your ow
```java ```java
Group G_heater "Zhimi Heater" <status> Group G_heater "Zhimi Heater" <status>
Switch power "Power" (G_heater) {channel="miio:basic:heater:power"} Switch power "Power" (G_heater) {channel="miio:basic:heater:power"}
Number target_temperature "Target Temperature" (G_heater) {channel="miio:basic:heater:target_temperature"} Number:Temperature target_temperature "Target Temperature" (G_heater) {channel="miio:basic:heater:target_temperature"}
Number brightness "Brightness" (G_heater) {channel="miio:basic:heater:brightness"} Number brightness "Brightness" (G_heater) {channel="miio:basic:heater:brightness"}
Switch buzzer "Buzzer Status" (G_heater) {channel="miio:basic:heater:buzzer"} Switch buzzer "Buzzer Status" (G_heater) {channel="miio:basic:heater:buzzer"}
Number relative_humidity "Relative Humidity" (G_heater) {channel="miio:basic:heater:relative_humidity"} Number relative_humidity "Relative Humidity" (G_heater) {channel="miio:basic:heater:relative_humidity"}
Switch childlock "Child Lock" (G_heater) {channel="miio:basic:heater:childlock"} Switch childlock "Child Lock" (G_heater) {channel="miio:basic:heater:childlock"}
Switch HWSwitch "HW Switch" (G_heater) {channel="miio:basic:heater:HWSwitch"} Switch HWSwitch "HW Switch" (G_heater) {channel="miio:basic:heater:HWSwitch"}
Number temperature "Temperature" (G_heater) {channel="miio:basic:heater:temperature"} Number:Temperature temperature "Temperature" (G_heater) {channel="miio:basic:heater:temperature"}
Number usedhours "Run Time" (G_heater) {channel="miio:basic:heater:usedhours"} Number usedhours "Run Time" (G_heater) {channel="miio:basic:heater:usedhours"}
``` ```

View File

@ -0,0 +1,60 @@
/**
* 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;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
/**
* Enum of the units used in the miio protocol
* Used to find the right {@link javax.measure.Unit} given the string of the unit
*
* @author Marcel Verpaalen - Initial contribution
*/
@NonNullByDefault
public enum MiIoQuantiyTypes {
CELCIUS(SIUnits.CELSIUS),
FAHRENHEIT(ImperialUnits.FAHRENHEIT),
SECOND(SmartHomeUnits.SECOND),
MINUTE(SmartHomeUnits.MINUTE),
HOUR(SmartHomeUnits.HOUR),
AMPERE(SmartHomeUnits.AMPERE),
WATT(SmartHomeUnits.WATT);
private final Unit<?> unit;
private static Map<String, Unit<?>> stringMap = Arrays.stream(values())
.collect(Collectors.toMap(Enum::toString, MiIoQuantiyTypes::getUnit));
private MiIoQuantiyTypes(Unit<?> unit) {
this.unit = unit;
}
public Unit<?> getUnit() {
return unit;
}
public static @Nullable Unit<?> get(String unitName) {
return stringMap.get(unitName.toUpperCase());
}
}

View File

@ -53,6 +53,9 @@ public class MiIoBasicChannel {
@SerializedName("type") @SerializedName("type")
@Expose @Expose
private @Nullable String type; private @Nullable String type;
@SerializedName("unit")
@Expose
private @Nullable String unit;
@SerializedName("refresh") @SerializedName("refresh")
@Expose @Expose
private @Nullable Boolean refresh; private @Nullable Boolean refresh;
@ -155,6 +158,15 @@ public class MiIoBasicChannel {
this.type = type; this.type = type;
} }
public String getUnit() {
final @Nullable String unit = this.unit;
return unit != null ? unit : "";
}
public void setUnit(String unit) {
this.unit = unit;
}
public Boolean getRefresh() { public Boolean getRefresh() {
final @Nullable Boolean rf = refresh; final @Nullable Boolean rf = refresh;
return rf != null && rf.booleanValue() && !getProperty().isEmpty(); return rf != null && rf.booleanValue() && !getProperty().isEmpty();

View File

@ -23,11 +23,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.measure.Unit;
import javax.measure.format.ParserException;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miio.internal.MiIoBindingConfiguration; import org.openhab.binding.miio.internal.MiIoBindingConfiguration;
import org.openhab.binding.miio.internal.MiIoCommand; import org.openhab.binding.miio.internal.MiIoCommand;
import org.openhab.binding.miio.internal.MiIoCryptoException; import org.openhab.binding.miio.internal.MiIoCryptoException;
import org.openhab.binding.miio.internal.MiIoQuantiyTypes;
import org.openhab.binding.miio.internal.MiIoSendCommand; import org.openhab.binding.miio.internal.MiIoSendCommand;
import org.openhab.binding.miio.internal.Utils; import org.openhab.binding.miio.internal.Utils;
import org.openhab.binding.miio.internal.basic.ActionConditions; import org.openhab.binding.miio.internal.basic.ActionConditions;
@ -44,7 +48,10 @@ import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -105,7 +112,8 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
} }
@Override @Override
public void handleCommand(ChannelUID channelUID, Command command) { public void handleCommand(ChannelUID channelUID, Command receivedCommand) {
Command command = receivedCommand;
if (command == RefreshType.REFRESH) { if (command == RefreshType.REFRESH) {
if (updateDataCache.isExpired()) { if (updateDataCache.isExpired()) {
logger.debug("Refreshing {}", channelUID); logger.debug("Refreshing {}", channelUID);
@ -119,7 +127,7 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
cmds.put(sendCommand(command.toString()), command.toString()); cmds.put(sendCommand(command.toString()), command.toString());
return; return;
} }
logger.debug("Locating action for channel '{}': '{}'", channelUID.getId(), command); logger.debug("Locating action for {} channel '{}': '{}'", getThing().getUID(), channelUID.getId(), command);
if (!actions.isEmpty()) { if (!actions.isEmpty()) {
if (actions.containsKey(channelUID)) { if (actions.containsKey(channelUID)) {
int valuePos = 0; int valuePos = 0;
@ -137,6 +145,25 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
} }
String cmd = action.getCommand(); String cmd = action.getCommand();
CommandParameterType paramType = action.getparameterType(); CommandParameterType paramType = action.getparameterType();
if (command instanceof QuantityType) {
QuantityType<?> qtc = null;
try {
if (!miIoBasicChannel.getUnit().isBlank()) {
Unit<?> unit = MiIoQuantiyTypes.get(miIoBasicChannel.getUnit());
if (unit != null) {
qtc = ((QuantityType<?>) command).toUnit(unit);
}
}
} catch (ParserException e) {
// swallow
}
if (qtc != null) {
command = new DecimalType(qtc.toBigDecimal());
} else {
logger.debug("Could not convert QuantityType to '{}'", miIoBasicChannel.getUnit());
command = new DecimalType(((QuantityType<?>) command).toBigDecimal());
}
}
if (paramType == CommandParameterType.COLOR) { if (paramType == CommandParameterType.COLOR) {
if (command instanceof HSBType) { if (command instanceof HSBType) {
HSBType hsb = (HSBType) command; HSBType hsb = (HSBType) command;
@ -505,9 +532,10 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
val = transformed; val = transformed;
} }
try { try {
switch (basicChannel.getType().toLowerCase()) { String[] chType = basicChannel.getType().toLowerCase().split(":");
switch (chType[0]) {
case "number": case "number":
updateState(basicChannel.getChannel(), new DecimalType(val.getAsBigDecimal())); quantityTypeUpdate(basicChannel, val, chType.length > 1 ? chType[1] : "");
break; break;
case "dimmer": case "dimmer":
updateState(basicChannel.getChannel(), new PercentType(val.getAsBigDecimal())); updateState(basicChannel.getChannel(), new PercentType(val.getAsBigDecimal()));
@ -534,6 +562,39 @@ public class MiIoBasicHandler extends MiIoAbstractHandler {
} }
} }
private void quantityTypeUpdate(MiIoBasicChannel basicChannel, JsonElement val, String type) {
if (!basicChannel.getUnit().isBlank()) {
Unit<?> unit = MiIoQuantiyTypes.get(basicChannel.getUnit());
if (unit != null) {
logger.debug("'{}' channel '{}' has unit '{}' with symbol '{}'.", getThing().getUID(),
basicChannel.getChannel(), basicChannel.getUnit(), unit);
updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), unit));
} else {
logger.debug("Unit '{}' used by '{}' channel '{}' is not found.. using default unit.",
getThing().getUID(), basicChannel.getUnit(), basicChannel.getChannel());
}
}
// if no unit is provided or unit not found use default units, these units have so far been seen for miio
// devices
switch (type.toLowerCase()) {
case "temperature":
updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), SIUnits.CELSIUS));
break;
case "electriccurrent":
updateState(basicChannel.getChannel(),
new QuantityType<>(val.getAsBigDecimal(), SmartHomeUnits.AMPERE));
break;
case "energy":
updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), SmartHomeUnits.WATT));
break;
case "time":
updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), SmartHomeUnits.HOUR));
break;
default:
updateState(basicChannel.getChannel(), new DecimalType(val.getAsBigDecimal()));
}
}
@Override @Override
public void onMessageReceived(MiIoSendCommand response) { public void onMessageReceived(MiIoSendCommand response) {
super.onMessageReceived(response); super.onMessageReceived(response);

View File

@ -154,6 +154,11 @@
<label>Temperature</label> <label>Temperature</label>
<state pattern="%.1f" readOnly="true"/> <state pattern="%.1f" readOnly="true"/>
</channel-type> </channel-type>
<channel-type id="temperatureC">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="humidity"> <channel-type id="humidity">
<item-type>Number</item-type> <item-type>Number</item-type>
<label>Humidity</label> <label>Humidity</label>

View File

@ -42,8 +42,9 @@
"property": "temperature", "property": "temperature",
"friendlyName": "Temperature", "friendlyName": "Temperature",
"channel": "temperature", "channel": "temperature",
"channelType": "temperature", "channelType": "temperatureC",
"type": "Number", "unit": "CELCIUS",
"type": "Number:Temperature",
"refresh": true, "refresh": true,
"ChannelGroup": "", "ChannelGroup": "",
"actions": [] "actions": []

View File

@ -24,7 +24,8 @@
"property": "target_temperature", "property": "target_temperature",
"friendlyName": "Target Temperature", "friendlyName": "Target Temperature",
"channel": "target_temperature", "channel": "target_temperature",
"type": "Number", "type": "Number:Temperature",
"channelType": "temperatureC",
"refresh": true, "refresh": true,
"actions": [ "actions": [
{ {
@ -100,8 +101,8 @@
"property": "temperature", "property": "temperature",
"friendlyName": "Temperature", "friendlyName": "Temperature",
"channel": "temperature", "channel": "temperature",
"channelType": "temperature", "channelType": "temperatureC",
"type": "Number", "type": "Number:Temperature",
"refresh": true, "refresh": true,
"actions": [] "actions": []
}, },