Viron Astral Pool ChlorinatorGo integration

also would be great if this was possible

Hi @Mikkaat, how have you been going with this?

Hi, I only just got back onto this. I do not know how to code so am slowly working my way through this.

1 Like

Would love to get this working…

These videos might help as well:

this would be great if someone could get this going

Hi all, just an update, I have extracted the Service UUID, Characteristic UUID and Values for the pump settings of Auto, Manual, Off, and the pump speeds of High, Medium Low. I am not in any way shape or form able to code so will try to take a look at the videos to see what i can do. Happy to post them here if you want to have a go as well.

1 Like

As an update, I have been advised to try the BLE Client — ESPHome and BLE Client Sensor — ESPHome.

I tried these and was able to connect to the controller, and located the characteristics, but unfortunately was not able to control it in any way. I will paste the Service UUID, Characteristic UUID and values here just in case someone wants to give it a go for themselves.

Service UUID UUID Value
4500000198b74e29a03f160174643001 4500000398b74e29a03f160174643001 86683a6d3db48556d5d8aa3d28d8fa4a
4500000198b74e29a03f160174643001
4500000198b74e29a03f160174643001
4500000198b74e29a03f160174643001
4500000198b74e29a03f160174643001 4500020398b74e29a03f160174643001 23cd2194d6be98a6aa388d18062454e44e51016b
4500000198b74e29a03f160174643001 4500020398b74e29a03f160174643001 ecf698e596b01947527bea11441c388923d584aa
4500000198b74e29a03f160174643001 4500020398b74e29a03f160174643001 f82ff7cad5cd1ff6c923e72e6728d5a0cefcd5a6
4500000198b74e29a03f160174643001 4500020398b74e29a03f160174643001 3cf09bfc23695df55d160eaa134a675ba0bf0189
4500000198b74e29a03f160174643001 4500020398b74e29a03f160174643001 63b1ef9a625ebd01139c56d6f18ae0d8d48e7ce5
1 Like

hopefully someone with some knowledge could figure it out, be handy to have it integrated into HA

I’ve just ordered a couple doit esp32 modules. Should arrive in about a month.
@Mikkaat, what did you use to do this BLE sniffing?

@pbutterworth I purchased this from AliExpress and followed the process defined in Stuart Patterson’s videos. The ServiceUUID, UUID and Values are pasted above.

If you get your code running, please share.

thanks

I just got a Viron EQ as well, while the Viron heat pump has Wi-Fi and a few other bits of my home plant have Wi-Fi, the EQ is an oddity with offering only Bluetooth.

Keen to help out but my programming skill is low and resources to do so and make boards right now even lower.

Keen to be able to get ORP and pH values out into HA for recording long term trends and notifications of alerts like “low salt”.

Being able to configure via HA doesn’t offer much unless to shift into manual mode out of programmed mode (Eg shift between manual on, manual off and auto mode). Because made in the future id sync EQ into manual on/off to be triggered by solar panel and total house consumption output.

I’ve sniffed the connection between the app and the chlorinator using Wireshark and a nRF dongle.
Looks like the link is not encrypted, but the payload data is meaningless to me. Perhaps the payload itself is encrypted?

Here’s the wireshark capture:

1 Like

So I’m thinking that decompiling the android app might yield some clues to the comms.
I’ve unpacked the android apk, and it appears to be built with Xamarin/.NET - so all the interesting bits are in dll files. These also can be decompiled, but unfortunately the tools to do this don’t work on my M1 silicon macbook.
So I’m a bit stuck…

Happy to help out if i can… (not that I know how, but if you want to guide me through…) what tools are needed?

A quick scan, and i’m by no means a bluetooth expert.

Your pool name is POOL01

The setup of connection between your device and the EQ seems to happen at packets # 915-918

Packets 919 → 930 seem to be setting up the bluetooth link-layer agreements (versions, features, length with the REQ - Request followed by the RSP - Response).

From there I did a quick google to try and filter some noise out, and found this as a copy paste straight into the filter bar:

btle.data_header.length > 0 || btle.advertising_header.pdu_type == 0x05

This shows only connection requests and non-zero data packets. (brings 3288 captured packets down to 182)

For reference the “Slave_0xaf9a8dad” is the EQ and the “Master_0xaf9a8dad” is the bluetooth device/application.

it would be nice to know what you are doing within the app at various time through the capture. But applying a tiny bit of ethernet experience (which may be way wrong), it doesn’t look like data payload was captured.

eg: packet request 1206 and response 1209 seem to be a fetch of what your Device Name is which is POOL01

So focusing on 1206 & 1209 for a little bit of learning, and this “Bluetooth Assigned Numbers” document ( Assigned Numbers (windows.net) )

We see in packet 1209, that it has inside Bluetooth Attribute Protocol, “Attribute Data” Handle 0x0003. The “Device Name” POOL01 is responded with 0x2a00. Which from the document link above is on page 83/393 “device Name” “0x2A00”.

But I cannot make out much else, unless it’s buried in the “unknown” space of the bluetooth spectrum, or if we don’t know what you were doing during the capture. Hope thats a start?

Thanks for that @ianmcginley!
I’m deep down the rabbit hole decompiling the dlls from the android app.

So far I’ve found some juicy snippets, such as:

				if (base.DeviceProtocolRevision == 1)
				{
					ICharacteristic characteristic = GetCharacteristic("45000203-98b7-4e29-a03f-160174643001");
					if (characteristic != null)
					{
						WriteEncryptedCharacteristic(characteristic, appActionCharacteristic2);
					}
				}
				else
				{
					WriteAppAction(appActionCharacteristic2);
				}

Which is inside a function that is called to do stuff such as changing modes.
Looks like there must be a couple of protocol versions, one plain and one encrypted.
Judging by the payloads in the sniffed packets, the device I have is using the encrypted protocol. When you add a chlorinator to the app for the first time, you have to put in a pass code that you read from the device’s screen.

Anyhow, I’ll keep pulling at threads in the decompiled code.

Update:
Seems that it is using the PCLCrypto library, using AES encryption and ECB block mode.
I need to work out how the secret key is determined.

Update:
Here’s the sequence of events when connecting:

  • StartConnectionProcedure → ConnectToDevice
  • OnDeviceConnected → DiscoverServices
  • OnServiceDiscovered
    if service UUID = 45000001-98b7-4e29-a03f-160174643001 or 45000001-98b7-4e29-a03f-160174643001
    → DiscoverCharacteristics
  • OnCharacteristicsDiscovered
    if characteristic UUID = 45000002-98b7-4e29-a03f-160174643001 or 45000001-98b7-4e29-a03f-160174643002
    → ReadCharacteristic
  • OnReadCharacteristic (actually called OnSlaveSessionValueUpdated)

In here ^ is where the SlaveSessionKey is read from the characteristic, and used along with the AccessCode to encrypt the MAC…

happy to help where i can… I cannot code, but can extract wireshark messaging

Quick update for anyone who might be interested:

A BLE characteristic is read from the Chlorinator immediately after connecting. This data is then manipulated in a variety of different ways to generate a SessionKey, which is used for encrypting/decrypting subsequent messages.

There is a ‘SecretKey’ which is instantiated in the code, which appears impossible to determine from the decompiled code that I have. Without this, no progress can be made. I’ve asked on StackOverflow for some help. I really need someone experienced in this kind of hacking.

So, not looking good at this stage. Will post any further updates.

1 Like

Thanks for it all so far. I’m really hoping not to pay my way into a connect10 system which will solve this. But it may be the most likely option.

Update:
Glimmer of hope on the horizon!

I’m managed to find the ‘SecretKey’ tucked way in a deep dark corner of the disassembly.
Also this string: “AstralPoolService.BusinessObjects.Chlorinator.ChlorinatorDevice”, “ChlorinatorApp.App”, which gets flipped around, converted to a byte array and a SHA1 hash is calculated from said array.
The hash is then XORed with SecretKey to derive the actual key used for the AES encoding/decoding.

The first attribute that gets read from the chlorinator (characteristic uuid 45000002) is the ‘sessionKey’.

Then the sessionKey gets XORed with the AccessCode (read from the screen of the chlorinator), and the result is AES encrypted using the key mentioned above. This forms the MAC key which is written back to the chlorinator (characteristic uuid 45000003).

Anyway, long story short, I can successfully generate the MAC key from the received sessionKey.
AND, I can successfully decode a received packet (!).

For example, this received attribute response from the chlorinator:

[ MANY CALCULATION STEPS ]

:partying_face:
Anyway, that was a pretty short explanation, but it was quite a few hours of tail chasing.

2 Likes