Hello, I spent already more than a week to get this working, but I still can not achieve the result I want. I have heat pump from Acond which support communication over ModBus. The problem is that when I set it up it jups between the auto mode and the ModBus mode. In the documentation there is mentioned that it will switch to auto mode when in time = MaxCommDataRefresh there was no value written or read over ModBus. I suspect that this MaxCommDataRefresh is 1s or lower, but Home Assistant only allows me to read in 1s intervals. That is the first problem.
Second problem is that when I setup “climates” it can write only one register, but based on the documentation I need to write multiple registers because some needs to be set to some values. Is there some solution how to write multiple registers when the target temperature is set?
Hi Jakub! I also have an Acond heat pump and I am just getting my bearings around its Modbus interface. Can you tell me where you obtained the documentation you mentioned?
Also, did you make any headway with the issue you described here? I am currently looking to use the heat pump as a sensor source only, and I want to be able to track its reported outside and inside air temperature, and the heating and hot water temperature. In other words, I am currently only interested in the read registers. Since your question was about writes, I suppose you already succeeded at reads before those?
Hello,
here is my configuration.yaml which I use and it works.
You have to change the IP address.
And technicians from Acond must allow Modbus communication on the unit.
With Best Regards Thomas
though I haven’t made much headway yet, I just wanted to say thanks for the pointers thus far.
I have put in this request with the contractor that installed my heat pump, and am still waiting to hear back from them.
I have found this video though; it contains some information about Modbus from about the 14-minute mark:
Unfortunately I don’t speak Czech, and Youtube’s auto-caption/auto-translate feature doesn’t appear to work for this video. So I can’t make out a whole lot. One question that I do have is this, though: do the Modbus registers expose the system’s current power consumption? Is that the ACOND_capacity_actual_W sensor you define, @tnovicky?
Ahoj Jakube,
I see your modbus with ACOND is working. I have ACOND as well, I have paper protocol from ACOND, and I think I made it all exactly according this protocol, but answer is ILLEGAL DATA ADDRESS. Can you send me a couple of packets from communication recorded by wireshark or can you check where should be problem on my side please? I have info from ACOND that it should works, but not in my case. Thank you Vasek.
Hello,
below is my configuration.yaml. I think I copied it correctly from you, but I can’t get the correct result.
After restarting HA it gives me an error:
Logger: homeassistant.setup
Source: setup.py:190
First occurred: 11:41:39 (2 occurrences)
Last logged: 11:41:40
Setup failed for sensors: Integration not found.
Setup failed for climates: Integration not found.
Could you please help me with what I am doing wrong.
I’m sorry, but I’m a beginner with HA, so please be patient and forgiving.
thank you very much
Hello, did You solve the problem? If not I will try to help, but prefer email conversation. So send Your question to [email protected].
With Best Regards Thomas
Returning to this thread with additional information, just in case someone finds it useful.
I’ve finally added my Acond PRO-R to my Home Assistant configuration via Modbus, using the documentation available from loxone-library as a reference (this appears to be a translation of https://acond.cz/wp-content/uploads/Acond_ModbusTCP-v2.34.pdf though the version IDs don’t match up, and also I’m not sure if the translation comes directly from Acond or from a third party).
Here’s what I’ve added to my modbus configuration thus far:
- name: Acond PRO-R
type: tcp
host: acond.lan # This is a DNS entry in my local network; using an IP address is fine too
port: 502
delay: 1
timeout: 1
message_wait_milliseconds: 1
retries: 1
climates:
- name: ACOND_T_set_indoor1 # Indoor temperature, sensor #1 (I only have one at this time)
slave: 1
address: 1
target_temp_register: 0
input_type: input
data_type: int16
scan_interval: 60
scale: 0.1
precision: 1
max_temp: 28
min_temp: 10
temp_step: 0.1
- name: ACOND_T_set_TUV # Domestic hot water temperature
slave: 1
address: 5
target_temp_register: 4
input_type: input
data_type: int16
scan_interval: 60
scale: 0.1
precision: 1
max_temp: 55
min_temp: 10
temp_step: 0.1
sensors:
- name: ACOND_T_act_indoor1 # Current indoor temperature at sensor #1
slave: 1
address: 1
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: "°C"
- name: ACOND_T_act_TUV # Current domestic hot water temperature
slave: 1
address: 5
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: "°C"
- name: ACOND_T_act_water_back # Current return water temperature in heating circuit
slave: 1
address: 8
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: "°C"
- name: ACOND_T_act_air # Current outside air temperature
slave: 1
address: 9
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: "°C"
- name: ACOND_err_number # General error number, should always be 0 in normal operation
slave: 1
address: 20
data_type: int16
input_type: input
- name: ACOND_err_number_SECMono # SECMono error number, should always be 0 in normal operation
slave: 1
address: 21
data_type: int16
input_type: input
- name: ACOND_err_number_driver # Driver error number, should always be 0 in normal operation
slave: 1
address: 22
data_type: int16
input_type: input
- name: ACOND_capacity_actual_W # Current heat production
slave: 1
address: 23
data_type: int16
input_type: input
device_class: power
unit_of_measurement: "W"
For the ACOND Grandis R/P the configuration is different.
For instance the power settings
I have made some automations to let the heat pump work with TADO.
The house I own is build in 1895. And has over 240 square meters flooring, of which most is ground level.
To make sure the rooms will get the correct temperature, I let TADO decide what the room temperature should be.
config.yaml
# Modbus configuration for Acond Grandis R
modbus: !include modbus.yaml
# Logger for modbud
logger: !include logger.yaml
# Template for the modbus implementation
template: !include template.yaml
# Include scripts configuration
scripts: !include scripts.yaml
# Include the automations
automation: !include automations.yaml
modbus.yaml
- name: acond_grandis_r
type: tcp
host: _host_ip_address_# Change to the IP address of the control unit
port: 502
delay: 1
timeout: 1
message_wait_milliseconds: 1
climates:
- name: ACOND_T_set_indoor1 # Indoor temperature, sensor #1 (I only have one at this time)
slave: 1
address: 0
target_temp_register: 0
input_type: input
data_type: int16
scan_interval: 60
scale: 0.1
precision: 1
max_temp: 28
min_temp: 10
temp_step: 0.1
unique_id: acond_grandis_r_0001
- name: ACOND_T_set_TUV # Domestic hot water temperature
slave: 1
address: 4
target_temp_register: 4
input_type: input
data_type: int16
scan_interval: 60
scale: 0.1
precision: 1
max_temp: 65
min_temp: 10
temp_step: 0.1
unique_id: acond_grandis_r_0002
sensors:
- name: ACOND_T_act_indoor1 # Current indoor temperature at sensor #1
slave: 1
address: 1
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: 'ÂşC'
unique_id: acond_grandis_r_0003
- name: ACOND_T_act_TUV # Current domestic hot water temperature
slave: 1
address: 5
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: 'ÂşC'
unique_id: acond_grandis_r_0004
- name: ACOND_T_act_water_back # Current return water temperature in heating circuit
slave: 1
address: 8
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: 'ÂşC'
unique_id: acond_grandis_r_0005
- name: ACOND_T_act_air # Current outside air temperature
slave: 1
address: 9
data_type: int16
scale: 0.1
precision: 1
input_type: input
device_class: temperature
unit_of_measurement: 'ÂşC'
unique_id: acond_grandis_r_0006
- name: ACOND_err_number # General error number, should always be 0 in normal operation
slave: 1
address: 20
data_type: int16
input_type: input
unique_id: acond_grandis_r_0007
- name: ACOND_err_number_SECMono # SECMono error number, should always be 0 in normal operation
slave: 1
address: 21
data_type: int16
input_type: input
unique_id: acond_grandis_r_0008
- name: ACOND_err_number_driver # Driver error number, should always be 0 in normal operation
slave: 1
address: 22
data_type: int16
input_type: input
unique_id: acond_grandis_r_0009
- name: ACOND_comp_rpm_actual # Current compressor speed
slave: 1
address: 23
data_type: int16
input_type: input
unit_of_measurement: "rpm"
unique_id: acond_grandis_r_0010
- name: ACOND_comp_rpm_max # Maximum compressor speed
slave: 1
address: 19
data_type: int16
input_type: input
unit_of_measurement: "rpm"
unique_id: acond_grandis_r_0011
- name: ACOND_type_reg_pan # Current type regulation
slave: 1
address: 14
data_type: int16
input_type: input
scan_interval: 60
unique_id: acond_grandis_r_0012
- name: ACOND_type_rezim_pan # Heatpump mode
slave: 1
address: 13
data_type: int16
input_type: input
scan_interval: 60
unique_id: acond_grandis_r_0013
- name: ACOND_TC_status # Current status of the heatpump
slave: 1
address: 6
scan_interval: 60
data_type: int16
input_type: input
unique_id: acond_grandis_r_0014
- name: ACOND_TC_set # Set the state of the heatpump using the holding register
slave: 1
address: 5
input_type: holding
data_type: int16
unique_id: acond_grandis_r_0015
- name: ACOND_T_set_indoor1_holding # Set the indoor temperature of the heatpump using the holding register
slave: 1
address: 0
input_type: holding
data_type: int16
unique_id: acond_grandis_r_0016
Template.yaml
- sensor:
# Debugging the bitwise word value of sensor acond_tc_status
- name: "Get_ACOND_TC_status"
state: "{{ states('sensor.acond_tc_status') }}"
attributes:
hp_on: "{% if states('sensor.acond_tc_status') | int | bitwise_and(1) %}on{% else %}off{% endif %}"
hp_operation: "{% if states('sensor.acond_tc_status') | int | bitwise_and(2) %}on{% else %}off{% endif %}"
hp_failure: "{% if states('sensor.acond_tc_status') | int | bitwise_and(4) %}on{% else %}off{% endif %}"
dhw_heating_progress: "{% if states('sensor.acond_tc_status') | int | bitwise_and(8) %}on{% else %}off{% endif %}"
circulation_heating_circuit_1: "{% if states('sensor.acond_tc_status') | int | bitwise_and(16) %}on{% else %}off{% endif %}"
circulation_heating_circuit_2: "{% if states('sensor.acond_tc_status') | int | bitwise_and(32) %}on{% else %}off{% endif %}"
solar_circulation: "{% if states('sensor.acond_tc_status') | int | bitwise_and(64) %}on{% else %}off{% endif %}"
pool_circulation: "{% if states('sensor.acond_tc_status') | int | bitwise_and(128) %}on{% else %}off{% endif %}"
defrosting: "{% if states('sensor.acond_tc_status') | int | bitwise_and(256) %}on{% else %}off{% endif %}"
auxiliary_heating: "{% if states('sensor.acond_tc_status') | int | bitwise_and(512) %}on{% else %}off{% endif %}"
summer_operation: "{% if states('sensor.acond_tc_status') | int | bitwise_and(1024) %}on{% else %}off{% endif %}"
brine_circulation: "{% if states('sensor.acond_tc_status') | int | bitwise_and(2048) %}on{% else %}off{% endif %}"
cooling_operation: "{% if states('sensor.acond_tc_status') | int | bitwise_and(4096) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_0
# Debugging the sensor acond_type_reg_pan
- name: "GET_ACOND_type_reg_pan"
state: "{{ states('sensor.acond_type_reg_pan') }}"
attributes:
acondtherm: "{% if int(states('sensor.acond_type_reg_pan')) == 0 %}on{% else %}off{% endif %}"
equiterm: "{% if int(states('sensor.acond_type_reg_pan')) == 1 %}on{% else %}off{% endif %}"
manually: "{% if int(states('sensor.acond_type_reg_pan')) == 3 %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0012_0
# Debugging the sensor acond_type_rezim_pan
- name: "GET_ACOND_type_rezim_pan"
state: "{{ states('sensor.acond_type_rezim_pan') }}"
attributes:
automatically: "{% if int(states('sensor.acond_type_rezim_pan')) == 0 %}on{% else %}off{% endif %}"
only_heatpump: "{% if int(states('sensor.acond_type_rezim_pan')) == 1 %}on{% else %}off{% endif %}"
only_auxiliary: "{% if int(states('sensor.acond_type_rezim_pan')) == 3 %}on{% else %}off{% endif %}"
off: "{% if int(states('sensor.acond_type_rezim_pan')) == 4 %}on{% else %}off{% endif %}"
manually: "{% if int(states('sensor.acond_type_rezim_pan')) == 5 %}on{% else %}off{% endif %}"
cooling: "{% if int(states('sensor.acond_type_rezim_pan')) == 6 %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0013_0
# Defining all the binary sensors
- binary_sensor:
- name: Heatpump On
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(1) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_1
- binary_sensor:
- name: Heatpump Operation
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(2) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_2
- binary_sensor:
- name: Heatpump Failure
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(4) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_3
- binary_sensor:
- name: Domestic Hot Water heating in progress
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(8) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_13
- binary_sensor:
- name: Circulation Heating Circuit 1
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(16) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_4
- binary_sensor:
- name: Circulation Heating Circuit 2
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(32) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_5
- binary_sensor:
- name: Solar Circulation
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(64) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_6
- binary_sensor:
- name: Pool Circulation
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(128) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_7
- binary_sensor:
- name: Defrosting
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(256) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_8
- binary_sensor:
- name: Auxiliary Heating
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(512) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_9
- binary_sensor:
- name: Summer Operation
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(1024) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_10
- binary_sensor:
- name: Brine Circulation
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(2048) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_11
- binary_sensor:
- name: Cooling Operation
state: "{% if states('sensor.acond_tc_status') | int | bitwise_and(4096) %}on{% else %}off{% endif %}"
unique_id: acond_grandis_r_0014_12
- sensor:
- name: "Heatpump mode"
icon: mdi:state-machine
unique_id: acond_grandis_r_0013_1
state: >-
{% if int(states('sensor.acond_type_rezim_pan')) == 0 %}
"Automatic"
{% elif int(states('sensor.acond_type_rezim_pan')) == 1 %}
"Only Heatpump"
{% elif int(states('sensor.acond_type_rezim_pan')) == 3 %}
"Only Auxiliary" }}
{% elif int(states('sensor.acond_type_rezim_pan')) == 4 %}
"Off"
{% elif int(states('sensor.acond_type_rezim_pan')) == 5 %}
"Manually"
{% elif int(states('sensor.acond_type_rezim_pan')) == 6 %}
"Cooling"
{% else %}
"Unknown"
{% endif %}
- sensor:
- name: "Heatpump temperature regulation"
icon: mdi:state-machine
unique_id: acond_grandis_r_0012_1
state: >-
{% if int(states('sensor.acond_type_reg_pan')) == 0 %}
"Acondtherm"
{% elif int(states('sensor.acond_type_reg_pan')) == 1 %}
"Equitherm"
{% elif int(states('sensor.acond_type_reg_pan')) == 3 %}
"Manually"
{% else %}
"Unknown"
{% endif %}
scripts.yaml
# Generic code to turn on a bit in a bitwise holding register
write_register_turn_on_bit:
sequence:
- service: modbus.write_register
data:
hub: "{{ hub }}"
slave: "{{ slave_id }}"
address: "{{ address }}"
value: "{{ states(sensor_value) | int(default=0) | bitwise_or(2 ** (bit | int - 1)) }}"
- service: homeassistant.update_entity
data:
entity_id: "{{ sensor_value }}"
# Generic code to turn off a bit in a bitwise holding register
write_register_turn_off_bit:
sequence:
- service: modbus.write_register
data:
hub: "{{ hub }}"
slave: "{{ slave_id }}"
address: "{{ address }}"
value: "{{ states(sensor_value) | int(default=0) | bitwise_and(65535 - (2 ** (bit | int - 1))) }}"
- service: homeassistant.update_entity
data:
entity_id: "{{ sensor_value }}"
# Increase the indoor temperature by an int value of the Acond Grandis R
increase_acond_temperature_value:
sequence:
- service: modbus.write_register
data:
hub: "{{ hub }}"
slave: "{{ slave_id }}"
address: "{{ address }}"
value: "{{ int(states(sensor_value)) + (int(temperature_value) * 10) }}"
- service: homeassistant.update_entity
data:
entity_id:
- "{{ sensor_value }}"
- "{{ update_sensor }}"
# Decrease the indoor temperature by an int value of the Acond Grandis R
decrease_acond_temperature_value:
sequence:
- service: modbus.write_register
data:
hub: "{{ hub }}"
slave: "{{ slave_id }}"
address: "{{ address }}"
value: "{{ int(states(sensor_value)) - (int(temperature_value) * 10) }}"
- service: homeassistant.update_entity
data:
entity_id:
- "{{ sensor_value }}"
- "{{ update_sensor }}"
# Set the indoor temperature by an int value of the Acond Grandis R
set_acond_temperature_value:
sequence:
- service: modbus.write_register
data:
hub: "{{ hub }}"
slave: "{{ slave_id }}"
address: "{{ address }}"
value: "(int(temperature_value) * 10)"
- service: homeassistant.update_entity
data:
entity_id:
- "{{ sensor_value }}"
- "{{ update_sensor }}"
Thanks everyone for keeping track of what you have done!
I am currently looking into this myself. However, as enabling Modbus is directly connected with a loss of warranty, I was wondering if anyone checked out how the local web server behaves? I was just briefly looking at it and it seems the client is just sending HTTP requests, such as a POST request for logging in:
POST /SYSWWW/LOGIN.XML HTTP/1.1
Host: 192.168.0.10
Content-Type: application/x-www-form-urlencoded
Priority: u=0, i
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:130.0) Gecko/20100101 Firefox/130.0
Referer: http://192.168.0.10/SYSWWW/LOGIN.XML
Content-Length: 56
USER=acond&PASS=03a9ca25acba8c63ef7d9ec14d4ffb2edc3a66ef
EDIT: Oh, and can someone explain to me, in more detail, what the purpose of the different modes (AcondTherm, EquiTherm, and Manual) is? I have no Acond control unit / sensor installed and am relying on the room thermostats.
Hi Bob, heel erg bedankt voor het delen van jou code bestanden. Ik ben helemaal nieuw met HA en totaal onervaren met code/programmeren maar kom langzaam wegwijs in de werking van de codering en heb jou code (met wat kleine aanpassingen) werkend gekregen op mijn acond PRO. Ik heb nu de modbus.yaml en template.yaml helemaal werkend en ben toegekomen aan het automatiserings gedeelte.
Hier had ik een vraag over, als ik het goed begrijp dan stuur jij de acond opdracht wanneer 1 van de verschillende kamers onder de gewenste temperatuur komt, om de acond thermostaat 2 of 5 graden te verhogen. Dit blijft verhoogd tot
dat alle kamers in idle staan en dan gaat de acond thermostaat weer terug naar 19 graden. Wanneer kies je ervoor om 2 of 5 graden te verhogen, wat houdt die meting <50 in?