Jandy iAqualink Pool Integration

If you want to get to local only and no cloud you can go the route I did.

Buy the controller here

https://www.autelis.com

Then use the HA REST sensor to query and parse the system status and template switches to control your programs.

Works 100% great here and on it’s 3rd season.

Thanks for the autelis link, but that method doesn’t work for the newer iAqualink devices. The RS485 4-wire bus is discontinued and the only method is cloud-based.

I’ve succeeded in getting a reply from the server. Any personal information is blanked out with XXXX.
The following response is received -

{
    "username": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "email": "[email protected]",
    "first_name": "XXX",
    "last_name": "XXXXXX",
    "address": "XXXXXXXXXXX\n, XXXXXXXXX, XX, au, XXXX",
    "address_1": "XXXXXXXXXXX",
    "address_2": null,
    "city": "XXXXXXXXXXX",
    "state": "XX",
    "country": "au",
    "postal_code": "XXXX",
    "id": "XXXXXX",
    "authentication_token": "XXXXXXXXXXXXXXXXXXXX",
    "session_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "created_at": "2019-12-09T03:12:57.000Z",
    "updated_at": "2019-12-09T03:12:57.000Z",
    "time_zone": "Australia/XXXXXX",
    "phone": "XXXXXXXXXX",
    "opt_in_1": "1",
    "opt_in_2": "0",
    "role": "customer",
    "cognitoPool": {
        "appClientId": "XXXXXXXXXXXXXXXXXXXXXXXXXX",
        "poolId": "us-east-1_XXXXXXXXX",
        "region": "us-east-1"
    },
    "credentials": {
        "AccessKeyId": "XXXXXXXXXXXXXXXXXXXX",
        "SecretKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "SessionToken": "XXXXXXXXXX.............XXXXXXXXXXXX",
        "Expiration": "2020-04-26T05:24:59.000Z",
        "IdentityId": "us-east-1:XXXXXXXXXXXXXXXXXXXXXXXXXX"
    },
    "userPoolOAuth": {
        "AccessToken": "XXXXXXXXX...............XXXXXXXXXXXX",
        "ExpiresIn": 3600,
        "TokenType": "Bearer",
        "RefreshToken": "XXXXXXXXX..........XXXXXXXXXXXX",
        "IdToken": "XXXXXXXXXXXXXX.........XXXXXXXXXXXXXXXX.."
    }
}

The problem I’m having is the subsequent GET request for the pH, ORP, status etc.

What specifically from the response above are you using for

authorization:xxxxxx

and

mydevicecodexxxxx

???

First you need to get list of devices and their codes
authentication_token as authorization
id as clientid

curl 'https://support.iaqualink.com/devices.json?api_key=xxxxxx&authentication_token=xxxxxxxxx&user_id=xxxxxxx' -H "Host:support.iaqualink.com" -H "accept:application/json" -H "user-agent:okhttp/3.12.0" -H "content-type:application/json"  

As answer you will get the devices serial numbers.

Next, you can activate/deactive the chlorinator changing the production from 0 to 1

curl -X POST -H "Host:prod.zodiac-io.com" -H "accept:application/json" -H "authorization:xxxxxxxxxxxx" -H "content-type:application/json; charset=UTF-8" -H "content-length:62" -H "accept-encoding:gzip" -H "user-agent:okhttp/3.12.0" -d '{"state":{"desired":{"equipment":{"swc_0":{"production":0}}}}}' "https://prod.zodiac-io.com/devices/v1/DEVICESERIALNUMBER/shadow"

The authorization is the IdToken field you get from the authentication curl.

From here, my problem is that I don’t know python :frowning:
It must be easy to upgrade the existing aqualink component to include the zodiac chlorinator…

Thanks! One step closer now.

[
{
id: XXXXXX,
serial_number: "JXXXXXXXXX",
created_at: "2020-01-07T09:41:41.000Z",
updated_at: "2020-01-07T09:41:41.000Z",
name: "Z400 iQ TD7",
device_type: "hpm",
owner_id: null,
updating: false,
firmware_version: null,
target_firmware_version: null,
update_firmware_start_at: null,
last_activity_at: null,
},
{
id: XXXXXX,
serial_number: "JTXXXXXXXX",
created_at: "2019-12-09T03:18:18.000Z",
updated_at: "2019-12-09T03:18:18.000Z",
name: "eXO-Pro iQ",
device_type: "exo",
owner_id: null,
updating: false,
firmware_version: null,
target_firmware_version: null,
update_firmware_start_at: null,
last_activity_at: null,
},
]

For activating the Chlorinator, you mentioned using the IDToken. Is that the really, really long string?

Also, in an earlier post, you mentioned a curl GET command for retrieving the pH, ORP, status etc. Is that also the really, really long string (IDToken)?

Yes

I removed the message because the CURL command is partially wrong, I need to turn on chlorinator (some issues with wifi) and test it again. Anyway the authentication long code is the same.

Thanks. I’ll be keen to find out what the working command is to get the pH, ORP etc. when you test it again.

When you say ‘activate/deactivate the chlorinator’ - do you mean switching the whole chlorinator system on and off (including pump electrical supply)? Or just the Salt Water Generator on and off?

Wow, that is horrible. Would never buy a pool control system that was cloud only.

does anyone else experience state changes from available to unavailable all the time for this integration?

My logbook is filled with them… what’s weird is the iAquaLink app never has a problem loading… but home assistant reports this integration available / unavailable all the time…

@flauran. Any chance we can make unavailable reporting optional? It also makes all of the switches and climates go gray whenever a poll of states fails.

Cloud only? It can be operated manually…

Not sure how does the pump go as I don’t have zodiac pump, but a generic pump, but I think YES. Basically it’s like when you open the app and turn on the chlorinator, it automatically turns on the pump.

:laughing: Yes…I should have mentioned the ancient practice of walking to the device and pushing buttons.

Regarding turning on the chlorinator - I had implemented a work-around for this already. I wired a potential free relay onto the back of the chlorinator ON/OFF switch - direct to the circuit board traces. This is controlled from my home automation system for energy management.

Zodiac needs to realise that a pretty wifi app is insufficient for a modern smart home.

And here you can find the CURL to get the ph, orp, temperature, …

curl -X GET -H "Host:prod.zodiac-io.com" -H "accept:application/json" -H "authorization:"$Authorization"" -H "accept-encoding:gzip" -H "user-agent:okhttp/3.12.0" "https://prod.zodiac-io.com/devices/v1/XXXXXXXXX/shadow"

Thanks. I’m trying to integrate this with a system that doesn’t do curl as such. I need to convert the GET curl command into a URL format. If you were to put that GET command into a browser, any idea how you would structure it?

Im not sure if that’s actually possible, don’t you have a linux / windows?

1 Like

From my reading, GET commands should be able to be composed as a single line URL.

The home automation system I’m using (Loxone) is a bit limited when it comes to sending curl.

Notwithstanding, I have both a Linux machine (Raspberry Pi) and Windows (using Postman) to experiment with.

Hi there, new to HA (migrating from Vera), but I’m seeing an error with some of my devices. Some work fine, but others, I see an error message like below, this is for a pump that pushes water down the slide:

2020-04-28 08:00:10 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection.140301969632912] ‘devices_screen’
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py”, line 134, in handle_call_service
connection.context(msg),
File “/usr/src/homeassistant/homeassistant/core.py”, line 1232, in async_call
await asyncio.shield(self._execute_service(handler, service_call))
File “/usr/src/homeassistant/homeassistant/core.py”, line 1255, in _execute_service
await handler.func(service_call)
File “/usr/src/homeassistant/homeassistant/helpers/entity_component.py”, line 213, in handle_service
self._platforms.values(), func, call, required_features
File “/usr/src/homeassistant/homeassistant/helpers/service.py”, line 412, in entity_service_call
future.result() # pop exception if have
File “/usr/src/homeassistant/homeassistant/helpers/entity.py”, line 600, in async_request_call
await coro
File “/usr/src/homeassistant/homeassistant/helpers/service.py”, line 443, in _handle_entity_call
await result
File “/usr/src/homeassistant/homeassistant/components/iaqualink/init.py”, line 187, in wrapper
await func(self, *args, **kwargs)
File “/usr/src/homeassistant/homeassistant/components/iaqualink/switch.py”, line 54, in async_turn_on
await self.dev.turn_on()
File “/usr/local/lib/python3.7/site-packages/iaqualink/device.py”, line 136, in turn_on
await self.toggle()
File “/usr/local/lib/python3.7/site-packages/iaqualink/device.py”, line 158, in toggle
await self.system.set_aux(self.data[“aux”])
File “/usr/local/lib/python3.7/site-packages/iaqualink/system.py”, line 151, in set_aux
await self._parse_devices_response®
File “/usr/local/lib/python3.7/site-packages/iaqualink/system.py”, line 118, in _parse_devices_response
if data[“devices_screen”][0][“status”] == “Offline”:
KeyError: ‘devices_screen’

Thanks for the report, please open a bug on HA’s GitHub. Turn on debugging so you’re able to give more details.