Skip to content

[vesync] Add auth V2 support#20236

Merged
lsiepel merged 24 commits intoopenhab:mainfrom
dag81:chore/vesyncAuth2
Apr 5, 2026
Merged

[vesync] Add auth V2 support#20236
lsiepel merged 24 commits intoopenhab:mainfrom
dag81:chore/vesyncAuth2

Conversation

@dag81
Copy link
Copy Markdown
Contributor

@dag81 dag81 commented Feb 16, 2026

The changes are currently being soak tested but the new flows have been proven by multiple users in different data centre's in the linked issue thread below. (Soak testing primarily for token refresh if it does happen). Opening now for review while the soak test's run.

The main adjustments here relate to: #19127

This adjusts:

  • Corrects a error when dispose is called, as .clear on the hashmap now appears to cause errors, it is re-referenced to a new HashMap instance to ensure a empty one is referenced without errors.

  • Authorization handling with VeSync servers to support a newer scheme, that at some point new accounts could only use. Old accounts do actually appear to support the new schema on testing with my account, so the upgrade should be backwards compatible for V2 based devices at least.

  • The URL/s utilised so the relevant cloud service is targeted based on the data extracted from the authorization handshakes.

  • A few other minor adjustments for Static Code Analysis bits that were flagged, since the last time this binding was updated, to remove them.

[veSync] Initial upload of prototype POC code before cleanup

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
@dag81 dag81 changed the title [veSync] No Need to review yet WIP [veSync] Auth V2 Support - No Need to review yet WIP Feb 16, 2026
[veSync] Non-op cleanup's from checks

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Constant cleanups

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Refactored to remove inner class with cleanups

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Bugfix previous constants update

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Cleanup commented out lines

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Cleanups - removal of unit test and cleanup

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] SCA cleanups

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Token refresh support

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
@dag81 dag81 changed the title [veSync] Auth V2 Support - No Need to review yet WIP [veSync] Auth V2 Support Feb 17, 2026
@dag81 dag81 marked this pull request as ready for review February 17, 2026 01:09
@psmedley psmedley added the bug An unexpected problem or unintended behavior of an add-on label Feb 17, 2026
@psmedley psmedley requested a review from Copilot February 17, 2026 08:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds VeSync “Auth V2” login support (incl. region-aware endpoint selection) to restore/maintain connectivity for newer VeSync accounts and data centers, alongside some minor SCA-driven cleanup.

Changes:

  • Introduce Auth V2 login handshake (token request → authorize-code login → optional region redirect) and persist the selected server base URL in the user session.
  • Route subsequent API calls via the selected regional server URL and add a token-refresh retry on token-related errors.
  • Minor robustness/SCA updates (Locale-stable toLowerCase, timer calculation using Instant, hashmap dispose behavior).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
bundles/org.openhab.binding.vesync/src/main/test/org/openhab/binding/vesync/internal/handler/requests/VesyncAuthenticatedRequestTest.java Renames the public test class (currently breaks Java filename/classname contract).
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirPurifierHandler.java Locale-stable lowercasing; removes unnecessary suppression.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncDeviceAirHumidifierHandler.java Locale-stable lowercasing; switches auto-off time calc to Instant.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBridgeHandler.java Adds token-expiry detection and re-login retry wrapper for authorized calls.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/handlers/VeSyncBaseDeviceHandler.java Switches request URL building to relative paths (base URL now comes from session/server selection).
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncUserSession.java Adds serverUrl to session; adjusts session fields/toString.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncAuthTokenResponse.java New DTO for the auth-token step response.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/responses/VeSyncAuthLoginWithAuthorizeCodeVeSyncResponse.java New DTO for the authorize-code login response (incl. region redirect data).
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncRequest.java Refactors to extend new base request DTO (currently introduces duplicate httpMethod field).
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncProtocolConstants.java Adds US/EU server addresses; converts endpoints to relative paths.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncBaseRequest.java New common request base (acceptLanguage/traceId/method/httpMethod).
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncAuthTokenRequest.java New request DTO for the auth-token step.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncAuthLoginWithAuthorizeCodeVeSyncRegionChange.java New DTO for region-change authorize-code login.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/dto/requests/VeSyncAuthLoginWithAuthorizeCodeVeSync.java New DTO for authorize-code login.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/VeSyncV2ApiHelper.java Replaces legacy login with Auth V2 helper; prefixes requests with the session’s selected server URL; fixes dispose map handling.
bundles/org.openhab.binding.vesync/src/main/java/org/openhab/binding/vesync/internal/api/LoginAuthV2Helper.java New helper implementing the Auth V2 sequence + cross-region redirect handling (needs null-safety hardening).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

[veSync] Copilot suggestion minor mods

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] LoginAuthV2Helper.java NPE

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Refactored to remove inner class with cleanups

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Token refresh correction and NPE handling updates based on co-pilot feedback

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Further NPE bits from copilot flagged bits

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
@dag81
Copy link
Copy Markdown
Contributor Author

dag81 commented Feb 17, 2026

Hi @psmedley I worked through each of the bits co-pilot raised. I resolved so I could track but the changes are from commit 4e9da4f onwards, along with one bug I spotted while re-reading. Currently soak testing here on our main server now - but would be good to open up for human review when there is time please.

@psmedley
Copy link
Copy Markdown
Contributor

Hi @psmedley I worked through each of the bits co-pilot raised. I resolved so I could track but the changes are from commit 4e9da4f onwards, along with one bug I spotted while re-reading. Currently soak testing here on our main server now - but would be good to open up for human review when there is time please.

I'm not a reviewer (my java is nowhere near good enough), I was just trying to help the you and the ultimate approver by kicking off the co-pilot reviews, and as they're a quick/easy win to address.

Copy link
Copy Markdown
Contributor

@psmedley psmedley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few things I noticed

@dag81
Copy link
Copy Markdown
Contributor Author

dag81 commented Feb 18, 2026

Hi @psmedley - thank you for looking through - I didn't know whether you had to do something to get it assigned to someone or mark co-pilot as good / re-run it, etc.... So big thanks for having a look through - I guess this gets automatically picked up then when someone has time and assigns it to themselves irrelevant of the co-pilot run then?

@psmedley
Copy link
Copy Markdown
Contributor

Hi @psmedley - thank you for looking through - I didn't know whether you had to do something to get it assigned to someone or mark co-pilot as good / re-run it, etc.... So big thanks for having a look through - I guess this gets automatically picked up then when someone has time and assigns it to themselves irrelevant of the co-pilot run then?

Yeah a reviewer will pick it up when they have time, I have a couple of PRs for Ring to extend functionality which are waiting. I added the 'Bug' tag to this one as the vesync binding is currently broken for many users - so this one should hopefully get looked at first. Bug fixes make it into 5.1.x builds, enhancements only go into 5.2.x (due midyear)

[veSync] Const creation

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Removal of unnecessary static imports

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
@dag81 dag81 changed the title [veSync] Auth V2 Support [veSync] Auth V2 Support so users can use the binding again Feb 19, 2026
[veSync] Alignment to use const

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Const simplification

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
@dag81
Copy link
Copy Markdown
Contributor Author

dag81 commented Mar 28, 2026

@openhab/add-ons-maintainers any chance of a review on this change please - currently users need to use snapshot builds, until we get it merged, for any new accounts at least. (I've been running locally now on our home server for approx. 2 month's - perfectly stable and running BAU).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +192 to +204
final ContentResponse response = request.send();

final VeSyncAuthLoginWithAuthorizeCodeVeSyncResponse result = VeSyncConstants.GSON
.fromJson(response.getContentAsString(), VeSyncAuthLoginWithAuthorizeCodeVeSyncResponse.class);

if (result == null) {
return false;
}

if (result.isMsgSuccess()) {
loginData.token = result.result.token;
loginData.accountId = result.result.accountID;
return true;
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the regional-redirect login flow, the HTTP status isn’t validated and result.result is dereferenced without checking for null. A non-200 response or a response without result will cause incorrect behavior or an NPE. Check response.getStatus() first and ensure result.result is non-null before reading token/accountID (otherwise return false or throw AuthenticationException).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah thats a good catch. Added the missing http response code check required.

Comment on lines +185 to +186
if (session != null && !url.startsWith(session.getServerUrl())) {
url = session.getServerUrl() + url;
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session.getServerUrl() may be null (it’s not annotated and is populated dynamically), but it’s used in url.startsWith(...) and string concatenation. This can throw an NPE during requests (e.g., device discovery). Guard against null/blank server URLs and only prefix when the incoming url is relative.

Suggested change
if (session != null && !url.startsWith(session.getServerUrl())) {
url = session.getServerUrl() + url;
if (session != null) {
String serverUrl = session.getServerUrl();
if (serverUrl != null && !serverUrl.isBlank() && !url.startsWith("http://")
&& !url.startsWith("https://") && !url.startsWith(serverUrl)) {
url = serverUrl + url;
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

@dag81 dag81 Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted with additional null check, to protect against NPE.

Comment on lines +280 to +283
String result = api.reqV2Authorized(url, macId, requestData);

VeSyncResponse responseFrame = VeSyncConstants.GSON.fromJson(result, VeSyncResponse.class);

Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromJson(result, VeSyncResponse.class) can throw a JsonSyntaxException if the backend returns non-JSON (HTML error pages, proxies, etc.). Right now that would bubble up as a runtime exception and break command handling. Wrap the parse in a try/catch and skip token-refresh logic when parsing fails (keeping the original result).

Copilot uses AI. Check for mistakes.
@SerializedName("result")
public VeSyncAuthTokenResponse.LoginTokenResponse result;

public class LoginTokenResponse {
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoginTokenResponse is a non-static inner class. Gson cannot reliably instantiate non-static inner classes during deserialization, which can cause the auth-token response parsing to fail at runtime. Make LoginTokenResponse a static nested class (or a top-level class) so it can be deserialized safely.

Suggested change
public class LoginTokenResponse {
public static class LoginTokenResponse {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to move all the inner classes out to top level dto. However on looking through the files as they currently are it would make the files much less readable when comparing to the raw payloads. This could be done on another PR but will possibly bloat this one. I have however changed the inner classes to be all static on the responses as recommended.

logger.trace("Refreshing API token due to error response regarding the token");
final VeSyncBridgeConfiguration config = getConfigAs(VeSyncBridgeConfiguration.class);
final String passwordMd5 = VeSyncV2ApiHelper.calculateMd5(config.password);
api.login(config.username, passwordMd5, "Europe/London");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The zone is global? Or do you need to make this configurable?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe the timezone matters, python vesync default to America/New_york - https://github.com/webdjoe/pyvesync/blob/2f2511f27acc8162722f2bf2eee5e74134d07cdd/README.md?plain=1#L57

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a parameter to the request, so it does something and i noticed different zones are used, so if you don;lt experience any issue, atleast align them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally it would be driven from the system and passed as you say Isiepel. Your right psmedley regarding timezone it doesn't technically get used (at least on the api's mapped by pyvesync - I suspect the app uses it when generating accounts possibly is my guess). However the API's are undocumented and mapped from the mapping work from pyvesync. Therefore I wouldn't plug new data generally into it, as they do appear to validate requests against what they expect the app to possibly send. I tested the old America/New York tz against my primary account (appears to be USA based due to its age), and a test one I setup for testing this binding (which logs in to the eu servers) I can see redirect's to eu based server, and it accepts the America/New York tz so have aligned on that one. If times are ever mapped rather than durations then this may need to be looked at but then pyvesync will then also map true tz data alongside the new api's as well, so it can be updated then based on the region openhab is running in. I have therefore kept as is just aligned for consistency as suggested, in case of future use.

Copy link
Copy Markdown
Contributor

@lsiepel lsiepel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beside the comment i made and copilot did. I wonder if the differnt (hardcoded and/or default) timezone/locale are right. As this supports both us and europ, i see both of them mixed. Please make sure that it works for both areas as intended.

@lsiepel
Copy link
Copy Markdown
Contributor

lsiepel commented Apr 3, 2026

Besides fixing the open comments, i wonder if you have any thoughts about backporting this to 4.3.x ?

@dag81
Copy link
Copy Markdown
Contributor Author

dag81 commented Apr 3, 2026 via email

@lsiepel
Copy link
Copy Markdown
Contributor

lsiepel commented Apr 3, 2026

Stand-alone /top level dto is preferred.

I'll look at refactoring this on a second pass after this is merged and backported. As I was about to do it, then realised it would create a lot more changes, that would introduce risk on what is a bug fix. It will need some careful renaming to keep everything easily traceable when done. So will move, but if okay I'll open a second less urgent PR for that against this binding.

dag81 added 6 commits April 3, 2026 23:20
[veSync] Timezone use alignment

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Add V1 failsafe fallback

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Add V1 failsafe fallback

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Revert and adjust V1 fallback behaviour

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] Further inner class static updates on responses

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
[veSync] HTTP Response code check

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
Copy link
Copy Markdown
Contributor

@lsiepel lsiepel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM

@lsiepel lsiepel merged commit 4895202 into openhab:main Apr 5, 2026
2 checks passed
@lsiepel lsiepel added this to the 5.2 milestone Apr 5, 2026
lsiepel pushed a commit that referenced this pull request Apr 5, 2026
* [veSync] Initial upload of prototype POC code before cleanup

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
@lsiepel lsiepel added the backported A PR that has been cherry-picked to a patch release branch label Apr 5, 2026
@lsiepel lsiepel changed the title [veSync] Auth V2 Support so users can use the binding again [vesync] Add auth V2 support Apr 5, 2026
jpg0 pushed a commit to jpg0/openhab-addons that referenced this pull request Apr 6, 2026
…20236)

* [veSync] Initial upload of prototype POC code before cleanup

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
dag81 added a commit to dag81/openhab-addons that referenced this pull request Apr 11, 2026
…20236)

[veSync] Auth V2 Support so users can use the binding again (openhab#20236)

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
lsiepel pushed a commit that referenced this pull request Apr 12, 2026
…20556)

[veSync] Auth V2 Support so users can use the binding again (#20236)

Signed-off-by: David Goodyear <david.goodyear@gmail.com>
@lsiepel lsiepel added the backported4 Backported patches to the 4.x branch. Used for constructing release notes. label Apr 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backported A PR that has been cherry-picked to a patch release branch backported4 Backported patches to the 4.x branch. Used for constructing release notes. bug An unexpected problem or unintended behavior of an add-on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants