Hargassner heating integration

Hi all!

I have recently started using Home Assistant and instantly liked the system and the neat GUI! In addition to lights, covers, and room temperature controls, I thought it would be great to also have data about the actual heating in there. I have a new Hargassner pellet heating in the basement, which even has a network plug, and it would be a pity not to use this, right?

Unfortunately, there is no official documentation about this network plug, but I have found some bits and pieces of information on the internet and started experimenting. The result, after one week of coding nights, is this:

As you can see, the integration adds a number of sensors to HA which show the current state of the heating, lots of different temperatures, but also error states. I have cleaned everything up and put the code of the custom component here:

https://github.com/TheRealKillaruna/nano_pk

If you happen to have a Hargassner heating system, feel free to try it out and let me know how it works in this thread. If feedback is good, I plan to release the underlying API to communicate with the heating as seperate Python library to make the HA component independent from it (as is recommended, I understand).

Cheers,
Tobias

1 Like

Hi!
Nice job. I’ll instal same type of heating system by HARGASSNER in couple of month. Keep you posted!

Hello,
I did my own integration for a Hargassner Nano too…
highly inspired by Jahislove very good job.

I kept
outside temperature (from boiler sensor),
Pellet consumption,
Boiler status
Temperatures of the cirduit and fumes.
because that was basically what I wanted.

Here is how it looks now :

I am using a script launched every 5 mintues + stats sensors :
I am using for 1 year now and am very satisfied.

Here is the script if it can help.

import socket               
import time
from datetime import date,datetime,timedelta
import sys
import os.path
import logging
import json
import sys

IP_CHAUDIERE = 'xxx.xxx.xxx.xxx'
PORT = 23  
Trace_dict = {"state" : 1,
                       "T_ext" : 25.3,
                       "T_ext_moy" : 25.8,
                       "consigne" : 30,
                       "T_depart" : 36,
                       "T_fumees": 45.5,
                       "T_chaudiere": 90.4,
                       "niv_silo" : 5000,
                       "conso_pellet_cumul" : 2000,
                       "conso_silo" : 5,
                       "conso_tremie" : 7,
                       "statut" : 0,
                       "statut B" : 0
                       }

status = {1 : "arret",
                2 : "allumage",
                3 :  "demarrage chaudiere",
                4 :  "controle allumage",
                5 :  "allumeur", 
                6 :  "demarrage combustion",
                7 :  "combustion",
                8 :  "veille",
                9 :  "decendrage dans 7 mn",
                10 : "decendrage",
                11 : "refroidissement",
                12 : "nettoyage"
                 }

#----------------------------------------------------------#
#        definition des logs                               #
#----------------------------------------------------------#
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger('log')
logger.setLevel(logging.DEBUG)                    # choisir le niveau de log : DEBUG, INFO, ERROR...

handler_debug = logging.FileHandler("trace.log", mode="a", encoding="utf-8")
handler_debug.setFormatter(formatter)
handler_debug.setLevel(logging.DEBUG)
logger.addHandler(handler_debug)
#----------------------------------------------------------#

#----------------------------------------------------------#
#        socket for Connection to Hargassner               #
#----------------------------------------------------------#
time_start=time.time()
while ((time.time() - time_start) < 5):
        try:      
          s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
          s.connect((IP_CHAUDIERE, PORT))
          logger.info("Creation du socket telnet OK")
          break
        except:
          logger.critical("Connexion a la chaudiere impossible")
          print("erreur")
#-----------------------------------------------------------#

time_start=time.time()
while ((time.time() - time_start) < 5):
      try:
         buffer = s.recv(600)                                            # waiting a packet (waiting as long as s.recv is empty)
         if buffer[0:2] == b'pm':
          bufferOK = buffer
          logger.debug("BufferOK")
          break
         else:
          logger.debug("buffer ERREUR pm")
      except:
        logger.error("buffer ERREUR lecture")
#----------------------------------------------------------#                                                   
try:
    if bufferOK[0:2] == b'pm':
      datebuff = time.strftime('%Y-%m-%d %H:%M:%S') #formating date for mySQL
      buff_liste=bufferOK.split()                            # transforme la string du buffer en liste 
      logger.debug(buff_liste)
      buff_liste[0] = datebuff                                  # remplace la valeur "pm" par la date
      logger.debug (buff_list)
except :
      logger.error('le if pm est KO, buffer non defini')

logger.debug(buff_liste)

Trace_dict['state']=status[(int(buff_liste[1]))]
Trace_dict["T_ext"]=float(buff_liste[16])
Trace_dict["T_ext_moy"]=float(buff_liste[17])
Trace_dict["consigne"]=float(buff_liste[58])
Trace_dict["T_depart"]=float(buff_liste[57])
Trace_dict["T_chaudiere"]=float(buff_liste[4])
Trace_dict["T_fumees"]=float(buff_liste[6])
Trace_dict["niv_silo"]=int(buff_liste[47])
Trace_dict["conso_pellet_cumul"]=int(buff_liste[48])
Trace_dict["statut"]=int(buff_liste[55])
Trace_dict["statut B"]=int(buff_liste[56])

if len(sys.argv) > 2:
        Trace_dict["conso_silo"]=max(int(sys.argv[1])- int(buff_liste[47]), 0)
        Trace_dict["conso_tremie"]=max(int(buff_liste[48])-int(sys.argv[2]), 0)
else:
        Trace_dict["conso_silo"]=0
        Trace_dict["conso_tremie"]=0

print(json.dumps(Trace_dict))    

And here the sensors (that I added in my conf file) :

sensor:      
  - platform: command_line
    name: chaudiere
    command: "python3 /config/python_scripts/HargassnerSensor.py {{ states('sensor.silo_granules') | int }} {{ state_attr('sensor.chaudiere','conso_pellet_cumul') | int }}"
    value_template: '{{ value_json.state }}'
    json_attributes:
      - T_ext
      - consigne
      - T_depart
      - T_fumees
      - niv_silo
      - conso_pellet_cumul
      - T_ext_moy
      - T_chaudiere
      - conso_silo
      - conso_tremie
    scan_interval : 300
    
  - platform: statistics
    name: stat_conso_pellet_24h
    entity_id: sensor.conso_granules
    max_age:
      hours: 24
    sampling_size: 300
 
  - platform: statistics
    name: stat_conso_pellet_7d
    entity_id: sensor.conso_granules
    max_age:
      hours: 168
    sampling_size: 2030
     
  - platform: statistics
    name: stat_deg_h_chauffage_7d
    entity_id: sensor.deg_h_chauffage
    max_age:
      hours: 168
    sampling_size: 2030

  - platform: template    
    sensors:
      silo_granules:
        value_template: >-
               {{ state_attr('sensor.chaudiere','niv_silo')|int }}
        unit_of_measurement: 'kg'

      t_exterieure:
        value_template: >-
                {{ state_attr('sensor.chaudiere','T_ext')|int }}
        unit_of_measurement: '°C'

      deg_h_clim:
        value_template: >-
                {{ [((state_attr('sensor.chaudiere','T_ext')|int)-18), 0]|max }}

      deg_h_chauffage:
        value_template: >-
                {{ [(18-(state_attr('sensor.chaudiere','T_ext')|int)), 0]|max }}

      conso_granules_24h:
        value_template: >-
               {{ state_attr('sensor.stat_conso_pellet_24h','total')|int }}
        unit_of_measurement: 'kg'

      conso_granules_7d:
        value_template: >-
               {{ state_attr('sensor.stat_conso_pellet_7d','total')|int }}
        unit_of_measurement: 'kg'

      conso_chauffage_7d_euro:
        value_template: >-
               {{ ((state_attr('sensor.stat_conso_pellet_7d','total')*0.28)|float) | round(2) }}
        unit_of_measurement: '€'


      conso_granules:
        value_template: >-
               {{ state_attr('sensor.chaudiere','conso_silo')|int }}
        unit_of_measurement: 'kg'
  
      deg_j_chauffage_7d:
        value_template: >-
               {{ (state_attr('sensor.stat_deg_h_chauffage_7d','mean')|float * 24)|int }}
        unit_of_measurement: '°.Jour'

Hello ALL,

Nice job and thanks to you and jahislove.

I’ve also an Hargassner NANO PK 20, ans a Home assistant.

Do you know the network order to send to the Hargassner to change the mode?

Cause with my home assistant, I’m able to know if my Family is at home, and with this information I would like to order in automatic the boiler. For example, the boiler has a program for the weekend, and if we are not at home, the home assistant launch a stop order to the boiler in automatic.

Actually, I’ve also the Hargassner app and the internet gateway, but it’s not possible to do this in automatic.

Thanks in advance.

Bye from France.
Jacki.

Hi,
A few Time ago i have started to experiment some changing on my nano pk.
I have the request to do it with postman, but never take the Time to do it properly in python for an HA integration.
Tell me if you are interested, i Can push it to a gitlab

Hi Jacki,

great that you find the integration useful! Unfortunately, I have no idea if and how the heating could be controlled (e.g. mode change) by this protocol. The data I am using is sent automatically by the heating over Telnet, and it is relatively straight-forward to decipher if you consult the documentation and cross-check values with your heating. I am not aware of any documentation for sending data to the heating, though, and I guess you’d need to intercept traffic between the official Hargassner IP module (which I don’t have) and the heating to get an idea of how things are implemented for this direction…

Best wishes,
Tobias

Hi Yoonie,
I’m not brave enough to send undocumented controls to my heating, I wouldn’t know how to fix it if I break something, and these things are too expensive to play around with :wink:
Hoever, I am sure there would be people interested in your code if it really works!

Best wishes,
Tobias

Hi there,

Thanks a lot for this integration, it was a must have for my HA. Works like a charm with my NanoPK.2 on v14l.

Here are the states translated to French, adding the Stanby state (code 8) :

    UNKNOWN_STATE = "?"
    STATES = {
        "1" : {CONF_LANG_DE:"Aus", CONF_LANG_EN:"Off", CONF_LANG_FR:"arrêt"},
        "2" : {CONF_LANG_DE:"Startvorbereitung", CONF_LANG_EN:"Preparing start", CONF_LANG_FR:"Preparation démarrage"},
        "3" : {CONF_LANG_DE:"Kessel Start", CONF_LANG_EN:"Boiler start", CONF_LANG_FR:"Démarrage chaudière"},
        "4" : {CONF_LANG_DE:"Zündüberwachung", CONF_LANG_EN:"Monitoring ignition", CONF_LANG_FR:"Contrôle allumage"},
        "5" : {CONF_LANG_DE:"Zündung", CONF_LANG_EN:"Ignition", CONF_LANG_FR:"Allumage"},
        "6" : {CONF_LANG_DE:"Übergang LB", CONF_LANG_EN:"Transition to FF", CONF_LANG_FR:"Transition combustion"},
        "7" : {CONF_LANG_DE:"Leistungsbrand", CONF_LANG_EN:"Full firing", CONF_LANG_FR:"Combustion"},
        "8" : {CONF_LANG_DE:"Standby", CONF_LANG_EN:"Standby", CONF_LANG_FR:"Veille"},
        "9" : {CONF_LANG_DE:"Warten auf EA", CONF_LANG_EN:"Waiting for AR", CONF_LANG_FR:"Décendrage dans 7mn"},
       "10" : {CONF_LANG_DE:"Entaschung", CONF_LANG_EN:"Ash removal", CONF_LANG_FR:"Décendrage"}},
       "12" : {CONF_LANG_DE:"Putzen", CONF_LANG_EN:"Cleaning", CONF_LANG_FR:"Nettoyage"},
       UNKNOWN_STATE : {CONF_LANG_DE:"Unbekannt", CONF_LANG_EN:"Unknown", CONF_LANG_FR:"Inconnu"}
    }

Hey Tobias,

I uploaded the nano_pk folder to /config/custom_components/nano_pk added the code to /config/configuration.yaml as well but I got the error below. Can’t figure it out since I have several other plugins installed in the custom_components folder which work fine

Would you mind and give me a tipp?

Your help is appreciated!
Markus

EDIT: I had to add the directory first, restart HA, then I added the code to configuration.yaml, then restart HA again. If you do both at once, HA will refuse to restart because the config check will fail. - In the end it worked like a charm with the custom message formats from my PK20 :wink: Thanks!

Hey Tobias,

sorry for bothering again but I have the problem that the integration won’t update anymore. If I change the parameters: STANDARD to FULL in the configuration.yaml everything works fine again.

Maybe you can help me out here?

Appreciated!

Best Markus

hello everyone,

I have a problem with my HA.
No value goes up. Can you help me?
I have a not unique id message

I have the same issue.
My firmware is 14N2 and i saw this is already prepared

in the configuration.yaml i added
nano_pk:
host: 192.168.178.119
msgformat : NANO_14N2
devicename: Nano-PK
parameters: FULL
language: DE

but no values are present. The IP is certainly ok

Any ideas ?

Hello everyone,

Same problem for my installation, no data.

below are the logs by portainer:
HargassnerBridge._update(): Received message has unexpected length.
HargassnerBridge._update(): Received message has unexpected length.
HargassnerBridge._update(): Received message has unexpected length.
HargassnerBridge._update(): Received message has unexpected length.
2022-12-02 22:02:16.246 ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.nano_pk_energy_consumption fails
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/entity.py”, line 527, in async_update_ha_state
await self.async_device_update()
File “/usr/src/homeassistant/homeassistant/helpers/entity.py”, line 722, in async_device_update
raise exc
File “/usr/local/lib/python3.10/concurrent/futures/thread.py”, line 58, in run
result = self.fn(*self.args, **self.kwargs)
File “/config/custom_components/nano_pk/sensor.py”, line 127, in update
self._value = 4.8 * float(self._bridge.getValue(self._paramName))
TypeError: float() argument must be a string or a real number, not ‘NoneType’

Thank you

Franck

Hi there,

Any chance to get this integration to work with the new v14_n3 firmware ?

Hi,

maybe i’m just too dumb, but is there a way to add two Hargassner devices using your integration?

With kind regards,
Linus

Hi @elektr0nisch, the integration is designed for a single Hargassner device, there is currently no way to add multiple.

Hi @CalamityJenn, this should certainly be possible, please check GitHub - TheRealKillaruna/nano_pk: Home Assistant integration of Hargassner Nano-PK

Hey @TheRealKillaruna - I’ve just updated to Homeassistant 2022.12.8 - Unfortunately all entities are gone, 2022.12.6 worked fine though! Any advice?

Hello @all,

same Problem with Version 2022.12.8 here, still started with it and i´m absoluteley unable do get it work. @TheRealKillaruna i´m also from frankonia :smile: perhaps you could help me in german?

Thank you all!

this is the failure that i get:

Logger: homeassistant.components.sensor
Source: helpers/entity_platform.py:224
Integration: Sensor (documentation, issues)
First occurred: 22:17:45 (1 occurrences)
Last logged: 22:17:45

Setup of platform nano_pk is taking longer than 60 seconds. Startup will proceed without waiting any longer.

Hi everyone,

I am Oliver and same new to home assistant and to the whole field of home automation. I was happy to find this integration to help me by improving the consumption of my Hargassner boiler.

All fine until this morning the new core_2023 release was installed. Since then none of the sensors is detected anymore. Details I found are following:

Logger: homeassistant.components.sensor
Source: custom_components/nano_pk/hargassner.py:119
Integration: Sensor (documentation, issues)
First occurred: 08:24:10 (1 occurrences)
Last logged: 08:24:10

Error while setting up nano_pk platform for sensor
Traceback (most recent call last):
File “/usr/local/lib/python3.10/site-packages/apscheduler/schedulers/base.py”, line 896, in _create_plugin_instance
plugin_cls = class_container[alias]
KeyError: ‘interval’

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 3037, in _dep_map
return self.__dep_map
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 2834, in getattr
raise AttributeError(attr)
AttributeError: _DistInfoDistribution__dep_map

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 3028, in _parsed_pkg_info
return self._pkg_info
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 2834, in getattr
raise AttributeError(attr)
AttributeError: _pkg_info

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/entity_platform.py”, line 289, in _async_setup_platform
await asyncio.shield(task)
File “/usr/local/lib/python3.10/concurrent/futures/thread.py”, line 58, in run
result = self.fn(*self.args, **self.kwargs)
File “/config/custom_components/nano_pk/sensor.py”, line 25, in setup_platform
bridge = HargassnerBridge(host, uniqueId, msgFormat=format)
File “/config/custom_components/nano_pk/hargassner.py”, line 119, in init
self._scheduler.add_job(lambda:self._update(),‘interval’,seconds=updateInterval)
File “/usr/local/lib/python3.10/site-packages/apscheduler/schedulers/base.py”, line 420, in add_job
‘trigger’: self._create_trigger(trigger, trigger_args),
File “/usr/local/lib/python3.10/site-packages/apscheduler/schedulers/base.py”, line 921, in _create_trigger
return self._create_plugin_instance(‘trigger’, trigger, trigger_args)
File “/usr/local/lib/python3.10/site-packages/apscheduler/schedulers/base.py”, line 899, in _create_plugin_instance
plugin_cls = class_container[alias] = plugin_container[alias].load()
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 2470, in load
self.require(*args, **kwargs)
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 2493, in require
items = working_set.resolve(reqs, env, installer, extras=self.extras)
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 803, in resolve
new_requirements = dist.requires(req.extras)[::-1]
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 2755, in requires
dm = self._dep_map
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 3039, in _dep_map
self.__dep_map = self._compute_dependencies()
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 3048, in _compute_dependencies
for req in self._parsed_pkg_info.get_all(‘Requires-Dist’) or []:
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 3030, in _parsed_pkg_info
metadata = self.get_metadata(self.PKG_INFO)
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 1431, in get_metadata
value = self._get(path)
File “/usr/local/lib/python3.10/site-packages/pkg_resources/init.py”, line 1635, in _get
with open(path, ‘rb’) as stream:
FileNotFoundError: [Errno 2] No such file or directory: ‘/usr/local/lib/python3.10/site-packages/pytz-2022.7.dist-info/METADATA’

Is there any advice from anyone how to fix that?