Changes to get separate Connection Manager working

After initial changes, tested various scenarios and made changes so they are working like before.

Signed-off-by: Bob Eckhoff <katmandodo@yahoo.com>
This commit is contained in:
Bob Eckhoff 2024-11-10 11:15:10 -05:00
parent a2159bed2a
commit 99ed3d5000
12 changed files with 188 additions and 144 deletions

View File

@ -41,7 +41,7 @@ No binding configuration is required.
| pollingTime | Yes | Polling time in seconds. Minimum time is 30 seconds. | 60 |
| timeout | Yes | Connecting timeout. Minimum time is 2 second, maximum 10 seconds. | 4 |
| promptTone | Yes | "Ding" tone when command is received and executed. | False |
| version | Yes | Version 3 has token, key and cloud requirements. | 3 |
| version | Yes | Version 3 has token, key and cloud requirements. | 0 |
## Channels

View File

@ -85,8 +85,8 @@ public class MideaACBindingConstants {
public static final String CONFIG_POLLING_TIME = "pollingTime";
public static final String CONFIG_CONNECTING_TIMEOUT = "timeout";
public static final String CONFIG_PROMPT_TONE = "promptTone";
public static final String CONFIG_VERSION = "version";
public static final String PROPERTY_VERSION = "version";
public static final String PROPERTY_SN = "sn";
public static final String PROPERTY_SSID = "ssid";
public static final String PROPERTY_TYPE = "type";

View File

@ -27,7 +27,7 @@ public class MideaACConfiguration {
public int ipPort = 6444;
public String deviceId = "";
public String deviceId = "0";
public String email = "";
@ -43,7 +43,7 @@ public class MideaACConfiguration {
public int timeout = 4;
public boolean promptTone;
public boolean promptTone = false;
public int version = 0;
@ -53,7 +53,8 @@ public class MideaACConfiguration {
* @return true(valid), false (not valid)
*/
public boolean isValid() {
return !("0".equalsIgnoreCase(deviceId) || deviceId.isBlank() || ipPort <= 0 || ipAddress.isBlank());
return !("0".equalsIgnoreCase(deviceId) || deviceId.isBlank() || ipPort <= 0 || ipAddress.isBlank()
|| version <= 1);
}
/**
@ -63,7 +64,7 @@ public class MideaACConfiguration {
*/
public boolean isDiscoveryNeeded() {
return ("0".equalsIgnoreCase(deviceId) || deviceId.isBlank() || ipPort <= 0 || ipAddress.isBlank()
|| !Utils.validateIP(ipAddress));
|| !Utils.validateIP(ipAddress) || version <= 1);
}
/**

View File

@ -135,7 +135,7 @@ public class CommandHelper {
if (command instanceof DecimalType decimalCommand) {
logger.debug("Handle Target Temperature as DecimalType in degrees C");
commandSet.setTargetTemperature(limitTargetTemperatureToRange(decimalCommand.floatValue()));
} else if (command instanceof QuantityType quantityCommand) {
} else if (command instanceof QuantityType<?> quantityCommand) {
if (quantityCommand.getUnit().equals(ImperialUnits.FAHRENHEIT)) {
quantityCommand = Objects.requireNonNull(quantityCommand.toUnit(SIUnits.CELSIUS));
}

View File

@ -66,17 +66,14 @@ public class ConnectionManager {
private Security security;
private final int version;
private final boolean promptTone;
private boolean deviceIsConnected;
private int droppedCommands = 0;
/**
* True allows one short retry after connection problem
*/
private boolean retry = true;
/**
* Suppresses the connection message if was online before
*/
private boolean connectionMessage = true;
public ConnectionManager(String ipAddress, int ipPort, int timeout, String key, String token, String cloud,
String email, String password, String deviceId, int version, boolean promptTone) {
this.deviceIsConnected = false;
@ -95,9 +92,6 @@ public class ConnectionManager {
this.security = new Security(cloudProvider);
}
private boolean deviceIsConnected;
private int droppedCommands = 0;
private Socket socket = new Socket();
private InputStream inputStream = new ByteArrayInputStream(new byte[0]);
private DataOutputStream writer = new DataOutputStream(System.out);
@ -121,25 +115,6 @@ public class ConnectionManager {
return str.trim().isEmpty();
}
/**
* Reset dropped commands from initialization in MideaACHandler
* Channel created for easy observation
* Dropped commands when no bytes to read after two tries or other
* byte reading problem. Device not responding.
*/
public void resetDroppedCommands() {
droppedCommands = 0;
}
/**
* Resets Dropped command
*
* @return dropped commands
*/
public int getDroppedCommands() {
return droppedCommands = 0;
}
/**
* After checking if the key and token need to be updated (Default = 0 Never)
* The socket is established with the writer and inputStream (for reading responses)
@ -155,17 +130,28 @@ public class ConnectionManager {
socket.setSoTimeout(timeout * 1000);
socket.connect(new InetSocketAddress(ipAddress, ipPort), timeout * 1000);
} catch (IOException e) {
logger.debug("IOException connecting to {}: {}", ipAddress, e.getMessage());
deviceIsConnected = false;
// Retry addresses most common wifi connection problems- wait 5 seconds and try again
if (retry) {
logger.debug("Retrying Socket, IOException connecting to {}: {}", ipAddress, e.getMessage());
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
logger.debug("An interupted error (pause) has occured {}", ex.getMessage());
logger.debug("An interupted error (socket retry) has occured {}", ex.getMessage());
}
connect();
retry = false;
try {
socket = new Socket();
socket.setSoTimeout(timeout * 1000);
socket.connect(new InetSocketAddress(ipAddress, ipPort), timeout * 1000);
} catch (IOException e2) {
deviceIsConnected = false;
logger.debug("Second try IOException connecting to {}: {}", ipAddress, e2.getMessage());
throw new MideaConnectionException(e2);
}
} else {
deviceIsConnected = false;
throw new MideaConnectionException(e);
}
throw new MideaConnectionException(e);
}
// Create streams
@ -177,16 +163,9 @@ public class ConnectionManager {
deviceIsConnected = false;
throw new MideaConnectionException(e);
}
if (!deviceIsConnected || !connectionMessage) {
logger.info("Connected to IP {}", ipAddress);
resetConnectionMessage();
}
logger.debug("Connected to IP {}", ipAddress);
deviceIsConnected = true;
resetRetry();
if (version == 3) {
logger.debug("Device {} require authentication, going to authenticate", ipAddress);
logger.debug("Device at IP: {} requires authentication, going to authenticate", ipAddress);
try {
authenticate();
} catch (MideaAuthenticationException | MideaConnectionException e) {
@ -194,8 +173,13 @@ public class ConnectionManager {
throw e;
}
}
// requestStatus(getDoPoll());
if (!deviceIsConnected) {
logger.info("Connected to IP {}", ipAddress);
}
logger.debug("Connected to IP {}", ipAddress);
deviceIsConnected = true;
retry = true;
}
/**
@ -212,7 +196,7 @@ public class ConnectionManager {
logger.trace("Cloud {}", cloud);
if (!isBlank(token) && !isBlank(key) && !"".equals(cloud)) {
logger.debug("Device {} authenticating", ipAddress);
logger.debug("Device at IP: {} authenticating", ipAddress);
doV3Handshake();
} else {
throw new MideaAuthenticationException("Token, Key and / or cloud provider missing");
@ -228,13 +212,13 @@ public class ConnectionManager {
private void doV3Handshake() throws MideaConnectionException, MideaAuthenticationException {
byte[] request = security.encode8370(Utils.hexStringToByteArray(token), MsgType.MSGTYPE_HANDSHAKE_REQUEST);
try {
logger.trace("Device {} writing handshake_request: {}", ipAddress, Utils.bytesToHex(request));
logger.trace("Device at IP: {} writing handshake_request: {}", ipAddress, Utils.bytesToHex(request));
write(request);
byte[] response = read();
if (response != null && response.length > 0) {
logger.trace("Device {} response for handshake_request length: {}", ipAddress, response.length);
logger.trace("Device at IP: {} response for handshake_request length:{}", ipAddress, response.length);
if (response.length == 72) {
boolean success = security.tcpKey(Arrays.copyOfRange(response, 8, 72),
Utils.hexStringToByteArray(key));
@ -247,7 +231,6 @@ public class ConnectionManager {
} catch (InterruptedException e) {
logger.debug("An interupted error (success) has occured {}", e.getMessage());
}
// requestStatus(getDoPoll()); need to handle
} else {
throw new MideaAuthenticationException("Invalid Key. Correct Key in configuration.");
}
@ -255,7 +238,7 @@ public class ConnectionManager {
throw new MideaAuthenticationException("Authentication failed!");
} else {
logger.warn("Authentication reponse unexpected data length ({} instead of 72)!", response.length);
throw new MideaAuthenticationException("Invalid Key. Correct Key in configuration.");
throw new MideaAuthenticationException("Unexpected authentication response length");
}
}
} catch (IOException e) {
@ -290,7 +273,7 @@ public class ConnectionManager {
* Normal device response in 0.75 - 1 second range
* If still empty, send the bytes again. If there are bytes, the read method is called.
* If the socket times out with no response the command is dropped. There will be another poll
* in the time set by the user (30 seconds min) or the set command can be retried
* in the time set by the user (30 seconds min). A Set command will need to be resent.
*
* @param command either the set or polling command
* @throws MideaAuthenticationException
@ -325,7 +308,7 @@ public class ConnectionManager {
} catch (InterruptedException e) {
logger.debug("An interupted error (retrycommand2) has occured {}", e.getMessage());
Thread.currentThread().interrupt();
// Note, but continue anyway. Command will be dropped
// Note, but continue anyway for second write.
}
if (inputStream.available() == 0) {
@ -340,7 +323,7 @@ public class ConnectionManager {
if (version == 3) {
Decryption8370Result result = security.decode8370(responseBytes);
for (byte[] response : result.getResponses()) {
logger.debug("Response length:{} IP address:{} ", response.length, ipAddress);
logger.debug("Response length: {} IP address: {} ", response.length, ipAddress);
if (response.length > 40 + 16) {
byte[] data = security.aesDecrypt(Arrays.copyOfRange(response, 40, response.length - 16));
@ -383,12 +366,12 @@ public class ConnectionManager {
default:
logger.debug("Invalid response type: {}", data[0x9]);
}
logger.trace("Response Type: {} and bodyType:{}", responseType, bodyType2);
logger.trace("Response Type: {} and bodyType: {}", responseType, bodyType2);
// The response data from the appliance includes a packet header which we don't want
data = Arrays.copyOfRange(data, 10, data.length);
byte bodyType = data[0x0];
logger.trace("Response Type expected: {} and bodyType:{}", responseType, bodyType);
logger.trace("Response Type expected: {} and bodyType: {}", responseType, bodyType);
logger.trace("Bytes in HEX, decoded and stripped without header: length: {}, data: {}",
data.length, Utils.bytesToHex(data));
logger.debug("Bytes in BINARY, decoded and stripped without header: length: {}, data: {}",
@ -401,7 +384,7 @@ public class ConnectionManager {
}
if (bodyType != -64) {
if (bodyType == 30) {
logger.warn("Error response 0x1E received {} from IP Address {}", bodyType,
logger.warn("Error response 0x1E received {} from IP Address:{}", bodyType,
ipAddress);
return;
}
@ -410,7 +393,7 @@ public class ConnectionManager {
}
lastResponse = new Response(data, version, responseType, bodyType);
try {
logger.trace("data length is {} version is {} IP address is {}", data.length,
logger.trace("Data length is {}, version is {}, IP address is {}", data.length,
version, ipAddress);
if (callback != null) {
callback.updateChannels(lastResponse);
@ -432,42 +415,42 @@ public class ConnectionManager {
Utils.bytesToHex(data));
lastResponse = new Response(data, version, "", (byte) 0x00);
logger.debug("V2 data length is {} version is {} Ip Address is {}", data.length, version,
logger.debug("Data length is {}, version is {}, Ip Address is {}", data.length, version,
ipAddress);
if (callback != null) {
callback.updateChannels(lastResponse);
}
} else {
droppedCommands = droppedCommands + 1;
logger.debug("Problem with reading V2 response, skipping command {} dropped count{}", command,
logger.debug("Problem with reading V2 response, skipping {} skipped count since startup {}", command,
droppedCommands);
}
}
return;
} else {
droppedCommands = droppedCommands + 1;
logger.debug("Problem with reading response, skipping command {} dropped count{}", command,
logger.debug("Problem with reading response, skipping {} skipped count since startup {}", command,
droppedCommands);
return;
}
} catch (SocketException e) {
logger.debug("SocketException writing to {}: {}", ipAddress, e.getMessage());
droppedCommands = droppedCommands + 1;
logger.debug("Socket exception, skipping command {} dropped count{}", command, droppedCommands);
logger.debug("Socket exception on IP: {}, skipping command {} skipped count since startup {}", ipAddress, command,
droppedCommands);
throw new MideaConnectionException(e);
} catch (IOException e) {
logger.debug(" Send IOException writing to {}: {}", ipAddress, e.getMessage());
droppedCommands = droppedCommands + 1;
logger.debug("Socket exception, skipping command {} dropped count{}", command, droppedCommands);
logger.debug("IO exception on IP: {}, skipping command {} skipped count since startup {}", ipAddress, command,
droppedCommands);
throw new MideaConnectionException(e);
}
}
/**
* Closes all elements of the connection before starting a new one
* Makes sure writer, inputStream and socket are closed before each command is started
*/
public synchronized void disconnect() {
// Make sure writer, inputStream and socket are closed before each command is started
logger.debug("Disconnecting from device at {}", ipAddress);
InputStream inputStream = this.inputStream;
@ -498,7 +481,7 @@ public class ConnectionManager {
try {
int len = inputStream.read(bytes);
if (len > 0) {
logger.debug("Response received length: {} Device IP {}", len, ipAddress);
logger.debug("Response received length: {} from device at IP: {}", len, ipAddress);
bytes = Arrays.copyOfRange(bytes, 0, len);
return bytes;
}
@ -527,25 +510,7 @@ public class ConnectionManager {
}
/**
* Reset Retry controls the short 5 second delay
* Before starting 30 second delays. (More severe Wifi issue)
* It is reset after a successful connection
*/
private void resetRetry() {
retry = true;
}
/**
* Limit logging of INFO connection messages to
* only when the device was Offline in its prior
* state
*/
private void resetConnectionMessage() {
connectionMessage = true;
}
/**
* Disconnects from the device
* Disconnects from the AC device
*
* @param force
*/

View File

@ -70,7 +70,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService {
private Security security;
/**
* Discovery Service
* Discovery Service Uses the default decryption for all devices
*/
public MideaACDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, discoveryTimeoutSeconds, false);
@ -152,7 +152,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService {
}
}
} catch (SocketTimeoutException e) {
logger.debug("Discovering poller timeout...");
logger.trace("Discovering poller timeout...");
} catch (IOException e) {
logger.debug("Error during discovery: {}", e.getMessage());
} finally {
@ -243,13 +243,14 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService {
final String ipAddress = packet.getAddress().getHostAddress();
byte[] data = Arrays.copyOfRange(packet.getData(), 0, packet.getLength());
logger.debug("Midea AC discover data ({}) from {}: '{}'", data.length, ipAddress, Utils.bytesToHex(data));
logger.trace("Midea AC discover data ({}) from {}: '{}'", data.length, ipAddress, Utils.bytesToHex(data));
if (data.length >= 104 && (Utils.bytesToHex(Arrays.copyOfRange(data, 0, 2)).equals("5A5A")
|| Utils.bytesToHex(Arrays.copyOfRange(data, 8, 10)).equals("5A5A"))) {
logger.trace("Device supported");
String mSmartId, mSmartVersion = "", mSmartip = "", mSmartPort = "", mSmartSN = "", mSmartSSID = "",
mSmartType = "";
String mSmartId, mSmartip = "", mSmartSN = "", mSmartSSID = "", mSmartType = "", mSmartPort = "",
mSmartVersion = "";
if (Utils.bytesToHex(Arrays.copyOfRange(data, 0, 2)).equals("5A5A")) {
mSmartVersion = "2";
}
@ -260,7 +261,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService {
data = Arrays.copyOfRange(data, 8, data.length - 16);
}
logger.trace("Version: {}", mSmartVersion);
logger.debug("Version: {}", mSmartVersion);
byte[] id = Arrays.copyOfRange(data, 20, 26);
logger.trace("Id Bytes: {}", Utils.bytesToHex(id));
@ -273,10 +274,10 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService {
logger.debug("Id: '{}'", mSmartId);
byte[] encryptData = Arrays.copyOfRange(data, 40, data.length - 16);
logger.debug("Encrypt data: '{}'", Utils.bytesToHex(encryptData));
logger.trace("Encrypt data: '{}'", Utils.bytesToHex(encryptData));
byte[] reply = security.aesDecrypt(encryptData);
logger.debug("Length: {}, Reply: '{}'", reply.length, Utils.bytesToHex(reply));
logger.trace("Length: {}, Reply: '{}'", reply.length, Utils.bytesToHex(reply));
mSmartip = Byte.toUnsignedInt(reply[3]) + "." + Byte.toUnsignedInt(reply[2]) + "."
+ Byte.toUnsignedInt(reply[1]) + "." + Byte.toUnsignedInt(reply[0]);
@ -343,7 +344,7 @@ public class MideaACDiscoveryService extends AbstractDiscoveryService {
properties.put(CONFIG_IP_ADDRESS, ipAddress);
properties.put(CONFIG_IP_PORT, port);
properties.put(CONFIG_DEVICEID, id);
properties.put(PROPERTY_VERSION, version);
properties.put(CONFIG_VERSION, version);
properties.put(PROPERTY_SN, sn);
properties.put(PROPERTY_SSID, ssid);
properties.put(PROPERTY_TYPE, type);

View File

@ -14,6 +14,7 @@ package org.openhab.binding.mideaac.internal.handler;
import static org.openhab.binding.mideaac.internal.MideaACBindingConstants.*;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@ -75,11 +76,13 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
private final Logger logger = LoggerFactory.getLogger(MideaACHandler.class);
private final CloudsDTO clouds;
private final boolean imperialUnits;
private boolean isPollRunning = false;
private final HttpClient httpClient;
private MideaACConfiguration config = new MideaACConfiguration();
private Map<String, String> properties = new HashMap<>();
private @Nullable ConnectionManager connectionManager;
// Default parameters are the same as in the MideaACConfiguration class
private ConnectionManager connectionManager = new ConnectionManager("", 6444, 4, "", "", "", "", "", "", 0, false);
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private @Nullable ScheduledFuture<?> scheduledTask = null;
@ -113,27 +116,22 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
}
/**
* This method handles the Channels that can be set (non-read only)
* First the Routine polling is stopped so there is no conflict
* Then connects and authorizes (if necessary) and returns here to
* create the command set which is then sent to the device.
* This method handles the AC Channels that can be set (non-read only)
* The command set is formed using the previous command to only
* change the item requested and leave the others the same.
* The command set which is then sent to the device via the connectionManager.
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Handling channelUID {} with command {}", channelUID.getId(), command.toString());
ConnectionManager connectionManager = this.connectionManager;
if (connectionManager == null) {
logger.warn("The connection manager was unexpectedly null, please report a bug");
return;
}
if (command instanceof RefreshType) {
try {
connectionManager.getStatus(callbackLambda);
} catch (MideaAuthenticationException e) {
logger.warn("Unable to proces command: {}", e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
} catch (MideaConnectionException | MideaException e) {
logger.warn("Unable to proces command: {}", e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
return;
@ -178,12 +176,14 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
* Initialize is called on first pass or when a device parameter is changed
* The basic check is if the information from Discovery (or the user update)
* is valid. Because V2 devices do not require a cloud provider (or token/key)
* The check is for the IP, port and deviceID. This method also resets the dropped
* commands, disconnects the socket and stops the connection monitor (if these were
* running)
* The first check is for the IP, port and deviceID. The second part
* checks the security configuration if required (V3 device).
*/
@Override
public void initialize() {
if (isPollRunning) {
stopScheduler();
}
config = getConfigAs(MideaACConfiguration.class);
if (!config.isValid()) {
@ -196,10 +196,13 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
try {
discoveryService.discoverThing(config.ipAddress, this);
return;
} catch (Exception e) {
logger.error("Discovery failure for {}: {}", thing.getUID(), e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Discovery failure. Check configuration.");
return;
}
return;
} else {
logger.debug("MideaACHandler config of {} is invalid. Check configuration", thing.getUID());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
@ -215,8 +218,10 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
logger.info("Retrieving Token and/or Key from cloud");
CloudProviderDTO cloudProvider = CloudProviderDTO.getCloudProvider(config.cloud);
getTokenKeyCloud(cloudProvider);
return;
} else {
logger.warn("Configuration invalid for {}", thing.getUID());
logger.warn("Configuration invalid for {} and no account info to retrieve from cloud", thing.getUID());
return;
}
} else {
logger.debug("Security Configuration (V3 Device) valid for {}", thing.getUID());
@ -228,21 +233,29 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
config.ipPort, config.timeout, config.key, config.token, config.cloud, config.email, config.password,
config.deviceId, config.version, config.promptTone);
// startScheduler(2, config.pollingTime, TimeUnit.SECONDS);
scheduler.scheduleWithFixedDelay(this::pollJob, 2, config.pollingTime, TimeUnit.SECONDS);
startScheduler(2, config.pollingTime, TimeUnit.SECONDS);
}
public void startScheduler(long initialDelay, long delay, TimeUnit unit) {
scheduledTask = scheduler.scheduleWithFixedDelay(this::pollJob, initialDelay, delay, unit);
logger.debug("Scheduled task started");
/**
* Starts the Scheduler for the Polling
*
* @param initialDelay Seconds before first Poll
* @param delay Seconds between Polls
* @param unit Seconds
*/
private void startScheduler(long initialDelay, long delay, TimeUnit unit) {
if (scheduledTask == null) {
isPollRunning = true;
scheduledTask = scheduler.scheduleWithFixedDelay(this::pollJob, initialDelay, delay, unit);
logger.debug("Scheduled task started");
} else {
logger.debug("Scheduler already running");
}
}
private void pollJob() {
ConnectionManager connectionManager = this.connectionManager;
if (connectionManager == null) {
logger.warn("The connection manager was unexpectedly null, please report a bug");
return;
}
try {
connectionManager.getStatus(callbackLambda);
updateStatus(ThingStatus.ONLINE);
@ -315,13 +328,15 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
Object propertyIpPort = Objects.requireNonNull(discoveryProps.get(CONFIG_IP_PORT));
configuration.put(CONFIG_IP_PORT, propertyIpPort.toString());
Object propertyVersion = Objects.requireNonNull(discoveryProps.get(CONFIG_VERSION));
BigDecimal bigDecimalVersion = new BigDecimal((String) propertyVersion);
logger.trace("Property Version in Handler {}", bigDecimalVersion.intValue());
configuration.put(CONFIG_VERSION, bigDecimalVersion.intValue());
updateConfiguration(configuration);
properties = editProperties();
Object propertyVersion = Objects.requireNonNull(discoveryProps.get(PROPERTY_VERSION));
properties.put(PROPERTY_VERSION, propertyVersion.toString());
Object propertySN = Objects.requireNonNull(discoveryProps.get(PROPERTY_SN));
properties.put(PROPERTY_SN, propertySN.toString());
@ -332,10 +347,14 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
properties.put(PROPERTY_TYPE, propertyType.toString());
updateProperties(properties);
initialize();
}
/**
* Gets the token and key from the Cloud
*
* @param cloudProvider Cloud Provider account
*/
public void getTokenKeyCloud(CloudProviderDTO cloudProvider) {
CloudDTO cloud = getClouds().get(config.email, config.password, cloudProvider);
if (cloud != null) {
@ -350,7 +369,7 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
logger.trace("Token: {}", tk.token());
logger.trace("Key: {}", tk.key());
logger.info("Token and Key obtained from cloud, saving, initializing");
logger.info("Token and Key obtained from cloud, saving, back to initialize");
initialize();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format(
@ -360,22 +379,20 @@ public class MideaACHandler extends BaseThingHandler implements DiscoveryHandler
}
}
public void stopScheduler() {
private void stopScheduler() {
ScheduledFuture<?> localScheduledTask = this.scheduledTask;
if (localScheduledTask != null && !localScheduledTask.isCancelled()) {
localScheduledTask.cancel(true);
logger.debug("Scheduled task cancelled.");
isPollRunning = false;
scheduledTask = null;
}
if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdownNow();
logger.debug("Scheduler service shut down.");
}
}
@Override
public void dispose() {
// stopScheduler();
stopScheduler();
connectionManager.dispose(true);
}
}

View File

@ -68,6 +68,7 @@ public class Response {
logger.debug("Swing Mode: {}", getSwingMode());
logger.debug("Sleep Function: {}", getSleepFunction());
logger.debug("Turbo Mode: {}", getTurboMode());
logger.debug("Eco Mode: {}", getEcoMode());
logger.debug("Indoor Temperature: {}", getIndoorTemperature());
logger.debug("Outdoor Temperature: {}", getOutdoorTemperature());
logger.debug("LED Display: {}", getDisplayOn());
@ -77,7 +78,6 @@ public class Response {
logger.trace("Prompt Tone: {}", getPromptTone());
logger.trace("Appliance Error: {}", getApplianceError());
logger.trace("Auxiliary Heat: {}", getAuxHeat());
logger.trace("Eco Mode: {}", getEcoMode());
logger.trace("Fahrenheit: {}", getFahrenheit());
logger.trace("Humidity: {}", getHumidity());
logger.trace("Alternate Target Temperature {}", getAlternateTargetTemperature());

View File

@ -22,7 +22,7 @@ thing-type.config.mideaac.ac.email.description = Email for cloud account chosen
thing-type.config.mideaac.ac.ipAddress.label = IP Address
thing-type.config.mideaac.ac.ipAddress.description = IP Address of the device.
thing-type.config.mideaac.ac.ipPort.label = IP Port
thing-type.config.mideaac.ac.ipPort.description = IP port of the device (for V2: 6444).
thing-type.config.mideaac.ac.ipPort.description = IP port of the device.
thing-type.config.mideaac.ac.key.label = Key
thing-type.config.mideaac.ac.key.description = Secret Key (length 64 HEX) used for secure connection authentication used with devices v3 (if not known, enter email and password for Cloud to retrieve it).
thing-type.config.mideaac.ac.password.label = Password
@ -36,7 +36,7 @@ thing-type.config.mideaac.ac.timeout.description = Connecting timeout. Minimum t
thing-type.config.mideaac.ac.token.label = Token
thing-type.config.mideaac.ac.token.description = Secret Token (length 128 HEX) used for secure connection authentication used with devices v3 (if not known, enter email and password for Cloud to retrieve it).
thing-type.config.mideaac.ac.version.label = AC Version
thing-type.config.mideaac.ac.version.description = Version 3 requires Token, Key and Cloud provider. Version 2 doesn't. Leave blank to discover
thing-type.config.mideaac.ac.version.description = Version 3 requires Token, Key and Cloud provider. Version 2 doesn't.
# channel types

View File

@ -41,10 +41,10 @@
<label>IP Address</label>
<description>IP Address of the device.</description>
</parameter>
<parameter name="ipPort" type="text" required="true">
<parameter name="ipPort" type="decimal" required="true">
<context>ipPort</context>
<label>IP Port</label>
<description>IP port of the device (for V2: 6444).</description>
<description>IP port of the device.</description>
<default>6444</default>
</parameter>
<parameter name="deviceId" type="text" required="true">
@ -106,11 +106,11 @@
<description>After sending a command device will play "ding" tone when command is received and executed.</description>
<default>false</default>
</parameter>
<parameter name="version" type="text" required="true">
<parameter name="version" type="decimal" required="true">
<context>version</context>
<label>AC Version</label>
<description>Version 3 requires Token, Key and Cloud provider. Version 2 doesn't. Leave blank to discover</description>
<default>3</default>
<description>Version 3 requires Token, Key and Cloud provider. Version 2 doesn't.</description>
<default>0</default>
</parameter>
</config-description>

View File

@ -31,37 +31,84 @@ public class MideaACConfigurationTest {
MideaACConfiguration config = new MideaACConfiguration();
/**
* Test for valid Configs
* Test for valid step 1 Configs
*/
@Test
public void testValidConfigs() {
config.ipAddress = "192.168.0.1";
config.ipPort = 6444;
config.deviceId = "1234567890";
config.version = 3;
assertTrue(config.isValid());
assertFalse(config.isDiscoveryNeeded());
}
/**
* Test for non-valid configs
* Test for non-valid step 1 configs
*/
@Test
public void testnonValidConfigs() {
config.ipAddress = "192.168.0.1";
config.ipPort = 0;
config.deviceId = "1234567890";
config.version = 3;
assertFalse(config.isValid());
assertTrue(config.isDiscoveryNeeded());
}
/**
* Test for bad IP configs
* Test for valid Security Configs
*/
@Test
public void testValidSecurityConfigs() {
config.key = "97c65a4eed4f49fda06a1a51d5cbd61d2c9b81d103ca4ca689f352a07a16fae6";
config.token = "D24046B597DB9C8A7CA029660BC606F3FD7EBF12693E73B2EF1FFE4C3B7CA00C824E408C9F3CE972CC0D3F8250AD79D0E67B101B47AC2DD84B396E52EA05193F";
config.cloud = "NetHome Plus";
assertTrue(config.isV3ConfigValid());
}
/**
* Test for Invalid Security Configs
*/
@Test
public void testInvalidSecurityConfigs() {
config.key = "97c65a4eed4f49fda06a1a51d5cbd61d2c9b81d103ca4ca689f352a07a16fae6";
config.token = "D24046B597DB9C8A7CA029660BC606F3FD7EBF12693E73B2EF1FFE4C3B7CA00C824E408C9F3CE972CC0D3F8250AD79D0E67B101B47AC2DD84B396E52EA05193F";
config.cloud = "";
assertFalse(config.isV3ConfigValid());
}
/**
* Test for if key and token are obtainable from cloud
*/
@Test
public void testIfTokenAndKeyCanBeObtainedFromCloud() {
config.email = "someemail.com";
config.password = "somestrongpassword";
config.cloud = "NetHome Plus";
assertTrue(config.isTokenKeyObtainable());
}
/**
* Test for if key and token cannot be obtaines from cloud
*/
@Test
public void testIfTokenAndKeyCanNotBeObtainedFromCloud() {
config.email = "";
config.password = "somestrongpassword";
config.cloud = "NetHome Plus";
assertFalse(config.isTokenKeyObtainable());
}
/**
* Test for bad IP v.4 address
*/
@Test
public void testBadIpConfigs() {
config.ipAddress = "192.1680.1";
config.ipPort = 6444;
config.deviceId = "1234567890";
config.version = 3;
assertTrue(config.isValid());
assertTrue(config.isDiscoveryNeeded());
}

View File

@ -40,6 +40,19 @@ public class MideaACDiscoveryServiceTest {
String mSmartId = "", mSmartVersion = "", mSmartip = "", mSmartPort = "", mSmartSN = "", mSmartSSID = "",
mSmartType = "";
/**
* Test Version
*/
@Test
public void testVersion() {
if (Utils.bytesToHex(Arrays.copyOfRange(data, 0, 2)).equals("8370")) {
mSmartVersion = "3";
} else {
mSmartVersion = "2";
}
assertEquals("3", mSmartVersion);
}
/**
* Test Id
*/