[miio] change deviceID to Xiaomi used string (#10951)

* [miio] change deviceID to Xiaomi used string

Change the deviceId from the current hexadecimal to the string used by
Xiaomi.
This is needed as we have some devices that have deviceIds that are
non-numeric, hence breaking the current logic.

Note: separately removing the upnp discovery as this has become
irrelevant with cloud discovery and devices supporting the udp regular
discovery.

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>

* Update bundles/org.openhab.binding.miio/src/main/java/org/openhab/binding/miio/internal/handler/MiIoAbstractHandler.java

Signed-off-by: Fabian Wolter <github@fabian-wolter.de>

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
Marcel 2021-07-06 19:42:42 +02:00 committed by GitHub
parent 89f2da140c
commit 7d2db98662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 73 additions and 199 deletions

View File

@ -76,7 +76,7 @@ However, for devices that are unsupported, you may override the value and try to
|-----------------|---------|----------|---------------------------------------------------------------------| |-----------------|---------|----------|---------------------------------------------------------------------|
| host | text | true | Device IP address | | host | text | true | Device IP address |
| token | text | true | Token for communication (in Hex) | | token | text | true | Token for communication (in Hex) |
| deviceId | text | true | Device ID number for communication (in Hex) | | deviceId | text | true | Device Id (typically a number for normal devices) for communication |
| model | text | false | Device model string, used to determine the subtype | | model | text | false | Device model string, used to determine the subtype |
| refreshInterval | integer | false | Refresh interval for refreshing the data in seconds. (0=disabled) | | refreshInterval | integer | false | Refresh interval for refreshing the data in seconds. (0=disabled) |
| timeout | integer | false | Timeout time in milliseconds | | timeout | integer | false | Timeout time in milliseconds |
@ -86,11 +86,11 @@ Note: Suggest to use the cloud communication only for devices that require it. I
### Example Thing file ### Example Thing file
`Thing miio:basic:light "My Light" [ host="192.168.x.x", token="put here your token", deviceId="0326xxxx", model="philips.light.bulb", communication="direct" ]` `Thing miio:basic:light "My Light" [ host="192.168.x.x", token="put here your token", deviceId="326xxxx", model="philips.light.bulb", communication="direct" ]`
or in case of unknown models include the model information of a similar device that is supported: or in case of unknown models include the model information of a similar device that is supported:
`Thing miio:vacuum:s50 "vacuum" @ "livingroom" [ host="192.168.15.20", token="xxxxxxx", deviceId=“0470DDAA”, model="roborock.vacuum.s4", communication="cloud"]` `Thing miio:vacuum:s50 "vacuum" @ "livingroom" [ host="192.168.15.20", token="xxxxxxx", deviceId="326xxxx", model="roborock.vacuum.s4", communication="direct" ]`
# Advanced: Unsupported devices # Advanced: Unsupported devices

View File

@ -76,7 +76,7 @@ However, for devices that are unsupported, you may override the value and try to
|-----------------|---------|----------|---------------------------------------------------------------------| |-----------------|---------|----------|---------------------------------------------------------------------|
| host | text | true | Device IP address | | host | text | true | Device IP address |
| token | text | true | Token for communication (in Hex) | | token | text | true | Token for communication (in Hex) |
| deviceId | text | true | Device ID number for communication (in Hex) | | deviceId | text | true | Device Id (typically a number for normal devices) for communication |
| model | text | false | Device model string, used to determine the subtype | | model | text | false | Device model string, used to determine the subtype |
| refreshInterval | integer | false | Refresh interval for refreshing the data in seconds. (0=disabled) | | refreshInterval | integer | false | Refresh interval for refreshing the data in seconds. (0=disabled) |
| timeout | integer | false | Timeout time in milliseconds | | timeout | integer | false | Timeout time in milliseconds |
@ -86,11 +86,11 @@ Note: Suggest to use the cloud communication only for devices that require it. I
### Example Thing file ### Example Thing file
`Thing miio:basic:light "My Light" [ host="192.168.x.x", token="put here your token", deviceId="0326xxxx", model="philips.light.bulb", communication="direct" ]` `Thing miio:basic:light "My Light" [ host="192.168.x.x", token="put here your token", deviceId="326xxxx", model="philips.light.bulb", communication="direct" ]`
or in case of unknown models include the model information of a similar device that is supported: or in case of unknown models include the model information of a similar device that is supported:
`Thing miio:vacuum:s50 "vacuum" @ "livingroom" [ host="192.168.15.20", token="xxxxxxx", deviceId=“0470DDAA”, model="roborock.vacuum.s4", communication="cloud"]` `Thing miio:vacuum:s50 "vacuum" @ "livingroom" [ host="192.168.15.20", token="xxxxxxx", deviceId="326xxxx", model="roborock.vacuum.s4", communication="direct" ]`
# Advanced: Unsupported devices # Advanced: Unsupported devices

View File

@ -127,4 +127,17 @@ public final class Utils {
} }
return value; return value;
} }
/**
* Formats the deviceId to a hex string if possible. Otherwise returns the id unmodified.
*
* @param did
* @return did
*/
public static String getHexId(String did) {
if (!did.isBlank() && !did.contains(".")) {
return toHEX(did);
}
return did;
}
} }

View File

@ -208,10 +208,9 @@ public class CloudConnector {
if (deviceListState != DeviceListState.AVAILABLE) { if (deviceListState != DeviceListState.AVAILABLE) {
return null; return null;
} }
String did = Long.toString(Long.parseUnsignedLong(id, 16));
List<CloudDeviceDTO> devicedata = new ArrayList<>(); List<CloudDeviceDTO> devicedata = new ArrayList<>();
for (CloudDeviceDTO deviceDetails : deviceList) { for (CloudDeviceDTO deviceDetails : deviceList) {
if (deviceDetails.getDid().contentEquals(did)) { if (deviceDetails.getDid().contentEquals(id)) {
devicedata.add(deviceDetails); devicedata.add(deviceDetails);
} }
} }

View File

@ -184,7 +184,7 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
if (cloudConnector.isConnected()) { if (cloudConnector.isConnected()) {
List<CloudDeviceDTO> dv = cloudConnector.getDevicesList(); List<CloudDeviceDTO> dv = cloudConnector.getDevicesList();
for (CloudDeviceDTO device : dv) { for (CloudDeviceDTO device : dv) {
String id = Utils.toHEX(device.getDid()); String id = device.getDid();
if (cloudDiscoveryMode.contentEquals(SUPPORTED)) { if (cloudDiscoveryMode.contentEquals(SUPPORTED)) {
if (MiIoDevices.getType(device.getModel()).getThingType().equals(THING_TYPE_UNSUPPORTED)) { if (MiIoDevices.getType(device.getModel()).getThingType().equals(THING_TYPE_UNSUPPORTED)) {
logger.warn("Discovered from cloud, but ignored because not supported: {} {}", id, device); logger.warn("Discovered from cloud, but ignored because not supported: {} {}", id, device);
@ -194,7 +194,7 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
logger.debug("Discovered from cloud: {} {}", id, device); logger.debug("Discovered from cloud: {} {}", id, device);
cloudDevices.put(id, device.getLocalip()); cloudDevices.put(id, device.getLocalip());
String token = device.getToken(); String token = device.getToken();
String label = device.getName() + " " + id + " (" + device.getDid() + ")"; String label = device.getName() + " " + id + " (" + Utils.getHexId(id) + ")";
String country = device.getServer(); String country = device.getServer();
boolean isOnline = device.getIsOnline(); boolean isOnline = device.getIsOnline();
String ip = device.getLocalip(); String ip = device.getLocalip();
@ -210,8 +210,9 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
logger.trace("Discovery responses from : {}:{}", ip, Utils.getSpacedHex(response)); logger.trace("Discovery responses from : {}:{}", ip, Utils.getSpacedHex(response));
Message msg = new Message(response); Message msg = new Message(response);
String token = Utils.getHex(msg.getChecksum()); String token = Utils.getHex(msg.getChecksum());
String id = Utils.getHex(msg.getDeviceId()); String hexId = Utils.getHex(msg.getDeviceId());
String label = "Xiaomi Mi Device " + id + " (" + Utils.fromHEX(id) + ")"; String id = Utils.fromHEX(hexId);
String label = "Xiaomi Mi Device " + id + " (" + Utils.getHexId(id) + ")";
String country = ""; String country = "";
boolean isOnline = false; boolean isOnline = false;
if (ip.equals(cloudDevices.get(id))) { if (ip.equals(cloudDevices.get(id))) {
@ -224,7 +225,7 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
if (cloudInfo != null) { if (cloudInfo != null) {
logger.debug("Cloud Info: {}", cloudInfo); logger.debug("Cloud Info: {}", cloudInfo);
token = cloudInfo.getToken(); token = cloudInfo.getToken();
label = cloudInfo.getName() + " " + id + " (" + Utils.fromHEX(id) + ")"; label = cloudInfo.getName() + " " + id + " (" + Utils.getHexId(id) + ")";
country = cloudInfo.getServer(); country = cloudInfo.getServer();
isOnline = cloudInfo.getIsOnline(); isOnline = cloudInfo.getIsOnline();
} }
@ -233,17 +234,17 @@ public class MiIoDiscovery extends AbstractDiscoveryService {
} }
private void submitDiscovery(String ip, String token, String id, String label, String country, boolean isOnline) { private void submitDiscovery(String ip, String token, String id, String label, String country, boolean isOnline) {
ThingUID uid = new ThingUID(THING_TYPE_MIIO, id.replace(".", "_")); ThingUID uid = new ThingUID(THING_TYPE_MIIO, Utils.getHexId(id).replace(".", "_"));
DiscoveryResultBuilder dr = DiscoveryResultBuilder.create(uid).withProperty(PROPERTY_HOST_IP, ip) DiscoveryResultBuilder dr = DiscoveryResultBuilder.create(uid).withProperty(PROPERTY_HOST_IP, ip)
.withProperty(PROPERTY_DID, id); .withProperty(PROPERTY_DID, id);
if (IGNORED_TOKENS.contains(token)) { if (IGNORED_TOKENS.contains(token)) {
logger.debug("Discovered Mi Device {} ({}) at {} as {}", id, Utils.fromHEX(id), ip, uid); logger.debug("Discovered Mi Device {} ({}) at {} as {}", id, Utils.getHexId(id), ip, uid);
logger.debug( logger.debug(
"No token discovered for device {}. For options how to get the token, check the binding readme.", "No token discovered for device {}. For options how to get the token, check the binding readme.",
id); id);
dr = dr.withRepresentationProperty(PROPERTY_DID).withLabel(label); dr = dr.withRepresentationProperty(PROPERTY_DID).withLabel(label);
} else { } else {
logger.debug("Discovered Mi Device {} ({}) at {} as {} with token {}", id, Utils.fromHEX(id), ip, uid, logger.debug("Discovered Mi Device {} ({}) at {} as {} with token {}", id, Utils.getHexId(id), ip, uid,
token); token);
dr = dr.withProperty(PROPERTY_TOKEN, token).withRepresentationProperty(PROPERTY_DID) dr = dr.withProperty(PROPERTY_TOKEN, token).withRepresentationProperty(PROPERTY_DID)
.withLabel(label + " with token"); .withLabel(label + " with token");

View File

@ -1,142 +0,0 @@
/**
* Copyright (c) 2010-2021 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.discovery;
import static org.openhab.binding.miio.internal.MiIoBindingConstants.*;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.jmdns.ServiceInfo;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miio.internal.MiIoDevices;
import org.openhab.binding.miio.internal.cloud.CloudConnector;
import org.openhab.binding.miio.internal.cloud.CloudDeviceDTO;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Discovers Mi IO devices announced by mDNS
*
* @author Marcel Verpaalen - Initial contribution
*
*/
@NonNullByDefault
@Component(service = MDNSDiscoveryParticipant.class)
public class MiIoDiscoveryParticipant implements MDNSDiscoveryParticipant {
private final CloudConnector cloudConnector;
private Logger logger = LoggerFactory.getLogger(MiIoDiscoveryParticipant.class);
@Activate
public MiIoDiscoveryParticipant(@Reference CloudConnector cloudConnector) {
this.cloudConnector = cloudConnector;
logger.debug("Start Xiaomi Mi IO mDNS discovery");
}
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return (NONGENERIC_THING_TYPES_UIDS);
}
@Override
public String getServiceType() {
return "_miio._udp.local.";
}
@Override
public @Nullable ThingUID getThingUID(@Nullable ServiceInfo service) {
if (service == null) {
return null;
}
logger.trace("ServiceInfo: {}", service);
String id[] = service.getName().split("_miio");
if (id.length != 2) {
logger.trace("mDNS Could not identify Type / Device Id from '{}'", service.getName());
return null;
}
long did;
try {
did = Long.parseUnsignedLong(id[1]);
} catch (Exception e) {
logger.trace("mDNS Could not identify Device ID from '{}'", id[1]);
return null;
}
ThingTypeUID thingType = MiIoDevices.getType(id[0].replaceAll("-", ".")).getThingType();
String uidName = String.format("%08X", did);
logger.debug("mDNS {} identified as thingtype {} with did {} ({})", id[0], thingType, uidName, did);
return new ThingUID(thingType, uidName);
}
private @Nullable InetAddress getIpAddress(ServiceInfo service) {
InetAddress address = null;
for (InetAddress addr : service.getInet4Addresses()) {
return addr;
}
// Fallback for Inet6addresses
for (InetAddress addr : service.getInet6Addresses()) {
return addr;
}
return address;
}
@Override
public @Nullable DiscoveryResult createResult(ServiceInfo service) {
DiscoveryResult result = null;
ThingUID uid = getThingUID(service);
if (uid != null) {
Map<String, Object> properties = new HashMap<>(2);
// remove the domain from the name
InetAddress ip = getIpAddress(service);
if (ip == null) {
logger.debug("Mi IO mDNS Discovery could not determine ip address from service info: {}", service);
return null;
}
String inetAddress = ip.toString().substring(1); // trim leading slash
String id = uid.getId();
String label = "Xiaomi Mi Device " + id + " (" + Long.parseUnsignedLong(id, 16) + ") " + service.getName();
if (cloudConnector.isConnected()) {
cloudConnector.getDevicesList();
CloudDeviceDTO cloudInfo = cloudConnector.getDeviceInfo(id);
if (cloudInfo != null) {
logger.debug("Cloud Info: {}", cloudInfo);
properties.put(PROPERTY_TOKEN, cloudInfo.getToken());
label = label + " with token";
String country = cloudInfo.getServer();
if (!country.isEmpty() && cloudInfo.getIsOnline()) {
properties.put(PROPERTY_CLOUDSERVER, country);
}
}
}
properties.put(PROPERTY_HOST_IP, inetAddress);
properties.put(PROPERTY_DID, id);
result = DiscoveryResultBuilder.create(uid).withProperties(properties)
.withRepresentationProperty(PROPERTY_DID).withLabel(label).build();
logger.debug("Mi IO mDNS Discovery found {} with address '{}:{}' name '{}'", uid, inetAddress,
service.getPort(), label);
}
return result;
}
}

View File

@ -351,16 +351,21 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
} }
@Nullable @Nullable
String deviceId = configuration.deviceId; String deviceId = configuration.deviceId;
if (deviceId.length() == 8 && deviceId.matches("^.*[a-zA-Z]+.*$")) {
logger.warn(
"As per openHAB version 3.2 the deviceId is no longer a string with hexadecimals, instead it is a string with the numeric respresentation of the deviceId. If you continue seeing this message, update deviceId in your thing configuration");
deviceId = "";
}
try { try {
if (deviceId.length() == 8 && tokenCheckPass(configuration.token)) { if (!deviceId.isBlank() && tokenCheckPass(configuration.token)) {
final MiIoAsyncCommunication miioCom = new MiIoAsyncCommunication(configuration.host, token, final MiIoAsyncCommunication miioCom = new MiIoAsyncCommunication(configuration.host, token, deviceId,
Utils.hexStringToByteArray(deviceId), lastId, configuration.timeout, cloudConnector); lastId, configuration.timeout, cloudConnector);
if (getCloudServer().isBlank()) { if (getCloudServer().isBlank()) {
logger.debug("Ping Mi device {} at {}", deviceId, configuration.host); logger.debug("Ping Mi deviceId '{}' at {}", deviceId, configuration.host);
Message miIoResponse = miioCom.sendPing(configuration.host); Message miIoResponse = miioCom.sendPing(configuration.host);
if (miIoResponse != null) { if (miIoResponse != null) {
logger.debug("Ping response from device {} at {}. Time stamp: {}, OH time {}, delta {}", logger.debug("Ping response from deviceId '{}' at {}. Time stamp: {}, OH time {}, delta {}",
Utils.getHex(miIoResponse.getDeviceId()), configuration.host, Utils.fromHEX(Utils.getHex(miIoResponse.getDeviceId())), configuration.host,
miIoResponse.getTimestamp(), LocalDateTime.now(), miioCom.getTimeDelta()); miIoResponse.getTimestamp(), LocalDateTime.now(), miioCom.getTimeDelta());
miioCom.registerListener(this); miioCom.registerListener(this);
this.miioCom = miioCom; this.miioCom = miioCom;
@ -374,20 +379,17 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
return miioCom; return miioCom;
} }
} else { } else {
logger.debug("No device ID defined. Retrieving Mi device ID"); logger.debug("No deviceId defined. Retrieving Mi deviceId");
final MiIoAsyncCommunication miioCom = new MiIoAsyncCommunication(configuration.host, token, final MiIoAsyncCommunication miioCom = new MiIoAsyncCommunication(configuration.host, token, "", lastId,
new byte[0], lastId, configuration.timeout, cloudConnector); configuration.timeout, cloudConnector);
Message miIoResponse = miioCom.sendPing(configuration.host); Message miIoResponse = miioCom.sendPing(configuration.host);
if (miIoResponse != null) { if (miIoResponse != null) {
logger.debug("Ping response from device {} at {}. Time stamp: {}, OH time {}, delta {}", deviceId = Utils.fromHEX(Utils.getHex(miIoResponse.getDeviceId()));
Utils.getHex(miIoResponse.getDeviceId()), configuration.host, miIoResponse.getTimestamp(), logger.debug("Ping response from deviceId '{}' at {}. Time stamp: {}, OH time {}, delta {}",
LocalDateTime.now(), miioCom.getTimeDelta()); deviceId, configuration.host, miIoResponse.getTimestamp(), LocalDateTime.now(),
deviceId = Utils.getHex(miIoResponse.getDeviceId());
logger.debug("Ping response from device {} at {}. Time stamp: {}, OH time {}, delta {}", deviceId,
configuration.host, miIoResponse.getTimestamp(), LocalDateTime.now(),
miioCom.getTimeDelta()); miioCom.getTimeDelta());
miioCom.setDeviceId(miIoResponse.getDeviceId()); miioCom.setDeviceId(deviceId);
logger.debug("Using retrieved Mi device ID: {}", deviceId); logger.debug("Using retrieved Mi deviceId: {}", deviceId);
updateDeviceIdConfig(deviceId); updateDeviceIdConfig(deviceId);
miioCom.registerListener(this); miioCom.registerListener(this);
this.miioCom = miioCom; this.miioCom = miioCom;
@ -396,7 +398,7 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
miioCom.close(); miioCom.close();
} }
} }
logger.debug("Ping response from device {} at {} FAILED", configuration.deviceId, configuration.host); logger.debug("Ping response from deviceId '{}' at {} FAILED", configuration.deviceId, configuration.host);
disconnectedNoResponse(); disconnectedNoResponse();
return null; return null;
} catch (IOException e) { } catch (IOException e) {
@ -413,7 +415,7 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
config.put(PROPERTY_DID, deviceId); config.put(PROPERTY_DID, deviceId);
updateConfiguration(config); updateConfiguration(config);
} else { } else {
logger.debug("Could not update config with device ID: {}", deviceId); logger.debug("Could not update config with deviceId: {}", deviceId);
} }
} }
@ -529,10 +531,11 @@ public abstract class MiIoAbstractHandler extends BaseThingHandler implements Mi
@Override @Override
public void onMessageReceived(MiIoSendCommand response) { public void onMessageReceived(MiIoSendCommand response) {
logger.debug("Received response for {} type: {}, result: {}, fullresponse: {}", getThing().getUID().getId(), logger.debug("Received response for device {} type: {}, result: {}, fullresponse: {}",
response.getCommand(), response.getResult(), response.getResponse()); getThing().getUID().getId(), response.getCommand(), response.getResult(), response.getResponse());
if (response.isError()) { if (response.isError()) {
logger.debug("Error received: {}", response.getResponse().get("error")); logger.debug("Error received for command '{}': {}.", response.getCommandString(),
response.getResponse().get("error"));
if (MiIoCommand.MIIO_INFO.equals(response.getCommand())) { if (MiIoCommand.MIIO_INFO.equals(response.getCommand())) {
network.invalidateValue(); network.invalidateValue();
} }

View File

@ -65,7 +65,7 @@ public class MiIoAsyncCommunication {
private final String ip; private final String ip;
private final byte[] token; private final byte[] token;
private byte[] deviceId; private String deviceId;
private @Nullable DatagramSocket socket; private @Nullable DatagramSocket socket;
private List<MiIoMessageListener> listeners = new CopyOnWriteArrayList<>(); private List<MiIoMessageListener> listeners = new CopyOnWriteArrayList<>();
@ -85,7 +85,7 @@ public class MiIoAsyncCommunication {
private ConcurrentLinkedQueue<MiIoSendCommand> concurrentLinkedQueue = new ConcurrentLinkedQueue<>(); private ConcurrentLinkedQueue<MiIoSendCommand> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();
public MiIoAsyncCommunication(String ip, byte[] token, byte[] did, int id, int timeout, public MiIoAsyncCommunication(String ip, byte[] token, String did, int id, int timeout,
CloudConnector cloudConnector) { CloudConnector cloudConnector) {
this.ip = ip; this.ip = ip;
this.token = token; this.token = token;
@ -156,7 +156,7 @@ public class MiIoAsyncCommunication {
// Obfuscate part of the token to allow sharing of the logfiles // Obfuscate part of the token to allow sharing of the logfiles
String tokenText = Utils.obfuscateToken(Utils.getHex(token)); String tokenText = Utils.obfuscateToken(Utils.getHex(token));
logger.debug("Command added to Queue {} -> {} (Device: {} token: {} Queue: {}).{}{}", logger.debug("Command added to Queue {} -> {} (Device: {} token: {} Queue: {}).{}{}",
fullCommand.toString(), ip, Utils.getHex(deviceId), tokenText, concurrentLinkedQueue.size(), fullCommand.toString(), ip, deviceId, tokenText, concurrentLinkedQueue.size(),
cloudServer.isBlank() ? "" : " Send via cloudserver: ", cloudServer); cloudServer.isBlank() ? "" : " Send via cloudserver: ", cloudServer);
} }
if (needPing && cloudServer.isBlank()) { if (needPing && cloudServer.isBlank()) {
@ -165,7 +165,7 @@ public class MiIoAsyncCommunication {
return cmdId; return cmdId;
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.warn("Send command '{}' with parameters {} -> {} (Device: {}) gave error {}", command, params, ip, logger.warn("Send command '{}' with parameters {} -> {} (Device: {}) gave error {}", command, params, ip,
Utils.getHex(deviceId), e.getMessage()); deviceId, e.getMessage());
throw e; throw e;
} }
} }
@ -177,7 +177,7 @@ public class MiIoAsyncCommunication {
if (miIoSendCommand.getCloudServer().isBlank()) { if (miIoSendCommand.getCloudServer().isBlank()) {
decryptedResponse = sendCommand(miIoSendCommand.getCommandString(), token, ip, deviceId); decryptedResponse = sendCommand(miIoSendCommand.getCommandString(), token, ip, deviceId);
} else { } else {
decryptedResponse = cloudConnector.sendRPCCommand(Utils.getHex(deviceId), decryptedResponse = cloudConnector.sendRPCCommand(Utils.getHexId(deviceId),
miIoSendCommand.getCloudServer(), miIoSendCommand); miIoSendCommand.getCloudServer(), miIoSendCommand);
logger.debug("Command {} send via cloudserver {}", miIoSendCommand.getCommandString(), logger.debug("Command {} send via cloudserver {}", miIoSendCommand.getCommandString(),
miIoSendCommand.getCloudServer()); miIoSendCommand.getCloudServer());
@ -216,16 +216,15 @@ public class MiIoAsyncCommunication {
logger.debug("{}: {}", errorMsg, decryptedResponse); logger.debug("{}: {}", errorMsg, decryptedResponse);
} catch (MiIoCryptoException | IOException e) { } catch (MiIoCryptoException | IOException e) {
logger.debug("Send command '{}' -> {} (Device: {}) gave error {}", miIoSendCommand.getCommandString(), ip, logger.debug("Send command '{}' -> {} (Device: {}) gave error {}", miIoSendCommand.getCommandString(), ip,
Utils.getHex(deviceId), e.getMessage()); deviceId, e.getMessage());
errorMsg = e.getMessage(); errorMsg = e.getMessage();
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.warn("Could not parse '{}' <- {} (Device: {}) gave error {}", decryptedResponse, logger.warn("Could not parse '{}' <- {} (Device: {}) gave error {}", decryptedResponse,
miIoSendCommand.getCommandString(), Utils.getHex(deviceId), e.getMessage()); miIoSendCommand.getCommandString(), deviceId, e.getMessage());
errorMsg = "Received message is invalid JSON"; errorMsg = "Received message is invalid JSON";
} catch (MiCloudException e) { } catch (MiCloudException e) {
logger.debug("Send command '{}' -> cloudserver '{}' (Device: {}) gave error {}", logger.debug("Send command '{}' -> cloudserver '{}' (Device: {}) gave error {}",
miIoSendCommand.getCommandString(), miIoSendCommand.getCloudServer(), Utils.getHex(deviceId), miIoSendCommand.getCommandString(), miIoSendCommand.getCloudServer(), deviceId, e.getMessage());
e.getMessage());
errorMsg = e.getMessage(); errorMsg = e.getMessage();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
} }
@ -288,22 +287,23 @@ public class MiIoAsyncCommunication {
} }
} }
private String sendCommand(String command, byte[] token, String ip, byte[] deviceId) private String sendCommand(String command, byte[] token, String ip, String deviceId)
throws MiIoCryptoException, IOException { throws MiIoCryptoException, IOException {
byte[] sendMsg = new byte[0]; byte[] sendMsg = new byte[0];
if (!command.isBlank()) { if (!command.isBlank()) {
byte[] encr; byte[] encr;
encr = MiIoCrypto.encrypt(command.getBytes(StandardCharsets.UTF_8), token); encr = MiIoCrypto.encrypt(command.getBytes(StandardCharsets.UTF_8), token);
timeStamp = (int) Instant.now().getEpochSecond(); timeStamp = (int) Instant.now().getEpochSecond();
sendMsg = Message.createMsgData(encr, token, deviceId, timeStamp + timeDelta); sendMsg = Message.createMsgData(encr, token, Utils.hexStringToByteArray(Utils.getHexId(deviceId)),
timeStamp + timeDelta);
} }
Message miIoResponseMsg = sendData(sendMsg, ip); Message miIoResponseMsg = sendData(sendMsg, ip);
if (miIoResponseMsg == null) { if (miIoResponseMsg == null) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("No response from device {} at {} for command {}.\r\n{}", Utils.getHex(deviceId), ip, logger.trace("No response from device {} at {} for command {}.\r\n{}", deviceId, ip, command,
command, (new Message(sendMsg)).toSting()); (new Message(sendMsg)).toSting());
} else { } else {
logger.debug("No response from device {} at {} for command {}.", Utils.getHex(deviceId), ip, command); logger.debug("No response from device {} at {} for command {}.", deviceId, ip, command);
} }
errorCounter++; errorCounter++;
if (errorCounter > MAX_ERRORS) { if (errorCounter > MAX_ERRORS) {
@ -330,7 +330,7 @@ public class MiIoAsyncCommunication {
public @Nullable Message sendPing(String ip) throws IOException { public @Nullable Message sendPing(String ip) throws IOException {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
logger.debug("Sending Ping {} ({})", Utils.getHex(deviceId), ip); logger.debug("Sending Ping to device '{}' ({})", deviceId, ip);
Message resp = sendData(MiIoBindingConstants.DISCOVER_STRING, ip); Message resp = sendData(MiIoBindingConstants.DISCOVER_STRING, ip);
if (resp != null) { if (resp != null) {
pingSuccess(); pingSuccess();
@ -342,14 +342,14 @@ public class MiIoAsyncCommunication {
} }
private void pingFail() { private void pingFail() {
logger.debug("Ping {} ({}) failed", Utils.getHex(deviceId), ip); logger.debug("Ping to device '{}' ({}) failed", deviceId, ip);
connected = false; connected = false;
status = ThingStatusDetail.COMMUNICATION_ERROR; status = ThingStatusDetail.COMMUNICATION_ERROR;
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
} }
private void pingSuccess() { private void pingSuccess() {
logger.debug("Ping {} ({}) success", Utils.getHex(deviceId), ip); logger.debug("Ping to device '{}' ({}) success", deviceId, ip);
if (!connected) { if (!connected) {
connected = true; connected = true;
status = ThingStatusDetail.NONE; status = ThingStatusDetail.NONE;
@ -476,11 +476,11 @@ public class MiIoAsyncCommunication {
return timeDelta; return timeDelta;
} }
public byte[] getDeviceId() { public String getDeviceId() {
return deviceId; return deviceId;
} }
public void setDeviceId(byte[] deviceId) { public void setDeviceId(String deviceId) {
this.deviceId = deviceId; this.deviceId = deviceId;
} }