nice job ! can you send us the full config file ?
Wow. Great work. The configuration you posted worked “straight out of the box”. Thank you!
Can you please post your full configuration for Systemair (on Github maybe?). That screenshot sure looks awsome
Hi,
Thanks and a happy new year!
All the config files for the VTR300 will be posted when finished, it will take some time due to regular work, family things and because the modbus integration is not straight forward and requires trail and error.
The schedule and fan compensation is now implemented in node red but are not fully finished yet.
Almost all setpoints readback (updating “input_number” and “input_datetime” if locally changed on the HMI panel) and modbus write will be handled through node red, if not it would require 112 automations only for the time schedule because each “hour” and “min” has its own register and because it is nice/best to have the correct value in HA if values are locally changed from the panel on the unit.
Any update on this one?
Slow progress due to lack of time and some serious RAM performance issues as the HA integrations are growing rapidly…
Not so much left dough.
This is done:
- All Modbus R/W is moved to Node-Red.
- Entities are now created in node red instead of loooong yaml files and to get better overview of the integration in general.
- Most automations is moved to Node-Red.
- Flow diagrams/pictures are adapted better.
This is to be done:
- Integrate enable for each time schedule/channel + some testing to verify functionality.
- Split Sch.1 and Sch.2 as the setup now is not mobile friendly
- Look further into the generic thermostat.
- Move the remaining automations to Node-Red.
- This is what i recall at this time, there is probably a few more things.
MInd sharing the code even though it’s not complete?
Would love to see some fresh ideas. Have an older VSR300 unit in my apartment. To make it look neat used MQTT climate component
Now got a new VSR 500 in country house to play around. Just pasted your older code without going through yet. Get some errors like missing scripts e.t.c
P.S. Also what is the use for the compensation variable like the one vifte_hast_komp_sommer_max_temp?
The files are too big to share in the forum, send me a pm and i will send my previous config.
The fan compensation is not a must, i just added it mostly because it’s possible. It might be useful if the outdoor temp is extreemly cold as reducing the fan speed with the fan compensation will reduce the load on the el.heater (lower the cost if wanted) and maybe reduce possibility for condensation inside the ventilation unit if it’s very cold outside (a guess from my side). During the summer i believe the compensation works opposite of the winter compensation by increasing the fan speed if it is hot by setting the summer fan comp. (summer comp is not tested yet, only winter). I hope that answered your questions.
The compensation also helps with regulating the humidity. I have my temperature setpoint set at 25C and ECO offset at 10C so my heater doesn’t start until supply air falls to 15C. I don’t feel the cold draught even when directly under the supply valves and it is much cheaper to heat the air using your main heating system. But I use compensation to lower the volume of air when the temperatures fall below 0C to avoid drying up the house, as absolute humidity is very low in cold air. In the summer it can also be used to avoid high humidity buildup.
Hi,
I have a SAVE VSR 300
To integrate it in HA i used an USB to RS485 stick (https://www.aliexpress.com/item/32428596578.html)
# configuration.yaml entry for a serial connection
modbus:
name: hub1
type: serial
method: rtu
port: /dev/ttyUSB0
baudrate: 9600
stopbits: 1
bytesize: 8
parity: E
timeout: 10
#sensors.yaml
#MODBUS
- platform: modbus
scan_interval: 100
registers:
- name: air_extract_air_temp
hub: hub1
slave: 1
register: 12543
register_type: holding
unit_of_measurement: °C
count: 1
scale: 0.1
# data_type: float
precision: 1
- platform: modbus
scan_interval: 120
registers:
- name: air_outdoor_air_temp
hub: hub1
slave: 1
register: 12101
register_type: holding
unit_of_measurement: °C
count: 1
scale: 0.1
# data_type: float
precision: 1
- platform: modbus
scan_interval: 40
registers:
- name: air_supply_air_temp
hub: hub1
slave: 1
register: 12102
register_type: holding
unit_of_measurement: °C
count: 1
scale: 0.1
# data_type: float
precision: 1
- platform: modbus
scan_interval: 35
registers:
- name: air_overheat_temp
hub: hub1
slave: 1
register: 12107
register_type: holding
unit_of_measurement: °C
count: 1
scale: 0.1
# data_type: float
precision: 1
- platform: modbus
scan_interval: 10
registers:
- name: air_supply_fan
hub: hub1
slave: 1
register: 12400
register_type: input
unit_of_measurement: rpm
count: 1
# scale: 0.1
# data_type: float
precision: 1
- platform: modbus
scan_interval: 10
registers:
- name: air_extract_fan
hub: hub1
slave: 1
register: 12401
register_type: input
unit_of_measurement: rpm
count: 1
# scale: 0.1
# data_type: float
precision: 1
- platform: modbus
scan_interval: 60
registers:
- name: air_rh_sensor
hub: hub1
slave: 1
register: 12135
register_type: holding
unit_of_measurement: ﹪
count: 1
# data_type: float
precision: 0
- platform: modbus
scan_interval: 3
registers:
- name: hrv_active_user_mode
hub: hub1
slave: 1
register: 1160
register_type: input
count: 1
# scale: 0.1
# data_type: float
precision: 0
- platform: modbus
scan_interval: 5
registers:
- name: hrv_active_fan_mode
hub: hub1
slave: 1
register: 1130
register_type: input
count: 1
# scale: 0.1
# data_type: float
precision: 0
- platform: modbus
scan_interval: 5
registers:
- name: hrv_active_temperature
hub: hub1
slave: 1
register: 2053
register_type: input
count: 1
scale: 0.1
# data_type: float
precision: 0
- platform: modbus
scan_interval: 9
registers:
- name: triac_output
hub: hub1
slave: 1
register: 2148
register_type: input
unit_of_measurement: ﹪
count: 1
# data_type: binary
precision: 0
- platform: modbus
scan_interval: 7
registers:
- name: heater_active
hub: hub1
slave: 1
register: 14380
register_type: input
# unit_of_measurement:
count: 1
# data_type: binary
precision: 0
- platform: modbus
scan_interval: 11
registers:
- name: exchanger_active
hub: hub1
slave: 1
register: 14103
register_type: input
# unit_of_measurement:
count: 1
# data_type: binary
precision: 0
- platform: modbus
scan_interval: 10
registers:
- name: humidity_transfer
hub: hub1
slave: 1
register: 2146
register_type: input
count: 1
precision: 0
NodeRed:
[{"id":"d420ee28.a1795","type":"tab","label":"HRV","disabled":false,"info":""},{"id":"95a074eb.9f83c8","type":"server-state-changed","z":"d420ee28.a1795","name":"desired_user_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_select.hrv_desired_user_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":190,"y":80,"wires":[["13548516.dd7a7b"]]},{"id":"13548516.dd7a7b","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n case \"None\":\n msg.payload = 0;\n break;\n case \"Auto\":\n msg.payload = 1;\n break;\n case \"Manual\":\n msg.payload = 2;\n break;\n case \"Crowded\":\n msg.payload = 3;\n break;\n case \"Refresh\":\n msg.payload = 4;\n break;\n case \"Fireplace\":\n msg.payload = 5;\n break;\n case \"Away\":\n msg.payload = 6;\n break;\n case \"Holiday\":\n msg.payload = 7;\n break;\n default:\n msg.payload = 0;\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":80,"wires":[["c4001308.2278c"]]},{"id":"c4001308.2278c","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_user_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"modbus","service":"write_register","entityId":"","data":"{\"address\":1161,\"unit\":1,\"value\":\"{{payload}}\",\"hub\":\"hub1\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":550,"y":80,"wires":[[]]},{"id":"e688476d.119478","type":"comment","z":"d420ee28.a1795","name":"Set desired_user_mode to HRV","info":"","x":230,"y":40,"wires":[]},{"id":"e5eb1534.546788","type":"server-state-changed","z":"d420ee28.a1795","name":"active_user_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.hrv_active_user_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":790,"y":540,"wires":[["7f43431e.622dfc"]]},{"id":"7f43431e.622dfc","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n case \"0\":\n msg.payload = \"Auto\";\n break;\n case \"1\":\n msg.payload = \"Manual\";\n break;\n case \"2\":\n msg.payload = \"Crowded\";\n break;\n case \"3\":\n msg.payload = \"Refresh\";\n break;\n case \"4\":\n msg.payload = \"Fireplace\";\n break;\n case \"5\":\n msg.payload = \"Away\";\n break;\n case \"6\":\n msg.payload = \"Holiday\";\n break;\n case \"7\":\n msg.payload = \"Cooker Hood\";\n break;\n case \"8\":\n msg.payload = \"Vacuum Cleaner\";\n break;\n case \"9\":\n msg.payload = \"CDI1\";\n break;\n case \"10\":\n msg.payload = \"CDI2\";\n break;\n case \"11\":\n msg.payload = \"CDI3\";\n break;\n case \"12\":\n msg.payload = \"PressureGuard\";\n break;\n default:\n msg.payload = \"Error\";\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":1020,"y":540,"wires":[["bd6163e7.6f971"]]},{"id":"c20c703e.58438","type":"comment","z":"d420ee28.a1795","name":"Update UI based on HRV info","info":"Some mode are active for a specific time. After this period USER_MODE return to previous value.","x":220,"y":480,"wires":[]},{"id":"bd6163e7.6f971","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_user_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"input_select","service":"select_option","entityId":"input_select.hrv_desired_user_mode","data":"{\"option\":\"{{payload}}\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1230,"y":540,"wires":[[]]},{"id":"54e9ade.53c3854","type":"server-state-changed","z":"d420ee28.a1795","name":"fan_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_select.hrv_fan_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":160,"y":200,"wires":[["635f286f.219e38"]]},{"id":"635f286f.219e38","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n case \"Off\":\n msg.payload = 0;\n break;\n case \"Low\":\n msg.payload = 2;\n break;\n case \"Normal\":\n msg.payload = 3;\n break;\n case \"High\":\n msg.payload = 4;\n break;\n default:\n msg.payload = 3;\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":200,"wires":[["99d27bff.ee2238"]]},{"id":"99d27bff.ee2238","type":"api-call-service","z":"d420ee28.a1795","name":"Set fan_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"modbus","service":"write_register","entityId":"","data":"{\"address\":1130,\"unit\":1,\"value\":\"{{payload}}\",\"hub\":\"hub1\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":520,"y":200,"wires":[[]]},{"id":"74c6591d.222978","type":"comment","z":"d420ee28.a1795","name":"Set fan_mode to HRV","info":"","x":200,"y":160,"wires":[]},{"id":"431ab7dc.42d818","type":"server-state-changed","z":"d420ee28.a1795","name":"active_fan_mode","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.hrv_active_fan_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":780,"y":660,"wires":[["1c190b23.960205"]]},{"id":"1c190b23.960205","type":"function","z":"d420ee28.a1795","name":"","func":"switch(msg.payload){\n case \"0\":\n msg.payload = \"Off\";\n break;\n case \"2\":\n msg.payload = \"Low\";\n break;\n case \"3\":\n msg.payload = \"Normal\";\n break;\n case \"4\":\n msg.payload = \"High\";\n break;\n default:\n msg.payload = \"Error\";\n}\n\n//msg.payload = { value: msg.payload, 'fc': 6, 'unitid': 1, 'address': 1161 , 'quantity': 1 };\n\nreturn msg;","outputs":1,"noerr":0,"x":1020,"y":660,"wires":[["dc35d906.905f18"]]},{"id":"dc35d906.905f18","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_fan_mode","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"input_select","service":"select_option","entityId":"input_select.hrv_fan_mode","data":"{\"option\":\"{{payload}}\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1220,"y":660,"wires":[[]]},{"id":"bc2e9915.41f348","type":"server-state-changed","z":"d420ee28.a1795","name":"temperature","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_number.hrv_temperature","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":170,"y":320,"wires":[["6656e916.035688"]]},{"id":"6656e916.035688","type":"function","z":"d420ee28.a1795","name":"","func":"msg.payload = msg.payload * 10;\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":320,"wires":[["7e0d4f49.6c978"]]},{"id":"7e0d4f49.6c978","type":"api-call-service","z":"d420ee28.a1795","name":"Set temperature","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"modbus","service":"write_register","entityId":"","data":"{\"address\":2000,\"unit\":1,\"value\":\"{{payload}}\",\"hub\":\"hub1\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":520,"y":320,"wires":[[]]},{"id":"49be9ca4.80aef4","type":"comment","z":"d420ee28.a1795","name":"Set Temperature to HRV","info":"","x":210,"y":280,"wires":[]},{"id":"2a011ba.bae88e4","type":"server-state-changed","z":"d420ee28.a1795","name":"active_temperature","server":"6cc1c546.4430dc","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.hrv_active_temperature","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":790,"y":780,"wires":[["d9027d79.df8c7"]]},{"id":"d9027d79.df8c7","type":"function","z":"d420ee28.a1795","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":1020,"y":780,"wires":[["e1768475.c79418"]]},{"id":"e1768475.c79418","type":"api-call-service","z":"d420ee28.a1795","name":"Set desired_active_temperature","server":"6cc1c546.4430dc","version":1,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.hrv_temperature","data":"{\"value\":\"{{payload}}\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1250,"y":780,"wires":[[]]},{"id":"8849a34f.df6b4","type":"api-current-state","z":"d420ee28.a1795","name":"hrv_active_temperature","server":"6cc1c546.4430dc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"sensor.hrv_active_temperature","state_type":"num","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":810,"y":720,"wires":[["d9027d79.df8c7"]]},{"id":"ae5bc303.c2a4f","type":"api-current-state","z":"d420ee28.a1795","name":"active_user_mode","server":"6cc1c546.4430dc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"sensor.hrv_active_user_mode","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":790,"y":480,"wires":[["7f43431e.622dfc"]]},{"id":"160ed3f2.3e675c","type":"api-current-state","z":"d420ee28.a1795","name":"active_fan_mode","server":"6cc1c546.4430dc","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"sensor.hrv_active_fan_mode","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":790,"y":600,"wires":[["1c190b23.960205"]]},{"id":"bde452f0.74f07","type":"server-events","z":"d420ee28.a1795","name":"","server":"6cc1c546.4430dc","event_type":"home_assistant_client","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"x":220,"y":600,"wires":[["57a15fbe.f93f6"]]},{"id":"57a15fbe.f93f6","type":"switch","z":"d420ee28.a1795","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"running","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":410,"y":600,"wires":[["f44558b1.ee8f48"]]},{"id":"f44558b1.ee8f48","type":"delay","z":"d420ee28.a1795","name":"","pauseType":"delay","timeout":"10","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":540,"y":600,"wires":[["ae5bc303.c2a4f","160ed3f2.3e675c","8849a34f.df6b4"]]},{"id":"6cc1c546.4430dc","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]
HA (basic info):
Maybe someone will help us with graphical interface
Hi! What modbus are you using?
Hi,
I’m using Modbus TCP/IP.
Ethernet directly from the IAM gateway to my switch.
More or less the same as Modbus RTU except that Modbus RTU is a serial interface. Modbus addr. are the same. (there is some advantages using modbus TCP/IP, i’m not an expert, just mentioning it.).
If i didn’t have the IAM gateway, i would use the Modbus RTU. But it all depends on whats easiest for each installation.
Bra jobb!
Honestly I can’t even call myself new in the HA community since I made an account several minutes ago
I bumped into this topic when I tried to look into the possibility to connect IAM to Smartthings. The whole idea of even purchasing IAM is the possibility to hook it up to a movement/water sensor in bathroom so each time we take a shower ventilation goes automatically to Boost.
Systemair not offering something like this is very disturbing, and that cloud server is like an island, nothing outside can get connected to it. But Modbus is another option of course. However, I’m kinda reluctant to spend over 2000 NOK (200€) on a device if I’m not going to use it’s main functionality, cloud!
So I was thinking something like a RS485 to Wifi adapter and connect it to that top JB plug, or even as simple as RS485 to ethernet RJ45 converter. Is this doable?
But I’m long way from there! I guess first I have to read and master some basics of HA. Then actually install HA on Raspberry PI presumably and go forward from there.
Just an extremely noob question, you are able to control your vent on both HA Android app and Windows 10 app or web server? And no problem to integrate another sensor in the loop, like movement detecttor as mentioned?
As an automation engineer I’m absolutely ashamed I have to ask such noob questions but you have to start somewhere!
Hi,
Welcome, I’m quite a noob with HA myself but i’ll try to help out:)
RS485 to Wifi
I have no experience using RS485 --> Wifi, but i see there there is RS485/Modbus --> Wifi/Ethernet converters, someone else may have experience? I would prefer wired interface, there is RS485–>USB converters available, which seems to be common to use. If i didn’t have the IAM, the RS485–>USB would be my approach.
Other sensors
I’m using netatmo ppm sensor and device tracker to control my VTR300, so there is no problem using other sensors to control the ventilation unit.
Humidity control
There is already a demand control in the unit where it uses it’s own humidity(RH) sensor by increasing the fan speed based on the RH setpoint vs actual RH, maybe that feature is good enough?
I was thinking of trying out that feature, but so far i dont find it nessecary in my case as the humidity from the bathrom already is vented quick and efficient without the humidity control enabled.
Remote control
I’m using the Nabu Casa HA app for remote control, i dont mind paying 5 dollar a month for such a great free open source and well supported integration system, there is others ways to this free but you have to op en a port on your router. My HA runs inside Virtualbox on a 10 year old Win10 laptop, wich soon is to be relocated to a Intel Nuc with Linux and Ubuntu UI.
I hope that answers some of your questions.
That’s what I meant, Modbus RTU/RS485 to Modbus TCP/WiFi
Effectively, that should be the same thing as with IAM.
RS485 to USB converters are extremely cheap, maybe a bit inconvenient since Pi would have to be placed near the ventilation unit.
Btw. I decided to give IAM a chance and purchased it yesterday! Frankly quite dissapointed. Setup is super easy, however the cloud solution is average at best. Really slow refresh rate and quite basic. It’s a totally closed system that doesn’t let anything in/out, meaning, no possibility for 3rd pary sensor integration for programming an event based boost function. And if you wan’t that, you need to set to Modbus/TCP but than you lose the cloud functionality that you’re actually paying such a hefty price for!
There’s no universe in which this thing is worth 200€ and I’m happily returning it early next week … at least I got my FW’s updated
The RH sensor you’re refering to is placed on the exhaust (avtrekk) line. I tested this function and I’m not sure if it’s good. In my case RH was ca 35% inside, with 50% (winter) setpoint. Checking the fan speed in rpm it was set to approx. low. Following some logic, it should measure both inside and outside RH, compare it with setpoint, and if you want to increase it, like in my case, auto mode should provide more flow and eventually lowering the setpoint for inlet air heating to prevent blowing somewhat dried air.
Thanks a lot! I see that I should start with some basics of HA first, for example I don’t even know what API is! From programming languages, I’m familiar with general concepts, C and PLC programming (in which I’m very experienced), but that’s a bit specific. That’s it, I don’t have a clue about object based programming for example.
So, I have a huge wish to learn something and start actually making something useful, just need some time, not so easy with a small child and one more coming soon, but I’ll hope I’ll find some, because this looks like so much fun!
If you don’t mind sharing, do you have background in coding? Or did you just start with it? I hope I can find all the things I need on HA page, not having to wonder arround.
Yes, you can retrieve the same data from both the RS485 serial interface and Modbus TCP using the IAM gateway. If only using the IAM cloud feature you will have “full” control and can change most parameters, functionality/features etc. so you will have more or less full control using the systemair app, but without any options as you say with integrating the system with other systems… If the app is running in the background and it’s being re-opened, the data/values was not updated, maybe that is fixed in the latest release, I’m not sure since I’m not using it.
It would expect that the built in humidity demand control regards the outdoor humidity as well (ref. modbus adr. List as its referring to calculated outdoor humidity as well), don’t have more to say about the feature since I’m not using it but I would expect at least the fan level to respond well in humidity changes to lower the indoor humidity.
Having the same background as you, both HA is and Node Red recently was totally new for me as well, the background is helping a lot. But it’s a new language to learn anyway, and yes it’s fun:) (but only if you have the time available).
If you check the post above from 2’nd December 2020, you will get an idea of how the Modbus integration works in HA. Since I wanted to have HA to be updated if settings was updates locally on the unit panel I had to create automations for each parameter, which would result in a lot of automations (especially the Auto schedule integration), I decided to move the integration to node red (running as a HA addon) which I prefer when writing automations.
My ventilation unit is integrated and works well but I have a few more details to finish before the code I shared on github. Im a little to hung up in details and don’t have much time available so it may take some time…
The main struggle is find the correct modbus registers to read, since the modbus adr. list has flaws… But that should be 98% sorted out now, now I need to find the registers for the Auto schedule Fan speed, though I had that, but that was wrong… And the timestamp of last filter change also has some strange behavior I’m not figuring out, tried byteswapping the registers but it seems like the register isn’t being updated when replacing the filter, maybe a bug or maybe it needs to be set by external command, not sure… (time to next filter change is OK) The timestamp issue is now solved by providing the actual timestamp when the filter alarm is deactivated and it logs the 10 latest filter changes with actual timestamp. Since I’m a noob using Node Red there was a few things to figure out how to make this possible.The function node code (node red) is shared below, as I believe you are curious about that:)
This function node is triggered by falling edge using the filteralarm as input.
There is more, but it will be shared later some at some point.
I don’t mind sharing the code (old HA config and latest Node Red config), send me a pm and I’ll send the code in return since it’s too much to share in the forum (not possible to share due to the size…).
Filter timestamp code in HA: (edit post/forgot this)
Filter timestamp code in node red:
var Recent_change = global.get ("TimeNow_YearDateHour");
//Flytter siste filterskift til forrige filterskifte
var Previous_change_1 = flow.get ("vtr300_forrige_filterskift_0","storeInFile");
var Previous_change_2 = flow.get ("vtr300_forrige_filterskift_1","storeInFile");
var Previous_change_3 = flow.get ("vtr300_forrige_filterskift_2","storeInFile");
var Previous_change_4 = flow.get ("vtr300_forrige_filterskift_3","storeInFile");
var Previous_change_5 = flow.get ("vtr300_forrige_filterskift_4","storeInFile");
var Previous_change_6 = flow.get ("vtr300_forrige_filterskift_5","storeInFile");
var Previous_change_7 = flow.get ("vtr300_forrige_filterskift_6","storeInFile");
var Previous_change_8 = flow.get ("vtr300_forrige_filterskift_7","storeInFile");
var Previous_change_9 = flow.get ("vtr300_forrige_filterskift_8","storeInFile");
var Previous_change_10 = flow.get ("vtr300_forrige_filterskift_9","storeInFile");
var Previous_change_0 = flow.get ("vtr300_siste_filterskift","storeInFile");
//Initilialiser verdi dersom udefinert
if (typeof Previous_change_0 == "undefined") //Forrige_0
Previous_change_0 = 'Not set';
if (typeof Previous_change_1 == "undefined") //Forrige_1
Previous_change_1 = 'Not set';
if (typeof Previous_change_2 == "undefined") //Forrige_2
Previous_change_2 = 'Not set';
if (typeof Previous_change_3 == "undefined") //Forrige_3
Previous_change_3 = 'Not set';
if (typeof Previous_change_4 == "undefined") //Forrige_4
Previous_change_4 = 'Not set';
if (typeof Previous_change_5 == "undefined") //Forrige_5
Previous_change_5 = 'Not set';
if (typeof Previous_change_6 == "undefined") //Forrige_6
Previous_change_6 = 'Not set';
if (typeof Previous_change_7 == "undefined") //Forrige_7
Previous_change_7 = 'Not set';
if (typeof Previous_change_8 == "undefined") //Forrige_8
Previous_change_8 = 'Not set';
if (typeof Previous_change_9 == "undefined") //Forrige_9
Previous_change_9 = 'Not set';
if (typeof Previous_change_10 == "undefined") //Forrige_10
Previous_change_10 = 'Not set';
//Lagrer status til fil for å beholde status "permanent"
flow.set("vtr300_forrige_filterskift_0", Previous_change_0,"storeInFile");
flow.set("vtr300_forrige_filterskift_1", Previous_change_1,"storeInFile");
flow.set("vtr300_forrige_filterskift_2", Previous_change_2,"storeInFile");
flow.set("vtr300_forrige_filterskift_3", Previous_change_3,"storeInFile");
flow.set("vtr300_forrige_filterskift_4", Previous_change_4,"storeInFile");
flow.set("vtr300_forrige_filterskift_5", Previous_change_5,"storeInFile");
flow.set("vtr300_forrige_filterskift_6", Previous_change_6,"storeInFile");
flow.set("vtr300_forrige_filterskift_7", Previous_change_7,"storeInFile");
flow.set("vtr300_forrige_filterskift_8", Previous_change_8,"storeInFile");
flow.set("vtr300_forrige_filterskift_9", Previous_change_9,"storeInFile");
flow.set("vtr300_forrige_filterskift_10", Previous_change_10,"storeInFile");
flow.set("vtr300_siste_filterskift", Recent_change,"storeInFile");
msg.payload = Previous_change_0;
return msg;
Thanks a lot!
It’s funny how I understand in principle the stuff you’re talking about, but don’t have a slightest clue about implementation … if only it would be as “easy” as programming Siemens PLC hehe
I don’t even have the slightest clue what “yaml” and “code red” even means, so I guess I have a loong way to go, hope I’ll be able to find some time soon, we moved into the newbuilt house several weeks ago and now the things are starting to settle in place finally!
I do have some experience with both Modbus RTU/TCP but everything through S7/Tia Portal so basically you already get the user freindly blocks to connect. But really, in industrial/marine/offshore automation Profibus and more and more Ethernet are the way to go!
Thanks a lot for you willing to share the code! I’m sure I’ll get back to you once I master the basics and have some clue
Oh! And one general tip regrding the vent unit filters. There is a guy I know that was working for one of those ventilation companies (can’t remember which one but it was the one I had before in appartment, Flexit maybe?) and he revealed the dirty little secret, which I kinda already assumed.
You can wash/vacume/blow the filters and as long as the fabric is intact you can re-use them many many times over. It was allways suspicious to me why every single company is pointing out that they are one-time use … well, for 50-60€ it surely is a very lucrative business to them
I think the same applies to Systemair.
And regarding Systemair, I find their business practices extremely lame! In order to connect wirelessly you need to spit out 200€, their cloud is an island, closed system and their sulutions are quite lame as well! They do have a motion sensor, but guess what, only the wired option, for a hefy price of some 150€ as I remember. And Modbus TCP functionality can be achieved with 20€ adapter, Modbus RTU to USB for 1€! And that’s probably the way I’ll be aiming. Pi with HA on it could be placed on the top of vent unit, why not
Delivered power + energy & Outdoor compensated supply temperature:
Delivered power:
From i got the unit installed late last summer i somehow wondered how much power and energy the unit deliver compared to the power and enrgy it uses. After some googling it seemed like “Sensible heat” is a way of calculating delivered power, after some head scratching/Node Red learning i managed to make the calculations. This is a new area for me so id like to share my findings/way of estimate added power+energy and true added power+energy. I was looking for a estimate of how efficient the unit is, by that i have calculated estimated delivered kW/10s and accumulate kW/10s each 10 sec. To give the “true delivered” energy i just subtracted the energy consumption. Do anyone have the knowledge if “Sensible heat” and “Energy consumption” is comparable?
Code etc. shared further below.
Ref. pictures below: The accumulation was not reset during last night and continued counting from yesterdays value because the Windows host rebooted at about 23:00 yesterday… Thats why the reset failed and there is a straight line in the trends. To be “fixed” by relocating HA to Intel Nuc sometime in the future.
Outdoor compensated supply temperature:
Instead of manually setting the temperature setpoint Id like the supply temperature to be adjusted automatically according to outdoor temperature changes, its probably not gona have much to say with the indoor temperature but it will prevent the heat exchanger from running when it’s not nessecary and maybe extend its lifetime. So i did some plots on the calculator (Stat) and made the calculator find the formula and constants. Then some more head scratching/Node Red learning had to be done. What i want is to have the X and Y plots as temperature setpoints, does anyone know how this is achievable with Node Red?
Code below:
Delivered power:
// hs := cp*ρ*q*dt
// Eks.= (1.006 kJ/kg°C) (1.202 kg/m3) (1 m3/s) ((20°C) - (0°C))
//hs := sensible heat (kW)
//cp := specific heat of air (1.006 kJ/kg°C)
//p := density of air (1.202 kg/m3) or "press/(Rspecific*t)"
//q := air volume flow (m3/s)
//dt := delta temperature delta [°C]
//press := absolute pressure (Pa)
//t := absolute temperature (kelvin)
//Rspecific := specific gas constant for dry air (J/(kg'K)) [Rspec]
var q_pv = flow.get ("vtr300_tilluftsvifte_pv","default");
var t_supply = flow.get ("vtr300_tillufts_temperatur","default");
var t_inlet = flow.get ("vtr300_inntak_temp","default");
var press = 101325;
var t = t_inlet + 273.15;
var Rspec = 287;
// Sjekker for "isNotNumber, ved "isNan" (opretting av Modbus kom./feil sensor data) tidlig "exit" (prøv igjen).
if (isNaN(q_pv) || isNaN(t_supply) || isNaN(t_inlet) || isNaN(t)) {
return;
}
var cp = 1.006;
//var p = 1.202;
var p = press/(Rspec*t);
var q = (q_pv * 3)/3600;
var dt = t_supply - t_inlet;
var hs = cp*p*q*dt;
// Lagrer watt og kW til memory for videreberegning av faktisk levert estimert effekt
flow.set("vtr300_tilført_Watt", hs*1000,"default"); // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWatt", hs,"default"); // Lagrer tilført kW til "memory/default"
// Estimert levert effekt & energi total
var watt = {payload:(hs*1000).toFixed(0)};
var kW = {payload:(hs).toFixed(2)};
var kW_10s = {payload:(hs/360)}; // kW/10s
// Tilpasser desimaler for attribute visning i HA
flow.set("vtr300_tilført_Watt_formated", (hs*1000).toFixed(0) +' Watt',"default"); // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWatt_formated", (hs).toFixed(2) +' kW',"default"); // Lagrer tilført kW til "memory/default"
return [watt,kW,kW_10s];
Delivered energy:
// Polles hvert 10s pga. payload = "kW/10s"
var kW_10s = msg.payload;
var Previous_kWh_tot = flow.get ("vtr300_tilført_kWh_cnt_tot","default"); // Total eneregiteller - Flytter siste akkumulerte verdi til forrige verdi
var Previous_kWh_tot_daily = flow.get ("vtr300_tilført_kWh_cnt_tot_daily","default"); // Daglig energi
var Previous_kWh_tot_weekly = flow.get ("vtr300_tilført_kWh_cnt_tot_weekly","default"); // Ukentlig energi
var Previous_kWh_tot_monthly = flow.get ("vtr300_tilført_kWh_cnt_tot_monthly","default"); // Månedlig energi
var Previous_kWh_tot_yearly = flow.get ("vtr300_tilført_kWh_cnt_tot_yearly","default"); // Årlig energi
var kWh_tot;
var kWh_tot_daily;
var kWh_tot_weekly;
var kWh_tot_monthly;
var kWh_tot_yearly;
// Totalteller energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot == undefined) {
kWh_tot = 0.0;
}
// Totalteller energi - Akkumulerer forrige verdi med ny verdi
else {
kWh_tot = Previous_kWh_tot + kW_10s;
}
// Daglig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_daily == undefined) {
kWh_tot_daily = 0.0;
}
// Daglig energi - Akkumulerer forrige verdi med ny verdi
else {
kWh_tot_daily = Previous_kWh_tot_daily + kW_10s;
}
// Ukentlig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_weekly == undefined) {
kWh_tot_weekly = 0.0;
}
// Ukentlig energi - Akkumulerer forrige verdi med ny verdi
else {
kWh_tot_weekly = Previous_kWh_tot_weekly + kW_10s;
}
// Månedlig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_monthly == undefined) {
kWh_tot_monthly = 0.0;
}
// Månedlig energi - Akkumulerer forrige verdi med ny verdi
else {
kWh_tot_monthly = Previous_kWh_tot_monthly + kW_10s;
}
// Årlig energi - Initilialiser verdi dersom udefinert/første gang verdi settes
if (Previous_kWh_tot_yearly == undefined) {
kWh_tot_yearly = 0.0;
}
// Årlig energi - Akkumulerer forrige verdi med ny verdi
else {
kWh_tot_yearly = Previous_kWh_tot_yearly + kW_10s;
}
//Oppdaterer med nyeste akkumulerte verdi (Scantid må være 10s pga. mottatt verdi er kW/10s)
flow.set("vtr300_tilført_kWh_cnt_tot", kWh_tot,"default"); // Total eneregiteller - Akkumulering i "memory/default", restore fra "file"
flow.set("vtr300_tilført_kWh_cnt_tot_daily", kWh_tot_daily,"default"); // Daglig energi - Akkumulert
flow.set("vtr300_tilført_kWh_cnt_tot_weekly", kWh_tot_weekly,"default"); // Ukentlig energi - Akkumulert
flow.set("vtr300_tilført_kWh_cnt_tot_monthly", kWh_tot_monthly,"default"); // Mý�nedlig energi - Akkumulert
flow.set("vtr300_tilført_kWh_cnt_tot_yearly", kWh_tot_yearly,"default"); // Årlig energi - Akkumulert
var kW_10s_out = {payload:kW_10s};
var kWh_tot_daily_out = {payload:(kWh_tot_daily).toFixed(2)};
var kWh_tot_weekly_out = {payload:(kWh_tot_weekly).toFixed(2)};
var kWh_tot_monthly_out = {payload:(kWh_tot_monthly).toFixed(2)};
var kWh_tot_yearly_out = {payload:(kWh_tot_yearly).toFixed(2)};
var kWh_tot_out = {payload:(kWh_tot).toFixed(2)};
// Tilpasser desimaler og benevnelse for attribute visning i HA
flow.set("vtr300_tilført_kWh_cnt_tot_formated", (kWh_tot).toFixed(2) +' kWh',"default"); // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_daily_formated", (kWh_tot_daily).toFixed(2) +' kWh',"default"); // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_weekly_formated", (kWh_tot_weekly).toFixed(2) +' kWh',"default"); // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_monthly_formated", (kWh_tot_monthly).toFixed(2) +' kWh',"default"); // Lagrer tilført kW til "memory/default"
flow.set("vtr300_tilført_kWh_cnt_tot_yearly_formated", (kWh_tot_yearly).toFixed(2) +' kWh',"default"); // Lagrer tilført kW til "memory/default"
//return [kW_10s_out,kWh_tot_daily_out,kWh_tot_weekly_out,kWh_tot_monthly_out,kWh_tot_yearly_out,kWh_tot_out];
True power:
var watts_consumption = msg.payload;
var Watt_delivered = flow.get ("vtr300_tilført_Watt","default");// Tilført effekt Watt estimert
var kWatt_delivered = flow.get ("vtr300_tilført_kWatt","default"); // Tilført effekt kW estimert
// Faktisk effekt produsert (produsert - levert)
var true_produced_Watts = Watt_delivered - watts_consumption;
var true_produced_kW = kWatt_delivered - (watts_consumption/1000);
// Faktisk effekt produsert, tilpasse data ut
var true_produced_Watts_out = {payload:(true_produced_Watts).toFixed(0)};
var true_produced_kW_out = {payload:(true_produced_kW).toFixed(2)};
return [true_produced_Watts_out,true_produced_kW_out];
Outdoor compensated supply temperature:
var x= msg.payload; // utetemp.
// X (ute temp) og Y (ønsket tilluft) plot:
// X1 :7,5, Y1 :22,0
// X2 :10,0, Y2 :20,0
// X3 :12,5, Y3 :18,0
// X4 :15,0, Y4 :17,0
// X5 :17,5, Y5 :15,0
// X6 :20,0, Y6 :15,0
//Fjerdgradslikning for best treff av X og Y plot
// 𝑦=𝑎𝑥^4+𝑏𝑥^3+𝑐𝑥^2+𝑑𝑥+𝑒
// Graf & konstanter ses ved å plotte X & Y på kalkulator med "Stat"->"Grph"->"Gph"->"X^4"
//gitte variable basert på X og Y plot:
var a = (1.0666e-03);
var b = (-0.0562962);
var c = (1.09555555);
var d = (-9.9465608);
var e = (55.3809523);
var y; //Ønsket tilluftstemp.
//Nødvendige regler for ønsket funksjosnalitet (pga. avvik utenfor ytterpunker)
if (x <= 7.5) {
y = 22.0;
}
else if (x >= 20.0) {
y= 15.0;
}
// Beregner plot
else {
y = (a * Math.pow(x, 4)+b*Math.pow(x, 3)+c*Math.pow(x,2)+d*x+e);
}
msg.payload = parseFloat((y).toFixed(1));
return msg;
Outdoor compensated supply temperature:
Found a way of making X and Y as setpoints from HA to calculate the supplytemperature according to the outdoor temperature, sharing the code below for others in the mean time. Y4, Y5 and Y6 setpoints are given some “test” values just to display/show that the calculated supplytemp. actually follows the calculated line between the points. Normally i would want the calculated supplytemp to be 22 degrees with the given outdoor temperature.
// X :Y(ute temp) og X (ønsket tilluft) plot:
// X1 :5,0, Y6 :22,0 (Eks.) - Slope 1
// X2 :7,5, Y5 :21,0 (Eks.) - Slope 1
// X3 :10,0, Y4 :20,0 (Eks.) - Slope 2
// X4 :12,5, Y3 :18,0 (Eks.) - Slope 2
// X5 :15,0, Y2 :17,0 (Eks.) - Slope 3
// X6 :20,0, Y1 :15,0 (Eks.) - Slope 3
// Slope = "change in Y/change in X=deltaY/deltaX"
var x= msg.payload; // utetemp.
var Y_output; // Ønsket tilluftstemp.
// Utetmp plot:
var X1 = flow.get ("vtr300_tilluftstemp_utekomp_x1","default"); // Henter settpunkt fra HA, lagrer i memory.
var X2 = flow.get ("vtr300_tilluftstemp_utekomp_x2","default");
var X3 = flow.get ("vtr300_tilluftstemp_utekomp_x3","default");
var X4 = flow.get ("vtr300_tilluftstemp_utekomp_x4","default");
var X5 = flow.get ("vtr300_tilluftstemp_utekomp_x5","default");
var X6 = flow.get ("vtr300_tilluftstemp_utekomp_x6","default");
// Ønsket tillufts temp. plot:
var Y6 = flow.get ("vtr300_tilluftstemp_utekomp_y6","default"); // Henter settpunkt fra HA, lagrer i memory.
var Y5 = flow.get ("vtr300_tilluftstemp_utekomp_y5","default");
var Y4 = flow.get ("vtr300_tilluftstemp_utekomp_y4","default");
var Y3 = flow.get ("vtr300_tilluftstemp_utekomp_y3","default");
var Y2 = flow.get ("vtr300_tilluftstemp_utekomp_y2","default");
var Y1 = flow.get ("vtr300_tilluftstemp_utekomp_y1","default");
var slope_1 = (Y6-Y5)/(X1-X2);
var slope_2 = (Y4-Y3)/(X3-X4);
var slope_3 = (Y2-Y1)/(X5-X6);
var Y_out_1 = Y6+slope_1*(x-X1);
var Y_out_2 = Y4+slope_2*(x-X3);
var Y_out_3 = Y2+slope_3*(x-X5);
// Sjekker for "isNotNumber, ved "isNan" tidlig "exit" (ved evt. feil ved HA reconnect ell.) (prøv igjen).
if (isNaN(Y_out_1) || isNaN(Y_out_2) || isNaN(Y_out_3)) {
return;
}
// Bruker Y6 som for max ytterpunkt
if (x < X1) {
Y_output = Y6;
}
//Rak linje for X1, X2, Y1 & Y2
else if (x >= X1 && x < X3) {
Y_output = Y_out_1;
}
//Rak linje for X3, X4, Y3 & Y4
else if (x >= X3 && x < X5) {
Y_output = Y_out_2;
}
//Rak linje for X5, X6, Y5 & Y6
else if (x >= X5 && x <= X6) {
Y_output = Y_out_3;
}
// Bruker Y1 som min ytterpunkt
else if (x > X6) {
Y_output = Y1;
}
// Bruker Y6 som min ytterpunkt
else {
Y_output = Y6;
}
msg.payload = parseFloat((Y_output).toFixed(1));
return msg;
Need some help in order to improve the Modbus response so i can finish the HA and Node Red Systemair modbus integration.
I believe i have found the cause to be “queue” building up over time, causing modbus writing to be very slow as the modbus “queue” is building up.
Its strange, but it doesn’t effect the modbus reading/polling. Restarting Node-Red flush the “queue”, but the slow modbus writing comes back with the “queue” build up. (the queue reset does not work)
I have tried several attempts to minimize the modbus poll and write rate but it does not help…
I have not found any errors or warning in the addon log related to this…
Don’t find much info about this other than that others, also have this issue and have reverted to modbus node ver. 4.1.3, which seems to have cleared their “queue” build up.
So to the question:
-
Anyone running “node-red-contrib-modbus” older than ver. 5.13.3, which i’m running, that can verify that they don’t have “queue” build up? and which version are you running?
-
Im running HA supervised on Virtual Box running Linux/ubuntu and trying to figure out how to downgrade the modbus node “node-red-contrib-modbus” to ver. 4.1.3. It’s probably easy, any hints?
Tried “sudo npm install [email protected]” without luck…
All help is appreciated so i can finish this integration, this “queue” issue is stopping me from getting any further progress…
Edit:
It seems like it after all was too many registers being polled to frequent causing the queue build up, maybe because max data traffic is reached or something.
I “just” had to lower the polling rate much more (lowered polling rate on all schedule time registers) than previous attempts, guess i got hung up in the wrong details… response is now good again.
Screenshot below regarding the “queue” building up (4211…), this is after approx. 24 hours runtime. If downgrading the node-red-contrib-modbus nodes is the “solution”, i would be very happy!
Thanks in advance.
Your integration is too awsome! I’m sorry, im running the same version as you do.
I’ve taken the time for some adustments now and it works great on my vtr250B. Had to rename some entities and remove presence detection in PPM mode.
Thanks again for the great work!