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";
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;
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.
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 = "";
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 {
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);
if (c != null && c.booleanValue()) {
return true;
}
- deviceListState = DeviceListState.FAILED;
+ deviceListState = CloudListState.FAILED;
return false;
}
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;
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;
public @Nullable CloudDeviceDTO getDeviceInfo(String id) {
getDevicesList();
- if (deviceListState != DeviceListState.AVAILABLE) {
+ if (deviceListState != CloudListState.AVAILABLE) {
return null;
}
List<CloudDeviceDTO> devicedata = new ArrayList<>();
}
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;
+ }
}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
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<>();
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;
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;
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;
/**
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());
- 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.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);
+ updateState(CHANNEL_HISTORY_DURATION, new QuantityType<>(duration, Units.MINUTE));
+ }
+ 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()) {
this.mapDrawOptions = RRMapDrawOptions
.getOptionsFromFile(BINDING_USERDATA_PATH + File.separator + "mapConfig.json", logger);
updateState(RobotCababilities.SEGMENT_CLEAN.getChannel(), new StringType("-"));
+ cloudConnector.getHomeLists();
}
@Override
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);
}
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()));
--- /dev/null
+/**
+ * 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;
+ }
+}
<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>
<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>