mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[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:
parent
09501d59c4
commit
b7ff168b18
@ -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";
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<>();
|
||||
|
@ -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()));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user