Skip to content
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

[iaqualink] Add Support for AWS MQTT based IAqualink devices, such as Zodiac Hydroxinator #17671

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jpg0
Copy link
Contributor

@jpg0 jpg0 commented Oct 30, 2024

Initial login and device lookup happens over rest, then switches to an MQTTv5 client to interact with an AWS IOT Shadow Device service. Tested with Zodiac Hydroxinator only.

Due to being completely different to the existing implementation it is build as a distinct handler.

Note that this requires the AWS IOT SDK, which includes the AWS CRT native library. This appears to work fine, although only with a minimum of GLIBC 2.32. The openHAB Debian docker container packages Debian 11 which uses 2.31, so you must use Debian 12, which is currently already committed to 4.3.x anyway. (If you want to use on openHAB 4.2.x it works fine, even on docker, but requires a later Debian image, otherwise it crashes the JVM.)

@jpg0 jpg0 requested a review from digitaldan as a code owner October 30, 2024 00:01
@jpg0 jpg0 force-pushed the iaqualink-devices-v2 branch 2 times, most recently from f91ecad to f112a3f Compare October 30, 2024 00:53
</properties>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk.iotdevicesdk</groupId>
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you can join @clinique to get rid of this SDK. For details see: #16893 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did actually have a look at using an alternative MQTT client, because it was originally such as pita to get the CRT working. The challenges that I had were:

  • Should support MQTTv5
  • Should work over Websockets
  • Should support AWS Signature V4 authentication

I'm sure that it's possible, but I doubt that it's simple. I would also question the longevity and applicability of what we were to build, given that AWS appear happy to build their own custom clients and protocol. We'd need to maintain a parallel pure-Java implementation.

I am sympathetic to the arguments regarding packaging the CRT, however given that AWS have such a strong position in IOT I would expect many bindings to have to interface with AWS, and hence wonder if a better solution would be to create a CRT OSGi for all bindings to use (instead of package themselves)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The alternative client to use would be the Paho MQTT client. It does look like it supports MQTTv5 over websockets, although not everything that AWS requires, for example: eclipse-paho/paho.mqtt.java#830, eclipse-paho/paho.mqtt.java#858 etc.

It should be coming, but it's not there yet.

Copy link
Contributor

Choose a reason for hiding this comment

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

I searched a long time but found no alternative to get logged in with HiveMQ. There are very rare informations available when you try to escape AWS SDK.

Copy link
Contributor

Choose a reason for hiding this comment

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

PAHO client seems abandoned. So there does not seem to be an alternative. I'm no expert in OSGI packages and if it would be usefull to add this SDK as OSGI package. Maybe @wborn can give some guidance?

Copy link
Contributor

@lsiepel lsiepel Nov 1, 2024

Choose a reason for hiding this comment

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

Adding support for forward slashes to the hivemq client would probably be best, but i can't asses how much work that or even if that is possible at all. Maybe create an issue for the specific hivemq here: https://github.com/hivemq/hivemq-mqtt-client

Copy link
Contributor

@clinique clinique Nov 2, 2024

Choose a reason for hiding this comment

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

I can get all the tokens, the problem I have is constructing the signed URL to connect to the broker (over wss). This is for a sigV4 URL, you may be using another approach like mtls etc?

It's a customAuthorizer :

MqttClientConnection connection = AwsIotMqttConnectionBuilder.newDefaultBuilder()
.withClientId("WX/USER/%s/%s/%s".formatted(userId, MQTT_USERNAME, productUuid))
.withEndpoint(endpoint).withUsername(MQTT_USERNAME).withCleanSession(false).withKeepAliveSecs(300)
.withConnectionEventCallbacks(this).withWebsockets(true)
.withWebsocketHandshakeTransform(handshakeArgs -> {
              HttpRequest httpRequest = handshakeArgs.getHttpRequest();
              httpRequest.addHeader("x-amz-customauthorizer-name", AUTHORIZER_NAME);
              httpRequest.addHeader("x-amz-customauthorizer-signature", tok[2]);
              httpRequest.addHeader("jwt", tok[0] + "." + tok[1]);
              handshakeArgs.complete(httpRequest);
}).build();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@clinique That is certainly different to my case; I do not have all those fields (such as a username).

Copy link
Contributor Author

@jpg0 jpg0 Nov 2, 2024

Choose a reason for hiding this comment

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

@lsiepel I've spent a little more time on this and my conclusion is that we have a few choices:

  • Ship the AWS SDK in the bundle. Possibly it can be shaved down a bit when shading, although given that much of it is JNI I'm not expecting much.
  • Ship the AWS SDK as a shared bundle.
  • Go with Hive: this means that we'd need to get a change into Hive, which unfortunately is down to an issue with the JDK URLEncoder, which doesn't escape forward slashes (or + signs). No chance of getting this changed there, and I'm afraid that Hive may be reluctant to deviate from it's behaviour. Seems some people say that escaping isn't required and others (e.g. Amazon) say that it is. The problem arises due to the signature calculation which is based on the URI. Whilst Hive allows you to pass a String, it puts in into a URI object (then gets it back out), which means that you cannot provide a raw querystring. Pre-encoded query string is decoded hivemq/hivemq-mqtt-client#643
  • Go with Paho: Whilst I have it working, it comes with 2 caveats: 1. It relies on a commit which whilst is about 4 years old, does not yet appear in an official release. 2. Paho seems somewhat abandoned, hence the first problem.

I also had a quick look to see if I could find any other suitable libraries. There is nothing in Java, the only thing close I found was https://github.com/davidepianca98/KMQTT/ which is Kotlin. Personally I'd love to see Kotlin shipped with openHAB, however that's another very heavy bundle.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would like @wborn's advice regarding the SDK. wdyt about adding it as a OSGi bundle?

@digitaldan
Copy link
Contributor

Thanks @jpg0 ! Nice to have someone else help out with this binding ! Question for you, is only a subset of iAqualink devices available via MQTT, or have they enabled this across all iAqualink products? The REST API the binding currently uses is kinda rough, was always hoping they would replace it with something a little more thought through.

@jpg0
Copy link
Contributor Author

jpg0 commented Oct 30, 2024

@digitaldan I don't know, but it wouldn't surprise me if it has enabled across all. I don't believe that the latest Android app supports the REST API (well, except for the initial connection, like this implementation), unlike if you visit the web app, which only supports the REST API.

Do you have an iaqualink device to test with? If you use the new handler and point it to a new but different device then it should work, but not create any channels - however if you set logging to debug you'll get a dump of the JSON properties of the device, indicating that it is supported.

@digitaldan
Copy link
Contributor

Thanks, I will try and test that out this weekend with my aqualink controller and see if we can consolidate to a single Mqtt handler.

@jpg0
Copy link
Contributor Author

jpg0 commented Oct 30, 2024

Great! It should be fairly straightforward to add support for other channels, although I've implemented it in quite a different way to the V1 binding. Potential channels are defined via JsonPath expressions (in Channels.java) which are checked when the device JSON is retrieved. Seemed simpler (and more change-tolerant) than trying to map the JSON to POJOs.

@jpg0 jpg0 force-pushed the iaqualink-devices-v2 branch from f112a3f to 09b1483 Compare October 31, 2024 00:40
@jpg0 jpg0 force-pushed the iaqualink-devices-v2 branch from 09b1483 to 14037a4 Compare December 7, 2024 01:58
@jpg0
Copy link
Contributor Author

jpg0 commented Dec 7, 2024

Just added support for "boost_time"

@jpg0
Copy link
Contributor Author

jpg0 commented Dec 7, 2024

@digitaldan Did you ever have a chance to test this out?

@lolodomo lolodomo added the enhancement An enhancement or new feature for an existing add-on label Dec 9, 2024
@jpg0 jpg0 force-pushed the iaqualink-devices-v2 branch from 14037a4 to b5c0d21 Compare December 18, 2024 08:24
@digitaldan
Copy link
Contributor

Sorry, i have not yet, slipped my mind. I'll try and find some time this week to test it out.

@digitaldan
Copy link
Contributor

Hi @jpg0 I gave this a try, but am getting the following error. I'm guessing this may be b/c mqtt is not yet enabled for iaqualink controllers?

(i replaced my ids with xxxxxx)

Trying https://p-api.iaqualink.net/v1/mobile/session.json?actionID=command&command=get_onetouch&serial=xxxxxx&sessionID=xxxxxx
08:32:36.000 [TRACE] [alink.internal.v2.api.IAqualinkClient] - MQTT connecting...
08:32:36.510 [ERROR] [alink.internal.v2.api.IAqualinkClient] - MQTT connect failed: Remote endpoint rejected the CONNECT attempt by returning an unsuccessful CONNACK

@jpg0
Copy link
Contributor Author

jpg0 commented Dec 22, 2024

I'm confused as to where the https://p-api.iaqualink.net/v1/mobile/session.json is coming from, as it's hardcoded to go elsewhere (https://prm.iaqualink.net/v2/devices.json). Have you recreated the Thing? It needs to be the iAquaLink Pool Controller - New type (I created a new Thing type and kept the existing one around.)

@digitaldan
Copy link
Contributor

digitaldan commented Dec 22, 2024

Yeah, i created a new V2 thing, but that line was probably from the the other thing and was mixed in the logging, if i pause my original thing, and just run the new v2 one:

08:02:41.056 [INFO ] [hab.event.ThingStatusInfoChangedEvent] - Thing 'iaqualink:controllerV2:test' changed from UNINITIALIZED (DISABLED) to INITIALIZING
08:02:41.059 [INFO ] [alink.internal.v2.api.IAqualinkClient] - Refreshing authentication tokens
08:02:41.890 [TRACE] [alink.internal.v2.api.IAqualinkClient] - Trying https://prm.iaqualink.net/v2/devices.json?user_id=xxxxx&timestamp=1734883456&signature=c2ab4307e6855fc5d9363c1e4dca8773373e8178
08:02:42.191 [TRACE] [alink.internal.v2.api.IAqualinkClient] - Response HttpContentResponse[HTTP/1.1 200 OK - 306 bytes]
08:02:42.192 [DEBUG] [ualink.internal.v2.IAqualinkV2Handler] - Using serial number xxxxxx
08:02:42.201 [TRACE] [alink.internal.v2.api.IAqualinkClient] - MQTT connecting...
08:02:42.984 [ERROR] [alink.internal.v2.api.IAqualinkClient] - MQTT connect failed: Remote endpoint rejected the CONNECT attempt by returning an unsuccessful CONNACK

@jpg0
Copy link
Contributor Author

jpg0 commented Dec 27, 2024

Hmm, not sure, but that may well be the case. Do you use the iaqualink mobile app? That works for me and I studied the traffic to determine what it was doing (aws mqtt in my case). Not sure if the mobile app can also fall back to rest/http though.

@digitaldan
Copy link
Contributor

How did you observe the network traffic? I tired MITMProxy, but it looks like they may be using certificate pinning, so i can't see the traffic from the app.

@jpg0
Copy link
Contributor Author

jpg0 commented Dec 28, 2024 via email

@digitaldan
Copy link
Contributor

was this the android app then ? I was trying on my iPhone. I'll try using the android version and see if that makes a difference.

@jpg0
Copy link
Contributor Author

jpg0 commented Dec 28, 2024

Yes, Android

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An enhancement or new feature for an existing add-on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants