[miio] additional vacuum channels for advanced rules (#10180)

* [miio] add feature channels
* [miio] additional vacuum channels for advanced rules
* [miio] update readme

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
This commit is contained in:
Marcel 2021-02-25 19:22:45 +01:00 committed by GitHub
parent 8928e8520c
commit d1d42c4de9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 17 deletions

View File

@ -245,8 +245,20 @@ Image map "Cleaning Map" (gVacLast) {channel="miio:vacuum:034F0E45:cleaning#map"
Note: cleaning map is only available with cloud access.
Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime
There are several advanced channels, which may be useful in rules (e.g. for individual room cleaning etc)
In case your vacuum does not support one of these commands, it will show "unsupported_method" for string channels or no value for numeric channels.
| Type | Channel | Description |
|---------|-----------------------------------|----------------------------|
| Number | status#segment_status | Segment Status |
| Number | status#map_status | Map Box Status |
| Number | status#led_status | Led Box Status |
| String | info#carpet_mode | Carpet Mode details |
| String | info#fw_features | Firmware Features |
| String | info#room_mapping | Room Mapping details |
| String | info#multi_maps_list | Maps Listing details |
Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime
| Type | Channel | Description |
|---------|-----------------------------------|----------------------------|

View File

@ -2663,7 +2663,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl
|----------------------|----------------------|------------------------------------------|------------|
| power | Switch | Power | |
| brightness | Dimmer | Brightness | |
| ambientBrightness | Number | Ambient Brightness | |
| ambientBrightness | Dimmer | Ambient Brightness | |
| delayoff | Number:Time | Shutdown Timer | |
| colorTemperature | Number | Color Temperature | |
| colorMode | Number | Color Mode | |
@ -2759,7 +2759,7 @@ e.g. `openhab:send actionCommand 'upd_timer["1498595904821", "on"]'` would enabl
|----------------------|----------------------|------------------------------------------|------------|
| power | Switch | Power | |
| brightness | Dimmer | Brightness | |
| ambientBrightness | Number | Ambient Brightness | |
| ambientBrightness | Dimmer | Ambient Brightness | |
| delayoff | Number:Time | Shutdown Timer | |
| colorTemperature | Number | Color Temperature | |
| colorMode | Number | Color Mode | |
@ -4543,8 +4543,20 @@ Image map "Cleaning Map" (gVacLast) {channel="miio:vacuum:034F0E45:cleaning#map"
Note: cleaning map is only available with cloud access.
Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime
There are several advanced channels, which may be useful in rules (e.g. for individual room cleaning etc)
In case your vacuum does not support one of these commands, it will show "unsupported_method" for string channels or no value for numeric channels.
| Type | Channel | Description |
|---------|-----------------------------------|----------------------------|
| Number | status#segment_status | Segment Status |
| Number | status#map_status | Map Box Status |
| Number | status#led_status | Led Box Status |
| String | info#carpet_mode | Carpet Mode details |
| String | info#fw_features | Firmware Features |
| String | info#room_mapping | Room Mapping details |
| String | info#multi_maps_list | Maps Listing details |
Additionally depending on the capabilities of your robot vacuum other channels may be enabled at runtime
| Type | Channel | Description |
|---------|-----------------------------------|----------------------------|
@ -7155,7 +7167,7 @@ note: Autogenerated example. Replace the id (light) in the channel with your own
Group G_light "Yeelight LED Ceiling Light" <status>
Switch power "Power" (G_light) {channel="miio:basic:light:power"}
Dimmer brightness "Brightness" (G_light) {channel="miio:basic:light:brightness"}
Number ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"}
Dimmer ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"}
Number:Time delayoff "Shutdown Timer" (G_light) {channel="miio:basic:light:delayoff"}
Number colorTemperature "Color Temperature" (G_light) {channel="miio:basic:light:colorTemperature"}
Number colorMode "Color Mode" (G_light) {channel="miio:basic:light:colorMode"}
@ -7272,7 +7284,7 @@ note: Autogenerated example. Replace the id (light) in the channel with your own
Group G_light "Yeelight Crystal Pendant Lamp" <status>
Switch power "Power" (G_light) {channel="miio:basic:light:power"}
Dimmer brightness "Brightness" (G_light) {channel="miio:basic:light:brightness"}
Number ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"}
Dimmer ambientBrightness "Ambient Brightness" (G_light) {channel="miio:basic:light:ambientBrightness"}
Number:Time delayoff "Shutdown Timer" (G_light) {channel="miio:basic:light:delayoff"}
Number colorTemperature "Color Temperature" (G_light) {channel="miio:basic:light:colorTemperature"}
Number colorMode "Color Mode" (G_light) {channel="miio:basic:light:colorMode"}

View File

@ -86,6 +86,15 @@ public enum MiIoCommand {
REMOTE_END("app_rc_end"),
REMOTE_MOVE("app_rc_move"),
GET_MAP_STATUS("get_map_status"),
GET_SEGMENT_STATUS("get_segment_status"),
GET_LED_STATUS("get_led_status"),
GET_CARPET_MODE("get_carpet_mode"),
GET_FW_FEATURES("get_fw_features"),
GET_CUSTOMIZED_CLEAN_MODE("get_customize_clean_mode"),
GET_MULTI_MAP_LIST("get_multi_maps_list"),
GET_ROOM_MAPPING("get_room_mapping"),
UNKNOWN("");
private final String command;

View File

@ -22,10 +22,14 @@ import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
@ -89,6 +93,12 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
private final ChannelUID mapChannelUid;
private static final Set<RobotCababilities> FEATURES_CHANNELS = Collections.unmodifiableSet(Stream
.of(RobotCababilities.SEGMENT_STATUS, RobotCababilities.MAP_STATUS, RobotCababilities.LED_STATUS,
RobotCababilities.CARPET_MODE, RobotCababilities.FW_FEATURES, RobotCababilities.ROOM_MAPPING,
RobotCababilities.MULTI_MAP_LIST, RobotCababilities.CUSTOMIZE_CLEAN_MODE)
.collect(Collectors.toSet()));
private ExpiringCache<String> status;
private ExpiringCache<String> consumables;
private ExpiringCache<String> dnd;
@ -289,6 +299,7 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
safeUpdateState(CHANNEL_IN_CLEANING, statusInfo.getInCleaning());
safeUpdateState(CHANNEL_MAP_PRESENT, statusInfo.getMapPresent());
if (statusInfo.getState() != null) {
stateId = statusInfo.getState();
StatusType state = StatusType.getType(statusInfo.getState());
updateState(CHANNEL_STATE, new StringType(state.getDescription()));
updateState(CHANNEL_STATE_ID, new DecimalType(statusInfo.getState()));
@ -464,6 +475,11 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
map.getValue();
}
}
for (RobotCababilities cmd : FEATURES_CHANNELS) {
if (isLinked(cmd.getChannel())) {
sendCommand(cmd.getCommand());
}
}
} catch (Exception e) {
logger.debug("Error while updating '{}': '{}", getThing().getUID().toString(), e.getLocalizedMessage());
}
@ -530,11 +546,54 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
}
}
break;
case GET_MAP_STATUS:
case GET_SEGMENT_STATUS:
case GET_LED_STATUS:
updateNumericChannel(response);
break;
case GET_CARPET_MODE:
case GET_FW_FEATURES:
case GET_CUSTOMIZED_CLEAN_MODE:
case GET_MULTI_MAP_LIST:
case GET_ROOM_MAPPING:
for (RobotCababilities cmd : FEATURES_CHANNELS) {
if (response.getCommand().getCommand().contentEquals(cmd.getCommand())) {
updateState(cmd.getChannel(), new StringType(response.getResult().toString()));
break;
}
}
break;
default:
break;
}
}
private void updateNumericChannel(MiIoSendCommand response) {
RobotCababilities capabilityChannel = null;
for (RobotCababilities cmd : FEATURES_CHANNELS) {
if (response.getCommand().getCommand().contentEquals(cmd.getCommand())) {
capabilityChannel = cmd;
break;
}
}
if (capabilityChannel != null) {
if (response.getResult().isJsonArray() && response.getResult().getAsJsonArray().get(0).isJsonPrimitive()) {
try {
Integer stat = response.getResult().getAsJsonArray().get(0).getAsInt();
updateState(capabilityChannel.getChannel(), new DecimalType(stat));
return;
} catch (ClassCastException | IllegalStateException e) {
logger.debug("Could not update numeric channel {} with '{}': {}", capabilityChannel.getChannel(),
response.getResult(), e.getMessage());
}
} else {
logger.debug("Could not update numeric channel {} with '{}': Not in expected format",
capabilityChannel.getChannel(), response.getResult());
}
updateState(capabilityChannel.getChannel(), UnDefType.UNDEF);
}
}
private void setCapabilities(JsonObject statusResponse) {
for (RobotCababilities capability : RobotCababilities.values()) {
if (statusResponse.has(capability.getStatusFieldName())) {

View File

@ -23,23 +23,33 @@ import org.openhab.core.thing.type.ChannelTypeUID;
@NonNullByDefault
public enum RobotCababilities {
WATERBOX_STATUS("water_box_status", "status#water_box_status", "miio:water_box_status"),
LOCKSTATUS("lock_status", "status#lock_status", "miio:lock_status"),
WATERBOX_MODE("water_box_mode", "status#water_box_mode", "miio:water_box_mode"),
WATERBOX_CARRIAGE("water_box_carriage_status", "status#water_box_carriage_status",
"miio:water_box_carriage_status"),
MOP_FORBIDDEN("mop_forbidden_enable", "status#mop_forbidden_enable", "miio:mop_forbidden_enable"),
LOCATING("is_locating", "status#is_locating", "miio:is_locating"),
SEGMENT_CLEAN("", "actions#segment", "miio:segment");
WATERBOX_STATUS("water_box_status", "status#water_box_status", "miio:water_box_status", ""),
LOCKSTATUS("lock_status", "status#lock_status", "miio:lock_status", ""),
WATERBOX_MODE("water_box_mode", "status#water_box_mode", "miio:water_box_mode", ""),
WATERBOX_CARRIAGE("water_box_carriage_status", "status#water_box_carriage_status", "miio:water_box_carriage_status",
""),
MOP_FORBIDDEN("mop_forbidden_enable", "status#mop_forbidden_enable", "miio:mop_forbidden_enable", ""),
LOCATING("is_locating", "status#is_locating", "miio:is_locating", ""),
SEGMENT_STATUS("", "status#segment_status", "miio:segment_status", "get_segment_status"),
MAP_STATUS("", "status#map_status", "miio:map_status", "get_map_status"),
LED_STATUS("", "status#led_status", "miio:led_status", "get_led_status"),
CARPET_MODE("", "info#carpet_mode", "miio:carpet_mode", "get_carpet_mode"),
FW_FEATURES("", "info#fw_features", "miio:fw_features", "get_fw_features"),
ROOM_MAPPING("", "info#room_mapping", "miio:room_mapping", "get_room_mapping"),
MULTI_MAP_LIST("", "info#multi_maps_list", "miio:multi_maps_list", "get_multi_maps_list"),
CUSTOMIZE_CLEAN_MODE("", "info#customize_clean_mode", "miio:customize_clean_mode", "get_customize_clean_mode"),
SEGMENT_CLEAN("", "actions#segment", "miio:segment", "");
private final String statusFieldName;
private final String channel;
private final String channelType;
private final String command;
RobotCababilities(String statusKey, String channel, String channelType) {
RobotCababilities(String statusKey, String channel, String channelType, String command) {
this.statusFieldName = statusKey;
this.channel = channel;
this.channelType = channelType;
this.command = command;
}
public String getStatusFieldName() {
@ -54,9 +64,14 @@ public enum RobotCababilities {
return new ChannelTypeUID(channelType);
}
public String getCommand() {
return command;
}
@Override
public String toString() {
return String.format("Capability %s: status field name: '%s', channel: '%s', channeltype: '%s'.", this.name(),
statusFieldName, channel, channelType);
return String.format("Capability %s: status field name: '%s', channel: '%s', channeltype: '%s'%s%s.",
this.name(), statusFieldName, channel, channelType, command.isBlank() ? "" : ", custom command: ",
command);
}
}

View File

@ -15,6 +15,7 @@
<channel-group id="history" typeId="history"/>
<channel-group id="cleaning" typeId="cleaning"/>
<channel-group id="network" typeId="network"/>
<channel-group id="info" typeId="info"/>
</channel-groups>
<properties>
@ -50,6 +51,9 @@
<channel id="map_present" typeId="map_present"/>
<channel id="state" typeId="state"/>
<channel id="state_id" typeId="state_id"/>
<channel id="segment_status" typeId="segment_status"/>
<channel id="map_status" typeId="map_status"/>
<channel id="led_status" typeId="led_status"/>
</channels>
</channel-group-type>
<channel-group-type id="consumables">
@ -95,6 +99,16 @@
<channel id="total_clean_count" typeId="total_clean_count"/>
</channels>
</channel-group-type>
<channel-group-type id="info">
<label>Info</label>
<channels>
<channel id="multi_maps_list" typeId="multi_maps_list"/>
<channel id="room_mapping" typeId="room_mapping"/>
<channel id="fw_features" typeId="fw_features"/>
<channel id="customize_clean_mode" typeId="customize_clean_mode"/>
<channel id="carpet_mode" typeId="carpet_mode"/>
</channels>
</channel-group-type>
<!-- Status channels -->
<channel-type id="clean_area">
@ -152,6 +166,21 @@
<label>State ID</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="segment_status" advanced="true">
<item-type>Number</item-type>
<label>Segment Status</label>
<state pattern="%.0f" readOnly="true"/>
</channel-type>
<channel-type id="map_status" advanced="true">
<item-type>Number</item-type>
<label>Map Status</label>
<state pattern="%.0f" readOnly="true"/>
</channel-type>
<channel-type id="led_status" advanced="true">
<item-type>Number</item-type>
<label>Led Status</label>
<state pattern="%.0f" readOnly="true"/>
</channel-type>
<!-- Dynamic Status Channels -->
<channel-type id="water_box_mode">
@ -358,4 +387,29 @@
<label>Cleaning Map</label>
<state readOnly="true"/>
</channel-type>
<!-- Info channels -->
<channel-type id="fw_features" advanced="true">
<item-type>String</item-type>
<label>Firmware Features</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="room_mapping" advanced="true">
<item-type>String</item-type>
<label>Room Mapping</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="multi_maps_list" advanced="true">
<item-type>String</item-type>
<label>Multi Map List</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="carpet_mode" advanced="true">
<item-type>String</item-type>
<label>Carpet Mode</label>
</channel-type>
<channel-type id="customize_clean_mode" advanced="true">
<item-type>String</item-type>
<label>Customized Clean Mode</label>
</channel-type>
</thing:thing-descriptions>