Decoding the Nibe SMOS40 current status

I’ve got a Nibe SMO S40 control unit integrated with Home Assistant over Modbus TCP. I’m trying to understand the output of sensor.current_status_31121. It switches between various numbers depending on what the heat pump is doing.

It looks like the following:

  • 9 = idle
  • 4125 + 4121 = heat
  • 8221 + 8217 = hot water
  • 25 = ?
  • 29 = ?

Does anyone know specifically the difference between 4125 and 4121, and also 8221 and 8217? I presume they’re different phases in the operations, but it’d be useful to have names for them.

Thanks!

I am trying to figure out the same thing. My system is a VVM S320, and I’m seeing slightly different numbers than you. 9 is idle, that we agree on, but I see these numbers in the log:

  • 4105
  • 4109
  • 8201
  • 8205

When the pump starts up there is usually a code of 4109 followed by a 8205 some 10-15 minutes later. But sometimes it starts with a 4101 and/or throws a 8201 before the 8205, or the numbers come in reverse.

I’ve tried googling and reading all kinds of manuals, but so far have found absolutely nothing. Do let us know if you decoded the numbers, and I’ll do the same.

1 Like

I think it’s a bitmask, so you convert it to binary and then each position represents a different flag. I started decoding it over here: Untitled spreadsheet - Google Sheets

However, for my purposes I found a dedicated register for the hot water reversing valve so I’ve decided to use that instead.

2 Likes

Hello @scraplab may I kindly ask you for your registers (and modbus) settings for SMOS40? I has it running in ma HA installation but is not working maybe more thean a year. So when I will start to investigate what happens I would like ot know if I have all set right. Thank you very much.

I just have it connected over Modbus TCP in the Nibe integration. All the entities appear automatically, and then I enable specific ones I’m interested in using.

I have an SMO S40 control unit as well since last September, so here’s what I’ve figured out so far:

template:
  - sensor:
      - name: "NIBE Status"
        unique_id: "nibe_status"
        state: >
          {% set state_map = {
            8:       "Off – No Demand",
            4124:    "Heating",
            8220:    "Domestic Hot Water",
            4194312: "Off – No Demand",
            4198428: "Heating",
            4202504: "Compressor Preheating",
            4202524: "Domestic Hot Water",
            5242909: "Cooling",
            5308445: "Cooling"          } %}
          {{ state_map.get(states('sensor.current_status_31121') | int, "Unknown") }}
        icon: mdi:heat-pump

Your bitmask idea is pretty clever and seems spot on. I extended it with my own codes and it matches up perfectly. Thanks!

Aaaaand here’s a template for everybody who wants to use it:

template:
  - sensor:
      - name: "NIBE Status"
        unique_id: "nibe_status"
        state: >-
          {%- set bit_map = {
                12: "Heating",
                13: "Hot Water",
                14: "Hot Water Boost",
                20: "Cooling"
          } %}
          {%- set value = states('sensor.current_status_31121') | int %}
          {%- set modes = [] %}
          {%- for bit, label in bit_map.items() %}
              {%- if value | bitwise_and(2 ** bit) %}
                  {%- set modes = modes + [label] %}
              {%- endif %}
          {%- endfor %}
          {{  modes | join(' + ')
              if modes
              else 'Off' }}
        icon: mdi:heat-pump

1 Like

@cprerovsky’s template did not work for me (immutable arrays?), here is the version that is working for me, including some debugging-attributes:

template:
  - sensor:
      - name: "NIBE Status"
        unique_id: "nibe_status"
        state: >-
          {%- set raw_value = states('sensor.current_status_31121') %}
          {%- if raw_value in ['unknown', 'unavailable', None] %}
            Sensor nicht verfügbar
          {%- else %}
            {%- set value = raw_value | int(0) %}
            {%- set bit_12 = value | bitwise_and(4096) > 0 %}
            {%- set bit_13 = value | bitwise_and(8192) > 0 %}
            {%- set bit_14 = value | bitwise_and(16384) > 0 %}
            {%- set bit_20 = value | bitwise_and(1048576) > 0 %}
            {%- set modes = [] %}
            {%- if bit_12 %}{%- set modes = modes + ["Heizen"] %}{%- endif %}
            {%- if bit_13 %}{%- set modes = modes + ["Warmwasser"] %}{%- endif %}
            {%- if bit_14 %}{%- set modes = modes + ["Warmwasser Boost"] %}{%- endif %}
            {%- if bit_20 %}{%- set modes = modes + ["Kühlen"] %}{%- endif %}
            {{ modes | join(' + ') if modes else 'Aus' }}
          {%- endif %}
        icon: mdi:heat-pump
        attributes:
          raw_value: "{{ states('sensor.current_status_31121') }}"
          numeric_value: "{{ states('sensor.current_status_31121') | int(0) }}"
          binary_representation: "{{ '{:b}'.format(states('sensor.current_status_31121') | int(0)) }}"
          bit_12_heizen: "{{ states('sensor.current_status_31121') | int(0) | bitwise_and(2 ** 12) > 0 }}"
          bit_13_warmwasser: "{{ states('sensor.current_status_31121') | int(0) | bitwise_and(2 ** 13) > 0 }}"
          bit_14_warmwasser_boost: "{{ states('sensor.current_status_31121') | int(0) | bitwise_and(2 ** 14) > 0 }}"
          bit_20_kühlen: "{{ states('sensor.current_status_31121') | int(0) | bitwise_and(2 ** 20) > 0 }}"
          last_updated: "{{ now() }}"