-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[mideaac] Initial contribution- second try #17749
base: main
Are you sure you want to change the base?
Conversation
8fae5fa
to
44bb90f
Compare
4ef6b08
to
071c1e7
Compare
This is not forgotten, the time I have to spare is consumed by the ZUI binding at the moment. So don’t wait for me. |
@lsiepel Well I'm not really waiting. I'm working on other things too. There shouldn't be much to review after you rewrote the connection manager. I did a few things to make it more robust to run without the A/C connected to the cloud and fixed some things that you left hanging. I have had it polling for months without issues. It is not exactly A/C season, so no one is exactly clamoring for an update. Hopefully you or someone else can have a look in a couple of months as we get towards spring. EDIT: I did update the version to OH5 and change the date to 2025 in the headers |
7350e50
to
02ae747
Compare
Forgot about this one, sorry. I might have some time to review in the next days. |
Yes, it is working fine. I have been using it since the last commit. Decided to rebase today in case something in the OH5.0 causes a problem, but it passed checks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for not leaving this PR/binding. It has improved, i also see more room for improvement. Besides some new minor issues that i probably missed before, i think the DTO and connection structure can benefit from some more work and thinking.
...binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACConfiguration.java
Outdated
Show resolved
Hide resolved
...binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACConfiguration.java
Outdated
Show resolved
Hide resolved
...binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/MideaACConfiguration.java
Outdated
Show resolved
Hide resolved
...es/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/Utils.java
Outdated
Show resolved
Hide resolved
...es/org.openhab.binding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/Utils.java
Outdated
Show resolved
Hide resolved
...nding.mideaac/src/main/java/org/openhab/binding/mideaac/internal/handler/MideaACHandler.java
Outdated
Show resolved
Hide resolved
} else { | ||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format( | ||
"Can't retrieve Token and Key from Cloud; email, password and/or cloud parameter error")); | ||
logger.warn("Can't retrieve Token and Key from Cloud; email, password and/or cloud parameter error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this logging is not needed when the thing state is set.
logger.warn("Can't retrieve Token and Key from Cloud; email, password and/or cloud parameter error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See question above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logger.warn("Can't retrieve Token and Key from Cloud; email, password and/or cloud parameter error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok in next commit
...mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/ConnectionManager.java
Outdated
Show resolved
Hide resolved
...mideaac/src/main/java/org/openhab/binding/mideaac/internal/connection/ConnectionManager.java
Outdated
Show resolved
Hide resolved
try { | ||
Thread.sleep(5000); | ||
} catch (InterruptedException ex) { | ||
logger.debug("An interupted error (socket retry) has occured {}", ex.getMessage()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stop processing and exit immediate when an InterruptException occurs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I recall that won't work as I intended (with 3 tries). The exceptions aren't serious and are ephemeral in nature. I haven't had that INFO log message in months (after I went from 2 to three tries. That's why I forgot it was there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An InterruptException usually signals an external call to stop the proces / exit the application /shutdown. And therefor the method should exit fast cleaning up any resources but surely not trying to perform another retry attempt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have separated the catches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marked some previous comments as resolved. Found some new ones, but nothing big. Let's slowly continue and narrow the remaining issues down and fix them together.
if (!deviceIsConnected) { | ||
logger.info("Connected to IP {}", ipAddress); | ||
} | ||
logger.debug("Connected to IP {}", ipAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like double logging. I guess this would be better:
if (!deviceIsConnected) { | |
logger.info("Connected to IP {}", ipAddress); | |
} | |
logger.debug("Connected to IP {}", ipAddress); | |
logger.debug("Connected to IP {}: {}", ipAddress, deviceIsConnected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What the intention here was one INFO for the first time and debug if it was recovering from some problem, but not disconnected. Otherwise there is nothing in the log to show it is running. Note starting date.
2025-01-24 16:45:59.796 [INFO ] [nternal.connection.ConnectionManager] - Connected to IP 192.168.0.246
This is when I updated to the rebased binding
2025-02-19 10:58:07.986 [INFO ] [nternal.connection.ConnectionManager] - Connected to IP 192.168.0.246
This is when I deleted the email and password this morning to recheck that it could be done
2025-02-20 08:54:25.405 [INFO ] [nternal.connection.ConnectionManager] - Connected to IP 192.168.0.246
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment to explain
sendCommand(command, callback); | ||
} else { | ||
droppedCommands = droppedCommands + 1; | ||
logger.info("Problem with reading response, skipping {} skipped count since startup {}", command, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logger.info("Problem with reading response, skipping {} skipped count since startup {}", command, | |
logger.debug("Problem with reading response, skipping {} skipped count since startup {}", command, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. I used that to track health, but probably no value to user
connectionManager.sendCommand(CommandHelper.handleOffTimer(command, lastresponse), callbackLambda); | ||
} | ||
} catch (MideaConnectionException | MideaAuthenticationException e) { | ||
logger.warn("Unable to proces command: {}", e.getMessage()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an authentication exception occurs, i guess it is permanenet. When a timeout occurs, it is transient and the binding should recover. You might want to handle both cases seperate.
logger.warn("Unable to proces command: {}", e.getMessage()); | |
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok This was part of the code you rewrote and have never seen an exception, so don't know what else to say.
} else { | ||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String.format( | ||
"Can't retrieve Token and Key from Cloud; email, password and/or cloud parameter error")); | ||
logger.warn("Can't retrieve Token and Key from Cloud; email, password and/or cloud parameter error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logger.warn("Can't retrieve Token and Key from Cloud; email, password and/or cloud parameter error"); |
if (!config.isValid()) { | ||
logger.warn("Configuration invalid for {}", thing.getUID()); | ||
if (config.isDiscoveryNeeded()) { | ||
logger.warn("Discovery needed, discovering....{}", thing.getUID()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logger.warn("Discovery needed, discovering....{}", thing.getUID()); |
|
||
## Discovery | ||
|
||
Once the Air Conditioner is on the network (WiFi active) the other required parameters can be discovered automatically. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you provide a lot more information then in the readme. This is what chatgpt makes out of it. Please check and adapt the text so that it gives the user enough guidance to set it up correctly.l
The key parameters—like deviceId, token, key, and version—are automatically discovered once the air conditioner is connected to the WiFi and you’ve provided the necessary cloud and password (unless it's a version 2 device).
However, you’ll still need to manually enter a few parameters such as: Polling time, Socket timeout, Prompt tone.
Frame label="AC Unit" { | ||
Text item=outdoor_temperature label="Outdoor Temperature [%.1f °F]" | ||
Text item=indoor_temperature label="Indoor Temperature [%.1f °F]" | ||
Setpoint item=target_temperature label="Target Temperature [%.1f °F]" minValue=63.0 maxValue=78 step=1.0 | ||
Switch item=power label="Midea AC Power" | ||
Switch item=temperature_unit label= "Temp Unit" mappings=[ON="Fahrenheit", OFF="Celsius"] | ||
Selection item=fan_speed label="Midea AC Fan Speed" | ||
Selection item=operational_mode label="Midea AC Mode" | ||
Selection item=swing_mode label="Midea AC Louver Swing Mode" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frame label="AC Unit" { | |
Text item=outdoor_temperature label="Outdoor Temperature [%.1f °F]" | |
Text item=indoor_temperature label="Indoor Temperature [%.1f °F]" | |
Setpoint item=target_temperature label="Target Temperature [%.1f °F]" minValue=63.0 maxValue=78 step=1.0 | |
Switch item=power label="Midea AC Power" | |
Switch item=temperature_unit label= "Temp Unit" mappings=[ON="Fahrenheit", OFF="Celsius"] | |
Selection item=fan_speed label="Midea AC Fan Speed" | |
Selection item=operational_mode label="Midea AC Mode" | |
Selection item=swing_mode label="Midea AC Louver Swing Mode" | |
Frame label="AC Unit" { | |
Text item=outdoor_temperature label="Outdoor Temperature [%.1f °F]" | |
Text item=indoor_temperature label="Indoor Temperature [%.1f °F]" | |
Setpoint item=target_temperature label="Target Temperature [%.1f °F]" minValue=63.0 maxValue=78 step=1.0 | |
Switch item=power label="Midea AC Power" | |
Switch item=temperature_unit label= "Temp Unit" mappings=[ON="Fahrenheit", OFF="Celsius"] | |
Selection item=fan_speed label="Midea AC Fan Speed" | |
Selection item=operational_mode label="Midea AC Mode" | |
Selection item=swing_mode label="Midea AC Louver Swing Mode" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
Text item=outdoor_temperature label="Outdoor Temperature [%.1f °F]" | ||
Text item=indoor_temperature label="Indoor Temperature [%.1f °F]" | ||
Setpoint item=target_temperature label="Target Temperature [%.1f °F]" minValue=63.0 maxValue=78 step=1.0 | ||
Switch item=power label="Midea AC Power" | ||
Switch item=temperature_unit label= "Temp Unit" mappings=[ON="Fahrenheit", OFF="Celsius"] | ||
Selection item=fan_speed label="Midea AC Fan Speed" | ||
Selection item=operational_mode label="Midea AC Mode" | ||
Selection item=swing_mode label="Midea AC Louver Swing Mode" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created a new comment with a suggestion. I meant to add indentation not removing a curly brace.
...ac/src/main/java/org/openhab/binding/mideaac/internal/discovery/MideaACDiscoveryService.java
Show resolved
Hide resolved
try { | ||
Thread.sleep(5000); | ||
} catch (InterruptedException ex) { | ||
logger.debug("An interupted error (socket retry) has occured {}", ex.getMessage()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An InterruptException usually signals an external call to stop the proces / exit the application /shutdown. And therefor the method should exit fast cleaning up any resources but surely not trying to perform another retry attempt.
In no particular order, 1) I'm not sure how to use the OH standard conversions and where to put the .toLowerCase since I do not see that in the documentation. 2) The exception in the retry I'm trying to address is simply a timeout. 3) Elsewhere, I was trying to avoid breaking something that I fixed earlier would cause me to go back and fix again. It has been months and I'm not recalling all the issues I had getting to the fix. 4) I also attached a log of the DTO actions, it appears to me everything is needed. The only logic is to pull from the cloud responses to compose the next message. EDIT: Regaring other comments; Second I retested a change to remind myself of the problem. I dropped the return after getTokenKeyCloud(cloudProvider); (line 218) and the initialize() after logger.debug("Token and Key obtained from cloud, saving, back to initialize"); (line 372). The token and key are filled in on the UI, the connection manager is started, but chokes on the authorization. Resaving the thing with a change corrects the problem (I just added a location), but that is messy. With the current code everything works seamlessly when deleting the token and key when the cloud provider, password and email are in place. |
Figured out how to leverage the OH core util hex/byte and separated the socket timeout exception from anything else. Seems to still work, but with the magnitude of changes am testing overnight. Have tested scan, retrieve key/token, normal polling and temperature changes. Had one timeout exception that worked as expected. 9 seconds = 4 for timeout and 5 delay for next try.
Normal connection is less that 10ms.
|
99ac827
to
2bb3b9c
Compare
Mideaac binding after partial PR review. Main remaining issue is the connection manager which currently needs to be embedded in the MideaACHandler to leverage the OH base thing handler. Signed-off-by: Bob Eckhoff <[email protected]>
Working (sort of) version of split connection manager. Problem with the connection manager being null when the binding is stop/start. Need reset to clear with clean-cache too. Signed-off-by: Bob Eckhoff <[email protected]>
After initial changes, tested various scenarios and made changes so they are working like before. Signed-off-by: Bob Eckhoff <[email protected]>
forgot to run spotless on the last update Signed-off-by: Bob Eckhoff <[email protected]>
Java doc and possible mdns discovery. Signed-off-by: Bob Eckhoff <[email protected]>
spotless Signed-off-by: Bob Eckhoff <[email protected]>
Makes retries more robust. Three times for connection and one retry on the command. Signed-off-by: Bob Eckhoff <[email protected]>
Changed V2 response to align with V3 process after extra decoding. Signed-off-by: Bob Eckhoff <[email protected]>
Changed version and changed dates (in advance-hopefully ok) Signed-off-by: Bob Eckhoff <[email protected]>
Changing dates early was a bad idea Signed-off-by: Bob Eckhoff <[email protected]>
This reverts commit 44bb90f. Signed-off-by: Bob Eckhoff <[email protected]>
Change to new Headers Signed-off-by: Bob Eckhoff <[email protected]>
Addressed some PR comments. Some questions outstanding. Signed-off-by: Bob Eckhoff <[email protected]>
Additional edits. Signed-off-by: Bob Eckhoff <[email protected]>
Updated utilities to leverage OH core and separated retry timeout from other exceptions. Signed-off-by: Bob Eckhoff <[email protected]>
Eliminated DTO folder and classes by addressing null issues in the cloud communications. Plus slight timing issue on retry. Signed-off-by: Bob Eckhoff <[email protected]>
New standard format? Signed-off-by: Bob Eckhoff <[email protected]>
Change connection log to either/or avoiding both on device being connected. Signed-off-by: Bob Eckhoff <[email protected]>
This is a second attempt. The first attempt is closed #17395. Essentially all of the previous comments have been addressed. The main new change is the separation of the connection manager and the MideaACHandler, (largely rewritten by @lsiepel) these two files are basically new. I have worked and tested with this version (made some modifications) for several weeks and it seems to work as well as the first attempt now. Also there were a number of new supporting files added by @lsiepel.
For reference the previous intro is here.
I’d like to put this out for new binding review.
This binding was started by the original author as a forum topic but has since had other contributors. I picked it up when I installed a mini-split AC that used the Midea protocol earlier this year, originally to allow for longer polling frequencies. Then as a retirement challenge (no formal java training), decided to attempt to clean it up for official status. I cleaned up the summary report from over 150 items and believe I conformed to current developer guidelines (Java docs, naming, tests, etc.). Along the way added some additional functionality and corrected some code issues.
The discovery and security classes remain largely unchanged from the original author (who I haven’t been able to contact), and work well. The other classes I understand pretty well at this point and hopefully can address any concerns.
As a note, to conform to the guidelines there are breaking changes in naming from the version I published on the forum thread.