[miio] new gen vacuums cleaning map and rooms (#15675)

* Room Mapping from cloud
* New history record

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
This commit is contained in:
Marcel 2023-10-06 08:23:23 +02:00 committed by GitHub
parent 09501d59c4
commit b7ff168b18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 886 additions and 32 deletions

View File

@ -100,6 +100,8 @@ public final class MiIoBindingConstants {
public static final String CHANNEL_HISTORY_DURATION = "cleaning#last_clean_duration";
public static final String CHANNEL_HISTORY_ERROR = "cleaning#last_clean_error";
public static final String CHANNEL_HISTORY_FINISH = "cleaning#last_clean_finish";
public static final String CHANNEL_HISTORY_FINISHREASON = "cleaning#last_clean_finish_reason";
public static final String CHANNEL_HISTORY_DUSTCOLLECTION = "cleaning#last_clean_dustcollection_status";
public static final String CHANNEL_HISTORY_RECORD = "cleaning#last_clean_record";
public static final String CHANNEL_VACUUM_MAP = "cleaning#map";

View File

@ -16,8 +16,11 @@ import static org.openhab.binding.miio.internal.MiIoBindingConstants.BINDING_ID;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
@ -33,7 +36,11 @@ import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
/**
* The {@link CloudConnector} is responsible for connecting OH to the Xiaomi cloud communication.
@ -46,14 +53,15 @@ public class CloudConnector {
private static final long CACHE_EXPIRY = TimeUnit.SECONDS.toMillis(60);
private enum DeviceListState {
private enum CloudListState {
FAILED,
STARTING,
REFRESHING,
AVAILABLE,
}
private volatile DeviceListState deviceListState = DeviceListState.STARTING;
private volatile CloudListState deviceListState = CloudListState.STARTING;
private volatile CloudListState homeListState = CloudListState.STARTING;
private String username = "";
private String password = "";
@ -64,19 +72,22 @@ public class CloudConnector {
private @Nullable MiCloudConnector cloudConnector;
private final Logger logger = LoggerFactory.getLogger(CloudConnector.class);
private ConcurrentHashMap<@NonNull String, @NonNull HomeListDTO> homeLists = new ConcurrentHashMap<>();
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
private ExpiringCache<Boolean> logonCache = new ExpiringCache<Boolean>(CACHE_EXPIRY, () -> {
return logon();
});
private ExpiringCache<String> refreshDeviceList = new ExpiringCache<String>(CACHE_EXPIRY, () -> {
if (deviceListState == DeviceListState.FAILED && !isConnected()) {
if (deviceListState == CloudListState.FAILED && !isConnected()) {
return ("Could not connect to Xiaomi cloud");
}
final @Nullable MiCloudConnector cl = this.cloudConnector;
if (cl == null) {
return ("Could not connect to Xiaomi cloud");
}
deviceListState = DeviceListState.REFRESHING;
deviceListState = CloudListState.REFRESHING;
deviceList.clear();
for (String server : country.split(",")) {
try {
@ -85,10 +96,48 @@ public class CloudConnector {
logger.debug("Parsing error getting devices: {}", e.getMessage());
}
}
deviceListState = DeviceListState.AVAILABLE;
deviceListState = CloudListState.AVAILABLE;
return "done";// deviceList;
});
private ExpiringCache<String> refreshHomeList = new ExpiringCache<String>(CACHE_EXPIRY, () -> {
if (homeListState == CloudListState.FAILED && !isConnected()) {
return ("Could not connect to Xiaomi cloud");
}
final @Nullable MiCloudConnector cl = this.cloudConnector;
if (cl == null) {
return ("Could not connect to Xiaomi cloud");
}
boolean isStarting = homeListState == CloudListState.STARTING;
homeListState = CloudListState.REFRESHING;
for (String server : country.split(",")) {
try {
updateHomeList(server);
} catch (JsonParseException e) {
logger.debug("Parsing error getting home details: {}", e.getMessage());
}
}
homeListState = CloudListState.AVAILABLE;
if (isStarting) {
printHomesandRooms();
}
return "done";// deviceList;
});
private void printHomesandRooms() {
for (Entry<String, HomeListDTO> countryHome : homeLists.entrySet()) {
String server = countryHome.getKey();
final HomeListDTO homelist = countryHome.getValue();
for (HomeDTO home : homelist.getHomelist()) {
logger.debug("Server: {}, Home id: {}, Name {}", server, home.getId(), home.getName());
for (HomeRoomDTO room : home.getRoomlist()) {
logger.debug("Server: {}, Home id: {}, Room id: {}, Name {}", server, home.getId(), room.getId(),
room.getName());
}
}
}
}
@Activate
public CloudConnector(@Reference HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.createHttpClient(BINDING_ID);
@ -119,7 +168,7 @@ public class CloudConnector {
if (c != null && c.booleanValue()) {
return true;
}
deviceListState = DeviceListState.FAILED;
deviceListState = CloudListState.FAILED;
return false;
}
@ -139,6 +188,21 @@ public class CloudConnector {
return cl.request(urlPart.startsWith("/") ? urlPart : "/" + urlPart, country, parameters);
}
private void updateHomeList(String country) {
final @Nullable MiCloudConnector cl = this.cloudConnector;
if (isConnected() && cl != null) {
try {
JsonObject homelistInfo = cl.getHomeList(country.trim().toLowerCase());
final HomeListDTO homelist = GSON.fromJson(homelistInfo, HomeListDTO.class);
if (homelist != null && homelist.getHomelist() != null && homelist.getHomelist().size() > 0) {
homeLists.put(country, homelist);
}
} catch (JsonSyntaxException e) {
logger.debug("Home List / Room info could not be updated for server '{}': {}", country, e.getMessage());
}
}
}
public @Nullable RawType getMap(String mapId, String country) throws MiCloudException {
logger.debug("Getting vacuum map {} from Xiaomi cloud server: '{}'", mapId, country);
String mapCountry;
@ -203,11 +267,11 @@ public class CloudConnector {
if (connected) {
getDevicesList();
} else {
deviceListState = DeviceListState.FAILED;
deviceListState = CloudListState.FAILED;
}
} catch (MiCloudException e) {
connected = false;
deviceListState = DeviceListState.FAILED;
deviceListState = CloudListState.FAILED;
logger.debug("Xiaomi cloud login failed: {}", e.getMessage());
}
return connected;
@ -220,7 +284,7 @@ public class CloudConnector {
public @Nullable CloudDeviceDTO getDeviceInfo(String id) {
getDevicesList();
if (deviceListState != DeviceListState.AVAILABLE) {
if (deviceListState != CloudListState.AVAILABLE) {
return null;
}
List<CloudDeviceDTO> devicedata = new ArrayList<>();
@ -243,4 +307,61 @@ public class CloudConnector {
}
return devicedata.get(0);
}
public HomeListDTO getHomeList(String server) {
refreshHomeList.getValue();
return homeLists.getOrDefault(server, new HomeListDTO());
}
public ConcurrentHashMap<String, HomeListDTO> getHomeLists() {
refreshHomeList.getValue();
return homeLists;
}
/**
* Get the room from the cloud given the room Id and country server
*
* @param room id
* @param country
* @return room
*/
public @Nullable HomeRoomDTO getRoom(String id, String country) {
@Nullable
HomeListDTO homeList = homeLists.getOrDefault(country, new HomeListDTO());
if (homeList.getHomelist() != null) {
for (HomeDTO home : homeList.getHomelist()) {
for (HomeRoomDTO room : home.getRoomlist()) {
if (room.getId().contentEquals(id)) {
return room;
}
}
}
}
return null;
}
/**
* Get the room from the cloud given the room Id
*
* @param room id
* @return room
*/
public @Nullable HomeRoomDTO getRoom(String id) {
return getRoom(id, true);
}
private @Nullable HomeRoomDTO getRoom(String id, boolean retry) {
for (Entry<String, HomeListDTO> countryHome : homeLists.entrySet()) {
HomeRoomDTO room = getRoom(id, countryHome.getKey());
if (room != null) {
return room;
}
}
if (retry) {
refreshHomeList.getValue();
return getRoom(id, false);
}
return null;
}
}

View File

@ -0,0 +1,269 @@
/**
* Copyright (c) 2010-2023 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.cloud;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* This DTO class wraps the home json structure
*
* @author Marcel Verpaalen - Initial contribution
*/
public class HomeDTO {
@SerializedName("id")
@Expose
private String id;
@SerializedName("name")
@Expose
private String name;
@SerializedName("bssid")
@Expose
private String bssid;
@SerializedName("dids")
@Expose
private List<String> dids;
@SerializedName("temp_dids")
@Expose
private Object tempDids;
@SerializedName("icon")
@Expose
private String icon;
@SerializedName("shareflag")
@Expose
private Integer shareflag;
@SerializedName("permit_level")
@Expose
private Integer permitLevel;
@SerializedName("status")
@Expose
private Integer status;
@SerializedName("background")
@Expose
private String background;
@SerializedName("smart_room_background")
@Expose
private String smartRoomBackground;
@SerializedName("longitude")
@Expose
private Integer longitude;
@SerializedName("latitude")
@Expose
private Integer latitude;
@SerializedName("city_id")
@Expose
private Integer cityId;
@SerializedName("address")
@Expose
private String address;
@SerializedName("create_time")
@Expose
private Integer createTime;
@SerializedName("roomlist")
@Expose
private List<HomeRoomDTO> roomlist;
@SerializedName("uid")
@Expose
private Integer uid;
@SerializedName("appear_home_list")
@Expose
private Object appearHomeList;
@SerializedName("popup_flag")
@Expose
private Integer popupFlag;
@SerializedName("popup_time_stamp")
@Expose
private Integer popupTimeStamp;
@SerializedName("car_did")
@Expose
private String carDid;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBssid() {
return bssid;
}
public void setBssid(String bssid) {
this.bssid = bssid;
}
public List<String> getDids() {
return dids;
}
public void setDids(List<String> dids) {
this.dids = dids;
}
public Object getTempDids() {
return tempDids;
}
public void setTempDids(Object tempDids) {
this.tempDids = tempDids;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Integer getShareflag() {
return shareflag;
}
public void setShareflag(Integer shareflag) {
this.shareflag = shareflag;
}
public Integer getPermitLevel() {
return permitLevel;
}
public void setPermitLevel(Integer permitLevel) {
this.permitLevel = permitLevel;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getBackground() {
return background;
}
public void setBackground(String background) {
this.background = background;
}
public String getSmartRoomBackground() {
return smartRoomBackground;
}
public void setSmartRoomBackground(String smartRoomBackground) {
this.smartRoomBackground = smartRoomBackground;
}
public Integer getLongitude() {
return longitude;
}
public void setLongitude(Integer longitude) {
this.longitude = longitude;
}
public Integer getLatitude() {
return latitude;
}
public void setLatitude(Integer latitude) {
this.latitude = latitude;
}
public Integer getCityId() {
return cityId;
}
public void setCityId(Integer cityId) {
this.cityId = cityId;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public List<HomeRoomDTO> getRoomlist() {
return roomlist;
}
public void setRoomlist(List<HomeRoomDTO> roomlist) {
this.roomlist = roomlist;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Object getAppearHomeList() {
return appearHomeList;
}
public void setAppearHomeList(Object appearHomeList) {
this.appearHomeList = appearHomeList;
}
public Integer getPopupFlag() {
return popupFlag;
}
public void setPopupFlag(Integer popupFlag) {
this.popupFlag = popupFlag;
}
public Integer getPopupTimeStamp() {
return popupTimeStamp;
}
public void setPopupTimeStamp(Integer popupTimeStamp) {
this.popupTimeStamp = popupTimeStamp;
}
public String getCarDid() {
return carDid;
}
public void setCarDid(String carDid) {
this.carDid = carDid;
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2023 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.cloud;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* This DTO class wraps the home json structure
*
* @author Marcel Verpaalen - Initial contribution
*/
public class HomeListDTO {
@SerializedName("homelist")
@Expose
private List<HomeDTO> homelist;
@SerializedName("has_more")
@Expose
private Boolean hasMore;
@SerializedName("max_id")
@Expose
private String maxId;
public List<HomeDTO> getHomelist() {
return homelist;
}
public void setHomelist(List<HomeDTO> homelist) {
this.homelist = homelist;
}
public Boolean getHasMore() {
return hasMore;
}
public void setHasMore(Boolean hasMore) {
this.hasMore = hasMore;
}
public String getMaxId() {
return maxId;
}
public void setMaxId(String maxId) {
this.maxId = maxId;
}
}

View File

@ -0,0 +1,126 @@
/**
* Copyright (c) 2010-2023 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.cloud;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* This DTO class wraps the home json structure
*
* @author Marcel Verpaalen - Initial contribution
*/
public class HomeRoomDTO {
@SerializedName("id")
@Expose
private String id;
@SerializedName("name")
@Expose
private String name;
@SerializedName("bssid")
@Expose
private String bssid;
@SerializedName("parentid")
@Expose
private String parentid;
@SerializedName("dids")
@Expose
private List<String> dids;
@SerializedName("icon")
@Expose
private String icon;
@SerializedName("background")
@Expose
private String background;
@SerializedName("shareflag")
@Expose
private Integer shareflag;
@SerializedName("create_time")
@Expose
private Integer createTime;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBssid() {
return bssid;
}
public void setBssid(String bssid) {
this.bssid = bssid;
}
public String getParentid() {
return parentid;
}
public void setParentid(String parentid) {
this.parentid = parentid;
}
public List<String> getDids() {
return dids;
}
public void setDids(List<String> dids) {
this.dids = dids;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getBackground() {
return background;
}
public void setBackground(String background) {
this.background = background;
}
public Integer getShareflag() {
return shareflag;
}
public void setShareflag(Integer shareflag) {
this.shareflag = shareflag;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
}

View File

@ -201,6 +201,25 @@ public class MiCloudConnector {
return request("/home/rpc/" + id, country, command);
}
public JsonObject getHomeList(String country) {
String response = "";
try {
response = request("/homeroom/gethome", country,
"{\"fg\":false,\"fetch_share\":true,\"fetch_share_dev\":true,\"limit\":300,\"app_ver\":7,\"fetch_cariot\":true}");
logger.trace("gethome response: {}", response);
final JsonElement resp = JsonParser.parseString(response);
if (resp.isJsonObject() && resp.getAsJsonObject().has("result")) {
return resp.getAsJsonObject().get("result").getAsJsonObject();
}
} catch (JsonParseException e) {
logger.info("{} error while parsing rooms: '{}'", e.getMessage(), response);
} catch (MiCloudException e) {
logger.info("{}", e.getMessage());
loginFailedCounter++;
}
return new JsonObject();
}
public List<CloudDeviceDTO> getDevices(String country) {
final String response = getDeviceString(country);
List<CloudDeviceDTO> devicesList = new ArrayList<>();

View File

@ -18,6 +18,8 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@ -41,9 +43,11 @@ import org.openhab.binding.miio.internal.MiIoSendCommand;
import org.openhab.binding.miio.internal.basic.MiIoDatabaseWatchService;
import org.openhab.binding.miio.internal.cloud.CloudConnector;
import org.openhab.binding.miio.internal.cloud.CloudUtil;
import org.openhab.binding.miio.internal.cloud.HomeRoomDTO;
import org.openhab.binding.miio.internal.cloud.MiCloudException;
import org.openhab.binding.miio.internal.robot.ConsumablesType;
import org.openhab.binding.miio.internal.robot.FanModeType;
import org.openhab.binding.miio.internal.robot.HistoryRecordDTO;
import org.openhab.binding.miio.internal.robot.RRMapDraw;
import org.openhab.binding.miio.internal.robot.RRMapDrawOptions;
import org.openhab.binding.miio.internal.robot.RobotCababilities;
@ -80,6 +84,7 @@ import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
@ -445,31 +450,113 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
return true;
}
private void updateHistoryRecord(JsonArray historyData) {
ZonedDateTime startTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(historyData.get(0).getAsLong()),
ZoneId.systemDefault());
ZonedDateTime endTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(historyData.get(1).getAsLong()),
ZoneId.systemDefault());
long duration = TimeUnit.SECONDS.toMinutes(historyData.get(2).getAsLong());
double area = historyData.get(3).getAsDouble() / 1000000D;
int error = historyData.get(4).getAsInt();
int finished = historyData.get(5).getAsInt();
private void updateHistoryRecordLegacy(JsonArray historyData) {
HistoryRecordDTO historyRecord = new HistoryRecordDTO();
for (int i = 0; i < historyData.size(); ++i) {
try {
BigInteger value = historyData.get(i).getAsBigInteger();
switch (i) {
case 0:
historyRecord.setStart(ZonedDateTime
.ofInstant(Instant.ofEpochSecond(value.longValue()), ZoneId.systemDefault())
.toString());
break;
case 1:
historyRecord.setStart(ZonedDateTime
.ofInstant(Instant.ofEpochSecond(value.longValue()), ZoneId.systemDefault())
.toString());
break;
case 2:
historyRecord.setDuration(value.intValue());
break;
case 3:
historyRecord.setArea(new BigDecimal(value).divide(BigDecimal.valueOf(1000000)));
break;
case 4:
historyRecord.setError(value.intValue());
break;
case 5:
historyRecord.setFinished(value.intValue());
break;
case 6:
historyRecord.setStartType(value.intValue());
break;
case 7:
historyRecord.setCleanType(value.intValue());
break;
case 8:
historyRecord.setFinishReason(value.intValue());
break;
}
} catch (ClassCastException | NumberFormatException | IllegalStateException e) {
}
}
updateHistoryRecord(historyRecord);
}
private void updateHistoryRecord(HistoryRecordDTO historyRecordDTO) {
JsonObject historyRecord = new JsonObject();
historyRecord.addProperty("start", startTime.toString());
historyRecord.addProperty("end", endTime.toString());
if (historyRecordDTO.getStart() != null) {
historyRecord.addProperty("start", historyRecordDTO.getStart().split("\\+")[0]);
updateState(CHANNEL_HISTORY_START_TIME, new DateTimeType(historyRecordDTO.getStart().split("\\+")[0]));
}
if (historyRecordDTO.getEnd() != null) {
historyRecord.addProperty("end", historyRecordDTO.getEnd().split("\\+")[0]);
updateState(CHANNEL_HISTORY_END_TIME, new DateTimeType(historyRecordDTO.getEnd().split("\\+")[0]));
}
if (historyRecordDTO.getDuration() != null) {
long duration = TimeUnit.SECONDS.toMinutes(historyRecordDTO.getDuration().longValue());
historyRecord.addProperty("duration", duration);
historyRecord.addProperty("area", area);
historyRecord.addProperty("error", error);
historyRecord.addProperty("finished", finished);
updateState(CHANNEL_HISTORY_START_TIME, new DateTimeType(startTime));
updateState(CHANNEL_HISTORY_END_TIME, new DateTimeType(endTime));
updateState(CHANNEL_HISTORY_DURATION, new QuantityType<>(duration, Units.MINUTE));
updateState(CHANNEL_HISTORY_AREA, new QuantityType<>(area, SIUnits.SQUARE_METRE));
updateState(CHANNEL_HISTORY_ERROR, new DecimalType(error));
updateState(CHANNEL_HISTORY_FINISH, new DecimalType(finished));
}
if (historyRecordDTO.getArea() != null) {
historyRecord.addProperty("area", historyRecordDTO.getArea());
updateState(CHANNEL_HISTORY_AREA, new QuantityType<>(historyRecordDTO.getArea(), SIUnits.SQUARE_METRE));
}
if (historyRecordDTO.getError() != null) {
historyRecord.addProperty("error", historyRecordDTO.getError());
updateState(CHANNEL_HISTORY_ERROR, new DecimalType(historyRecordDTO.getError()));
}
if (historyRecordDTO.getFinished() != null) {
historyRecord.addProperty("finished", historyRecordDTO.getFinished());
updateState(CHANNEL_HISTORY_FINISH, new DecimalType(historyRecordDTO.getFinished()));
}
if (historyRecordDTO.getFinishReason() != null) {
historyRecord.addProperty("finish_reason", historyRecordDTO.getFinishReason());
updateState(CHANNEL_HISTORY_FINISHREASON, new DecimalType(historyRecordDTO.getFinishReason()));
}
if (historyRecordDTO.getDustCollectionStatus() != null) {
historyRecord.addProperty("dust_collection_status", historyRecordDTO.getDustCollectionStatus());
updateState(CHANNEL_HISTORY_DUSTCOLLECTION, new DecimalType(historyRecordDTO.getDustCollectionStatus()));
}
updateState(CHANNEL_HISTORY_RECORD, new StringType(historyRecord.toString()));
}
private void updateRoomMapping(MiIoSendCommand response) {
for (RobotCababilities cmd : FEATURES_CHANNELS) {
if (response.getCommand().getCommand().contentEquals(cmd.getCommand())) {
if (response.getResult().isJsonArray()) {
JsonArray rooms = response.getResult().getAsJsonArray();
JsonArray mappedRoom = new JsonArray();
for (JsonElement roomE : rooms) {
JsonArray room = roomE.getAsJsonArray();
HomeRoomDTO name = cloudConnector.getRoom(room.get(1).getAsString());
if (name != null && name.getName() != null) {
room.add(name.getName());
} else {
room.add("not found");
}
mappedRoom.add(room);
}
updateState(cmd.getChannel(), new StringType(mappedRoom.toString()));
} else {
updateState(cmd.getChannel(), new StringType(response.getResult().toString()));
}
break;
}
}
}
@Override
protected boolean skipUpdate() {
if (!hasConnection()) {
@ -525,6 +612,7 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
this.mapDrawOptions = RRMapDrawOptions
.getOptionsFromFile(BINDING_USERDATA_PATH + File.separator + "mapConfig.json", logger);
updateState(RobotCababilities.SEGMENT_CLEAN.getChannel(), new StringType("-"));
cloudConnector.getHomeLists();
}
@Override
@ -570,7 +658,13 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
case CLEAN_RECORD_GET:
if (response.getResult().isJsonArray() && response.getResult().getAsJsonArray().size() > 0
&& response.getResult().getAsJsonArray().get(0).isJsonArray()) {
updateHistoryRecord(response.getResult().getAsJsonArray().get(0).getAsJsonArray());
updateHistoryRecordLegacy(response.getResult().getAsJsonArray().get(0).getAsJsonArray());
} else if (response.getResult().isJsonObject()) {
final HistoryRecordDTO historyRecordDTO = GSON.fromJson(response.getResult().getAsJsonObject(),
HistoryRecordDTO.class);
if (historyRecordDTO != null) {
updateHistoryRecord(historyRecordDTO);
}
} else {
logger.debug("Could not extract cleaning history record from: {}", response);
}
@ -589,11 +683,13 @@ public class MiIoVacuumHandler extends MiIoAbstractHandler {
case GET_LED_STATUS:
updateNumericChannel(response);
break;
case GET_ROOM_MAPPING:
updateRoomMapping(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()));

View File

@ -0,0 +1,148 @@
/**
* Copyright (c) 2010-2023 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.robot;
import java.math.BigDecimal;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* This DTO class wraps the history record message json structure
*
* @author Marcel Verpaalen - Initial contribution
*/
public class HistoryRecordDTO {
@SerializedName("start")
@Expose
private String start;
@SerializedName("end")
@Expose
private String end;
@SerializedName("duration")
@Expose
private Integer duration;
@SerializedName("area")
@Expose
private BigDecimal area;
@SerializedName("clean_time")
@Expose
private Integer cleanTime;
@SerializedName("error")
@Expose
private Integer error;
@SerializedName("finished")
@Expose
private Integer finished;
@SerializedName("start_type")
@Expose
private Integer startType;
@SerializedName("clean_type")
@Expose
private Integer cleanType;
@SerializedName("finish_reason")
@Expose
private Integer finishReason;
@SerializedName("dust_collection_status")
@Expose
private Integer dustCollectionStatus;
public final String getStart() {
return start;
}
public final void setStart(String start) {
this.start = start;
}
public final String getEnd() {
return end;
}
public final void setEnd(String end) {
this.end = end;
}
public final Integer getDuration() {
return duration;
}
public final void setDuration(Integer duration) {
this.duration = duration;
}
public final BigDecimal getArea() {
return area;
}
public final void setArea(BigDecimal area) {
this.area = area;
}
public final Integer getCleanTime() {
return cleanTime;
}
public final void setCleanTime(Integer cleanTime) {
this.cleanTime = cleanTime;
}
public final Integer getError() {
return error;
}
public final void setError(Integer error) {
this.error = error;
}
public final Integer getFinished() {
return finished;
}
public final void setFinished(Integer finished) {
this.finished = finished;
}
public final Integer getStartType() {
return startType;
}
public final void setStartType(Integer startType) {
this.startType = startType;
}
public final Integer getCleanType() {
return cleanType;
}
public final void setCleanType(Integer cleanType) {
this.cleanType = cleanType;
}
public final Integer getFinishReason() {
return finishReason;
}
public final void setFinishReason(Integer finishReason) {
this.finishReason = finishReason;
}
public final Integer getDustCollectionStatus() {
return dustCollectionStatus;
}
public final void setDustCollectionStatus(Integer dustCollectionStatus) {
this.dustCollectionStatus = dustCollectionStatus;
}
}

View File

@ -87,6 +87,8 @@
<channel id="last_clean_duration" typeId="last_clean_duration"/>
<channel id="last_clean_error" typeId="last_clean_error"/>
<channel id="last_clean_finish" typeId="last_clean_finish"/>
<channel id="last_clean_finish_reason" typeId="last_clean_finish_reason"/>
<channel id="last_clean_dustcollection_status" typeId="last_clean_dustcollection_status"/>
<channel id="last_clean_record" typeId="last_clean_record"/>
<channel id="map" typeId="map"/>
</channels>
@ -377,6 +379,16 @@
<label>Cleaning Finished</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="last_clean_finish_reason">
<item-type>Number</item-type>
<label>Cleaning Finished Reason</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="last_clean_dustcollection_status">
<item-type>Number</item-type>
<label>Dust Collection Status</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="last_clean_record" advanced="true">
<item-type>String</item-type>
<label>Cleaning Record</label>