Systemair SAVECare Ventilation unit

The files should just be copied into the save_vsr directory in the custom_components folder

Same. Used last release.
Got error when opening yr extension configuration.

Config flow could not be loaded: 500 Internal Server Error Server got itself in trouble.

Latest Home Assistant.

I put a release which can be downloaded as a zip file Release Working release · moskovskiy82/save_vsr · GitHub

This integration is completely written by ai and tested with usb stick on the VSR 500 unit.
To help i will need more details like when this error comes up. What settings were used and most important - logs

Also to clarify It’s just a foundation to save some time. I asked an integration guru to check the code - and the answer was “it’s written better than if it was written by a professional programmer who didn’t work with pyhon” So for example if tcp connection is not there (my guess why the config_flow fails) - anyone can just feed the init.py, coordinator.py, hub.py to grok/chatgpt/gemini and simply ask to implement it. Other files don’t need the changes. Cross feed the code between the 3 AIs and in 2-3 iterations get a working code ).

Just another one plain ModBus integration.
Tested with VSR300/500. Sometimes got 6/11 busy errors (damn RTU), but works.

Integration now supports all 3 connection methods: ModBus TCP/ModBus Serial and WebAPI (SAVECONNECT 2.0 module)

Not yet.

Traceback (most recent call last):
File “/usr/local/lib/python3.13/site-packages/aiohttp/web_protocol.py”, line 510, in _handle_request
resp = await request_handler(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.13/site-packages/aiohttp/web_app.py”, line 569, in _handle
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.13/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 92, in security_filter_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/forwarded.py”, line 210, in forwarded_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/request_context.py”, line 26, in request_context_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/ban.py”, line 86, in ban_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/auth.py”, line 242, in auth_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/headers.py”, line 41, in headers_middleware
response = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/helpers/http.py”, line 73, in handle
result = await handler(request, **request.match_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/decorators.py”, line 83, in with_admin
return await func(self, request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/config/config_entries.py”, line 225, in post
return await super().post(request, flow_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/components/http/data_validator.py”, line 74, in wrapper
return await method(view, request, data, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/helpers/data_entry_flow.py”, line 121, in post
result = await self._flow_mgr.async_configure(flow_id, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/data_entry_flow.py”, line 346, in async_configure
result = await self._async_configure(flow_id, user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/data_entry_flow.py”, line 393, in _async_configure
result = await self._async_handle_step(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flow, cur_step[“step_id”], user_input
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File “/usr/src/homeassistant/homeassistant/data_entry_flow.py”, line 497, in _async_handle_step
result: _FlowResultT = await getattr(flow, method)(user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/config/custom_components/systemair/config_flow.py”, line 180, in async_step_modbus_serial
await self._validate_serial_connection(user_input)
File “/config/custom_components/systemair/config_flow.py”, line 85, in _validate_serial_connection
if not await client.test_connection():
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/config/custom_components/systemair/api.py”, line 486, in test_connection
await self._ensure_connection()
File “/config/custom_components/systemair/api.py”, line 366, in _ensure_connection
result = await self._client.connect()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.13/site-packages/pymodbus/client/base.py”, line 61, in connect
rc = await self.ctx.connect()
^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.13/site-packages/pymodbus/transport/transport.py”, line 245, in connect
self.transport, _protocol = await asyncio.wait_for(
^^^^^^^^^^^^^^^^^^^^^^^
…<2 lines>…
)
^
File “/usr/local/lib/python3.13/asyncio/tasks.py”, line 507, in wait_for
return await fut
^^^^^^^^^
File “/usr/local/lib/python3.13/site-packages/pymodbus/transport/serialtransport.py”, line 174, in create_serial_connection
transport = SerialTransport(loop, protocol, url,
baudrate,
…<2 lines>…
stopbits,
timeout)
File “/usr/local/lib/python3.13/site-packages/pymodbus/transport/serialtransport.py”, line 29, in init
self.sync_serial = serial.serial_for_url(url, exclusive=True,
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
baudrate=baudrate, bytesize=bytesize, parity=parity, stopbits=stopbits, timeout=timeout
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File “/usr/local/lib/python3.13/site-packages/serial/init.py”, line 87, in serial_for_url
instance = klass(None, *args, **kwargs)
File “/usr/local/lib/python3.13/site-packages/serial/serialutil.py”, line 224, in init
self.bytesize = bytesize
^^^^^^^^^^^^^
File “/usr/local/lib/python3.13/site-packages/serial/serialutil.py”, line 310, in bytesize
raise ValueError(“Not a valid byte size: {!r}”.format(bytesize))
ValueError: Not a valid byte size: ‘8’

Please, create issue if you can at github - i’ll check when my usb-modbus converter reached me. This implementation is fully guessed and guy who requested this support disappeared. Ty in advance.
Edit: Ah, got it. Incorrect types, will be fixed at next release soon. Ty for test.

Excellent news. As i have written above i have used a pair of AIs to write the integration and it’s fully working. But as always AI is an AI so if you get it done - will switch without hesitation and can be quite a beta tester ) Not sure if it will be of any help but GitHub - moskovskiy82/save_vsr hub.py was refactored for compatibility with 2025.10 Also as modbus is pretty slow i had to introduce slow and fast cycle reads for different sensor groups. Otherwise it would just keep all the errors and at some point stop reading sensors
I have a Gen2 Save VSR 500 unit

P.S My knowledge of programming and python is 10 out of 100

I’m released 1.0.9 version with serial client fix.
In my implementation i’m using retry-queue for modbus messages - unit now always online even if modbus device is temporary failed to process messages. Tested with modbus tcp and web api and will be very appreciated if you test few days with serial connection.

Will try it out. Pasted the error log on github. Not sure if it is related to the bumping of pymodbus in 2025.10 but looks very simillar. This is what ai refactored for me to get it working

Summary of changes for pymodbus 3.11.2:

  1. :white_check_mark: Removed method="rtu" from AsyncModbusSerialClient
  2. :white_check_mark: Removed strict=False parameter
  3. :white_check_mark: Changed slave= → device_id= in all read/write calls
  4. :white_check_mark: Used keyword arguments for count= and device_id=

No other files need changes - they all use your hub wrapper methods.

Awesome project!

I have a VTR500 and seems like everything works, but target temperature in the climate entity is zero. When I change it, it resets to zero immidiately. The value in Systemair is not changed.

Hi, thanks for tests. Can you make issue at github? Ill fix that when will be at home

Is there a reason for having the “Filter remaining time” in seconds? Doesn’t tell me much when theres 21 681 279 seconds to next change. I think this would be better in a more understandable format.

Hi, it’s because MODBUS registers provide this value in seconds or months. It make sense to provide this value as it’s provided by unit and let each user to work with it as they need (calculate days to replace, calculate specific date to replace,…)

Hi,
bought waveshare RS485 serial USB [CH343G Waveshare 22456]

But I can't seem to get any signal, readings or so, like nothing works, USB gets loaded and seen on dmesg.
I tried changing all the settings, baud rates, polarities of A+/B- different cables you name it :smiley: what am I missing? Maybe you can advise something how to make this work?