Skip to content

Commit

Permalink
[miio] new gen vacuums cleaning map and rooms (openhab#15675)
Browse files Browse the repository at this point in the history
* Room Mapping from cloud
* New history record

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
  • Loading branch information
marcelrv authored and querdenker2k committed Oct 21, 2023
1 parent dcdf5cd commit 7a64494
Show file tree
Hide file tree
Showing 9 changed files with 886 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@

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;
Expand All @@ -33,7 +36,11 @@
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.
Expand All @@ -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 = "";
Expand All @@ -64,19 +72,22 @@ private enum DeviceListState {
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 {
Expand All @@ -85,10 +96,48 @@ private enum DeviceListState {
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);
Expand Down Expand Up @@ -119,7 +168,7 @@ public boolean isConnected(boolean force) {
if (c != null && c.booleanValue()) {
return true;
}
deviceListState = DeviceListState.FAILED;
deviceListState = CloudListState.FAILED;
return false;
}

Expand All @@ -139,6 +188,21 @@ public String sendCloudCommand(String urlPart, String country, String parameters
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;
Expand Down Expand Up @@ -203,11 +267,11 @@ private boolean logon() {
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;
Expand All @@ -220,7 +284,7 @@ public List<CloudDeviceDTO> getDevicesList() {

public @Nullable CloudDeviceDTO getDeviceInfo(String id) {
getDevicesList();
if (deviceListState != DeviceListState.AVAILABLE) {
if (deviceListState != CloudListState.AVAILABLE) {
return null;
}
List<CloudDeviceDTO> devicedata = new ArrayList<>();
Expand All @@ -243,4 +307,61 @@ public List<CloudDeviceDTO> getDevicesList() {
}
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;
}
}
Loading

0 comments on commit 7a64494

Please sign in to comment.