[ COMPLETE ]Culligan Connect - Smart HE - Water Softener

I have a water softener from Culligan. Wifi connected. smart app.
features, things! stuff!

Salt level - my app gives good/bad rating, but technician app shoed inches
Liters treated (water usage)
Leak detector
Membrane/brine status
Force Regenerate cycle
Bypass on/off
Away mode (water use while house empty notification)

i am willing to provide account credentials to a developer with experience with creating integrations.
vpn tunneled access can also be scheduled under strict conditions for local snooping of the device.

see if anyone can sniff anything out without a documented API?









I also am very interested in this integration. I also have the Culligan RO system that is WiFi connected. If there is anything I can do to help, please let me know.

4 Likes

With the release of the Water Consumption feature in Home Assistant, I am even more interested in this type of integration. Anyone able to help make this happen?

3 Likes

I’ve done some discovery and written a powershell module to interact with the cloud API hosted by aylanetworks for the water softener I have from Culligan.

Unsure how much time I’ll have to ever turn this into a Home Assistant module.

Currently, the cloud API requires a username, password, an app_secret, and the WiFi DSN of the device. I acquired the app_secret and DSN from proxy traffic, which would make the integration not as easy for anyone to just pick up and login.

Culligan Discovery Repo

1 Like

I explored the local webserver (port 80) a bit more. It really provides functionality for:

  • Wifi (scan, connect, disconnect, logging)
  • Device registration

There is a /regtoken.json endpoint to generate a device registration token. A few of the endpoints like /wifi_status.json and /status.json include the DSN of the device.

DSN is an Ayla “Device Serial Number” and DSNS (which you’ll see in endpoints) is “Devices Serial Numbers”.

While I haven’t found an Ayla integration, this could be a great cloud integration for Home Assistant as many IoT devices can use the same Ayla API Schema, which is listed: here. These endpoints line up with the limited discovery of the capabilities I observed through proxy traffic from the Culligan App.

Presumably, a Home Assistant integration could obtain it’s own app_secret (needed for communication) by

  1. Obtain a device serial number (DSN)
  2. Obtain the device’s registration token
  3. Register the device with Ayla → assumption that this provides an app secret during registration ←
  4. Use necessary information to interact with the Ayla API

Looking through the docs it appears the app_secret may be vendor specific. So granted by Ayla per vendor (e.g. static for Culligan users).

I found a previous discussion related to an Ayla integrated fan, but have not found a generic Ayla integration.

There must be a way for Ayla to communicate with these devices, but it requires digging through documentation or scanning local devices to find other communication channels.

1 Like

DSN can be found in the culligan connect app.
Settings> System Info> DSN

have you tried using culligan connect app to put the softener on bypass mode, and watch the traffic?

Great that the DSN is easily available.

I don’t anticipate having anytime soon to work on a solution to the project, but am keeping tabs and occasionally looking at other resources.

A possible “copy/paste/update” component to use could be the SharkIQ component: HomeAssistant, Github

Which seems to be an aylanetworks controlled device and would possibly need an automation for reload every 24h (auth token) as described in this issue.

The token refresh issue has been resolved as of the 2023.4.5 minor release.

Has there been any further development for the Culligan Connect integration?

Thanks to funkybunch’s efforts and the SharkIQ code, I started attempting to piece things together. Efforts are documented in two repositories.

Apparently libraries need to be posted to PyPi and then integrated into Home Assistant. Starting towards that route:

Culligan is now more package like. When working with the AylaApi, the previously captured Culligan App_Secret was not allowing me to see devices (but could access profile and other endpoints). I discovered that Culligan is now adding a middle layer connecting to uniapi.culliganiot.com. The uniapi_culliganiot.py file can interact with its basic functionality.

They are currently forwarding the ayla access token, so you can use the uniapi_culliganiot.py to get an access token and then instantiate an AylaApi a la SharkIQ.

Since there could be many more devices using Ayla (Culligan, SharkIQ, Owlet, etc), I wanted to possibly create a more generic library for use.

Ayla-iot-unofficial is an attempt at a package toward that end. The devices could probably be split into their own files, but currently there is a generic Device class and the Vacuum methods were moved into their own class. Softener will become its own, etc.

The other potential issue is that I have no idea how to create or request a ‘Water Softener’ entity in Home Assistant. There is a vacuum entity that SharkIQ uses. Another water softener is implemented via iQua as a sensor. The downside is that sensor entities can’t ‘do’ anything like set the softener to bypass or force regeneration for instance.

Lots of pieces, so anyone can pick it up or assist. Still completely a side project so there is no guarantee of work effort or timeline on my part.

1 Like

Trial and error and eventually got GitHub actions setup to publish the Ayla piece over to PyPi - Ayla-IoT-Unofficial.

Publishing the Culligan piece should be must easier after going through it all once.

1 Like

The Culligan package has been built and published. Now it ‘just’ needs to be integrated into Home Assistant.

2 Likes

thats a great step forward.

so looks like it just needs to be turned into/ used in a HACS integration.

funny enough, i just got a job offer as a culligan installer.

I have no idea where to go from here…

I’m also interested in this. I don’t think it would be too bad to put an integration together for this.

A frustrating journey, but I spent some time to get ‘something’ together. Some notes:

  1. This is not a finished product
  2. There is not robust error handling
  3. Reading data only. No setting of values and no calling of services.
  4. Have no idea if this works with HACS, but ‘it works in my dev environment’.
  5. State is: softening, vacation, bypass and all other data is an attribute. Not sure how to make each value an individual sensor from a setup perspective, but you can do that with template sensors. Also not sure how to set units_of_measurement for attributes if that is possible.
  6. Update interval is 30s for now, so not great if you’re attempting to use this for accurate short flow readings.

Feel free to play around with it, contribute work, fork it, etc. HomeAssistant-Culligan-Water-Softener
There are links in this thread to the repos for the needed libraries in PyPi: Culligan and Ayla-IoT-Unofficial.

1 Like

This is awesome going to give it a try when I get home

I am much happier with the latest update. Data wasn’t updating frequently … the app sends a wifi_report every few seconds. This seems to upload/refresh data regularly. The new ayla library update implements that function and the new integration update adds that to the entity update.

One softener device with many sensor entities and I figured out binary sensors too.

The last thing I want to try is an optional user-settable scan_interval. Far future may try to figure out offering services for mode/valve toggling.

I have not been able to get this to work, not sure what I am doing wrong.
I get the errors below for the most recent HACS version:
Logger: homeassistant.helpers.integration_platform
Source: helpers/integration_platform.py:50
First occurred: 5:18:40 PM (8 occurrences)
Last logged: 5:19:52 PM

Unexpected error importing culligan/system_health.py
Unexpected error importing culligan/logbook.py
Unexpected error importing culligan/media_source.py
Unexpected error importing culligan/silabs_multiprotocol.py
Unexpected error importing culligan/hardware.py
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/integration_platform.py”, line 50, in _async_process_single_integration_platform_component
platform = integration.get_platform(platform_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/loader.py”, line 838, in get_platform
cache[full_name] = self._import_platform(platform_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/loader.py”, line 855, in _import_platform
return importlib.import_module(f"{self.pkg_path}.{platform_name}")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.11/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “”, line 1204, in _gcd_import
File “”, line 1176, in _find_and_load
File “”, line 1140, in _find_and_load_unlocked
ModuleNotFoundError: No module named ‘custom_components.culligan_water_softener.diagnostics’

Logger: aiohttp.server
Source: /usr/local/lib/python3.11/site-packages/aiohttp/web_protocol.py:403
First occurred: 5:18:54 PM (2 occurrences)
Last logged: 5:18:58 PM

Error handling request
Traceback (most recent call last):
File “/usr/local/lib/python3.11/site-packages/aiohttp/web_protocol.py”, line 433, in _handle_request
resp = await request_handler(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.11/site-packages/aiohttp/web_app.py”, line 504, in _handle
resp = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.11/site-packages/aiohttp/web_middlewares.py”, line 117, in impl
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/security_filter.py”, line 85, in security_filter_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/forwarded.py”, line 100, in forwarded_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/request_context.py”, line 28, in request_context_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/ban.py”, line 80, in ban_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/auth.py”, line 236, in auth_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/headers.py”, line 31, in headers_middleware
response = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/view.py”, line 148, in handle
result = await handler(request, **request.match_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/decorators.py”, line 63, in with_admin
return await func(self, request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/config/config_entries.py”, line 213, in post
return await super().post(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/data_validator.py”, line 72, in wrapper
result = await method(view, request, data, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/helpers/data_entry_flow.py”, line 71, in post
result = await self._flow_mgr.async_init(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/data_entry_flow.py”, line 265, in async_init
result = await self._async_handle_step(flow, flow.init_step, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/data_entry_flow.py”, line 394, in _async_handle_step
result: FlowResult = await getattr(flow, method)(user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/config/custom_components/culligan_water_softener/config_flow.py”, line 247, in async_step_init
return await self.async_step_user()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/config/custom_components/culligan_water_softener/config_flow.py”, line 259, in async_step_user
{
File “/config/custom_components/culligan_water_softener/config_flow.py”, line 260, in
vol.Required(x, default=self.options.get(x, True)): bool
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.11/site-packages/voluptuous/schema_builder.py”, line 1152, in init
super(Required, self).init(schema, msg=msg,
File “/usr/local/lib/python3.11/site-packages/voluptuous/schema_builder.py”, line 982, in init
self.schema = Schema(schema)
^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.11/site-packages/voluptuous/schema_builder.py”, line 207, in init
self._compiled = self._compile(schema)
^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.11/site-packages/voluptuous/schema_builder.py”, line 302, in _compile
raise er.SchemaError(‘unsupported schema data type %r’ %
voluptuous.error.SchemaError: unsupported schema data type ‘Platform’

Funny enough, I had documented that error at one point, but didn’t realize how it was triggered.

I was also placing this code directly in my custom_components folder rather than through HACs (which I think will end up doing the same).

The bug was in options flow, which has now been rebuilt to function as intended.

v1 has now been released to the main branch. HACs automatically picked it up. Logos have been submitted to the brands repository. Hopefully, requirements are all pulled down and there are no issues getting users onboarded. Cheers.