Integration of HUUM UKU Wifi

I was wondering also if anyone has been able to communicate directly with the device on the local network. In an ideal world I’d like to see some sort of event from it to indicate the door was opened for example, because currently the polling every 60 seconds isn’t much use. I guess I could poll every 1 second but then would the HUUM api like that, or does anyone know the fastest polling rate permitted?

2 Likes

Integration PR created :slight_smile: Let’s see if we can get a proper integration in there in a couple of versions.

3 Likes

@fireheadman I have an automation that triggers alexa and notify my phone to say that the target temperature has been reached. I added a 1 hour delay at the end to make sure it didn’t get triggered repeatedly.

alias: sauna target reached
description: ''
trigger:
  - platform: numeric_state
    entity_id: sensor.sauna_temperature
    above: input_number.sauna_target_temperature
condition: []
action:
  - service: notify.notify
    data:
      message: >
        sauna has reached {{ states('input_number.sauna_target_temperature')|int }} degrees
  - service: notify.alexa_media_foo_s_echo_flex
    data:
      message: >
        sauna has reached {{ states('input_number.sauna_target_temperature')|int }} degrees
      data:
        type: tts
        method: speak
  - delay:
      hours: 1
      minutes: 0
      seconds: 0
      milliseconds: 0
mode: single

I use this format for the shell command to shield private info, I also have a split configuration, so these are both in the /config/entities/shell_commands/huum_shell_command.yaml


sauna_light_toggle: !secret huum_rest_sauna_light_toggle
sauna_off: !secret huum_rest_sauna_off

also… @deejbee Thanks for the new automation… I have my sauna in downtime for a remodel.

As for direct communication, I would be interested in that also, but I believe the UKU requires a login from estonia (HUUM), although, there may be a way to trigger a /etc/hosts entry ?

Hi,

What is the status of this integration pull request ? is it still processing or its off the table now ?

Hi, i am also very interested in an HUUM Integration. As i am a total newbie to Home Assistant and Programming in total - an integration would be perfect. Is there an update on the process? Thanks!

As i understand from the gihub thread that there will be no official plugin. So, could you explain how to make a custom install? BR Christoph

Hi @frwickst,
Thanks a lot for the Huum component. Too bad it wasn’t approved.

I would be super interested.

Is there anything I can do to help? I have some intermediate Python dev skills.

Otherwise, do you think it’s possible to do a custom component?

Hi,
can anyone tell me why the component wasn’t approved and whether there will be other attempts to integrate huum?

Or how to use the snippets you have been providing?

1 Like

I’m also interested in this integration. Is there any way to sponsor its development?

Hi, what a great Topic…I also have a HUUM Sauna and want to implement it in HA. I copied the Code below in the config and got the data for the sensors, but the shell commands doesn’t work.
my Code:
shell_command:
sauna_light_toggle: ‘curl “https://mail:[email protected]/action/home/light”’
sauna_off: ‘curl -X POST “https://mail:[email protected]/action/home/stop”’

if I try it via Webbrowser then it works, but not from HA.
What im doing wrong?

small addendum:
Switching on the sauna is possible with a set temperature via HA, but switching off is not possible…just like switching on or off the light.
Something with the shell_command does not fit with me yet.

ok, I found a solution for my problem.
i toggle the light and switch the sauna off with the rest functionality.

code:
rest_command:
sauna_licht_an_aus:
url: https://user:[email protected]/action/home/light

sauna_aus:
url: https://api.huum.eu/action/home/stop
username: user
password: password
verify_ssl: true
content_type: application/json
method: post

I use the configuration.yaml and script.yaml from upper and change the code

and now it works

Seems that this was not merged upstream. Maybe you want to provide it as HACS Addon?

+1 for HACS solution, please @frwickst !

I’m back to revisit my setup… I recently migrated my HA setup to a Apple Mac Mini (runs debian now) for a some power savings.

Count me in +1 more for a HACS integration.
Right now I am needing to sort out my light and sauna toggle as well as my input slider (which I wish was in F).

As a side note, I am also getting ready to add some small metal circulation fans, which will use the light relay.

Here is my current layout

the code:
I use a restriction card to prevent my kids from playing with it… Mimics the lockout function on the UKU control

type: vertical-stack
cards:
  - type: custom:restriction-card
    action: double_tap
    duration: 10
    restrictions:
      pin:
        code: XXXX
        text: >-
          Before confirming the PIN, Please ensure the Sauna is Safe and Ready!
          Dont burn my house down!!.. Oh, and enjoy your session. [XXXX]
        retry_delay: 10
    restriction-regular-lock-color: red
    restriction-success-lock-color: green
    card:
      type: entities
      title: Sauna [Hint - Double Tab]
      icon: mdi:radiator
      show_header_toggle: false
      entities:
        - entity: binary_sensor.sauna_door
          name: Door
        - entity: sensor.sauna_status_code
          name: Status
        - type: attribute
          entity: sensor.sauna_status
          attribute: light
          name: Light
        - entity: light.sauna
          name: Salt Lamp
        - entity: switch.sauna
          icon: mdi:radiator
        - entity: input_number.temperaturesauna
          name: Sauna Set Temperature (°C)
          icon: mdi:gauge
  - type: horizontal-stack
    cards:
      - type: conditional
        conditions:
          - entity: sensor.sauna_target_temperature
            state_not: unknown
        card:
          type: gauge
          entity: sensor.sauna_target_temperature
          max: 190
          min: 45
          name: Target Temp
          segments:
            - from: 190
              color: '#ff0d00'
            - from: 185
              color: '#ff1a00'
            - from: 180
              color: '#ff2600'
            - from: 175
              color: '#ff3300'
            - from: 170
              color: '#ff4000'
            - from: 165
              color: '#ff4c00'
            - from: 160
              color: '#ff5900'
            - from: 155
              color: '#ff6600'
            - from: 150
              color: '#ff7300'
            - from: 145
              color: '#ff8000'
            - from: 140
              color: '#ff8c00'
            - from: 135
              color: '#ff9900'
            - from: 130
              color: '#ffa600'
            - from: 125
              color: '#ffb200'
            - from: 120
              color: '#ffbf00'
            - from: 115
              color: '#ffcc00'
            - from: 110
              color: '#ffd900'
            - from: 105
              color: '#ffe600'
            - from: 100
              color: '#fff200'
            - from: 97
              color: '#ffff00'
            - from: 95
              color: '#ffff00'
            - from: 90
              color: '#f2ff00'
            - from: 87
              color: '#e6ff00'
            - from: 85
              color: '#d9ff00'
            - from: 80
              color: '#ccff00'
            - from: 77
              color: '#bfff00'
            - from: 75
              color: '#b2ff00'
            - from: 70
              color: '#a6ff00'
            - from: 67
              color: '#99ff00'
            - from: 65
              color: '#8cff00'
            - from: 60
              color: '#80ff00'
            - from: 57
              color: '#73ff00'
            - from: 55
              color: '#66ff00'
            - from: 50
              color: '#59ff00'
            - from: 47
              color: '#4dff00'
            - from: 45
              color: '#40ff00'
            - from: 40
              color: '#33ff00'
            - from: 37
              color: '#26ff00'
            - from: 35
              color: '#19ff00'
            - from: 30
              color: '#0dff00'
          needle: true
      - type: gauge
        entity: sensor.sauna_temperature
        max: 190
        min: 45
        name: Current Temp
        segments:
          - from: 190
            color: '#ff0d00'
          - from: 185
            color: '#ff1a00'
          - from: 180
            color: '#ff2600'
          - from: 175
            color: '#ff3300'
          - from: 170
            color: '#ff4000'
          - from: 165
            color: '#ff4c00'
          - from: 160
            color: '#ff5900'
          - from: 155
            color: '#ff6600'
          - from: 150
            color: '#ff7300'
          - from: 145
            color: '#ff8000'
          - from: 140
            color: '#ff8c00'
          - from: 135
            color: '#ff9900'
          - from: 130
            color: '#ffa600'
          - from: 125
            color: '#ffb200'
          - from: 120
            color: '#ffbf00'
          - from: 115
            color: '#ffcc00'
          - from: 110
            color: '#ffd900'
          - from: 105
            color: '#ffe600'
          - from: 100
            color: '#fff200'
          - from: 97
            color: '#ffff00'
          - from: 95
            color: '#ffff00'
          - from: 90
            color: '#f2ff00'
          - from: 87
            color: '#e6ff00'
          - from: 85
            color: '#d9ff00'
          - from: 80
            color: '#ccff00'
          - from: 77
            color: '#bfff00'
          - from: 75
            color: '#b2ff00'
          - from: 70
            color: '#a6ff00'
          - from: 67
            color: '#99ff00'
          - from: 65
            color: '#8cff00'
          - from: 60
            color: '#80ff00'
          - from: 57
            color: '#73ff00'
          - from: 55
            color: '#66ff00'
          - from: 50
            color: '#59ff00'
          - from: 47
            color: '#4dff00'
          - from: 45
            color: '#40ff00'
          - from: 40
            color: '#33ff00'
          - from: 37
            color: '#26ff00'
          - from: 35
            color: '#19ff00'
          - from: 30
            color: '#0dff00'
        needle: true

Just installed my HUUM sauna so in for helping to test, etc.

I did notice that the response between the HUUM app and the sauna itself is not terribly fast. Has anyone had any success trying local integration? I port scanned the controller but found no open ports (not terribly surprising). Also out of curiosity did anyone else notice that the MAC address on the sticker of the main control box is exactly backwards of the real MAC address?

I’ve made some good progress with the original code posted here (which I’ve also converted to English just for my own purposes because it makes it easier for me while editing). I will gladly share that as well. Before I do just trying to do some troubleshooting and clean a few things up. Hoping someone can help with the following questions:

  1. I see a lot of entities that aren’t able to be assigned an area because the “entity does not have a unique id” but from what I can tell it does in fact have a unique id? See images below


    image

  2. I noticed that the light off command in the original yaml above seems to use the word “duplicate” (in German of course) but I don’t really see a conflicting non-duplicative off command. Am I missing something or am uninformed (or is my German just bad) - the original is below

sauna_lampen_uit_dupliceer:
  alias: 'sauna lampen uit '
  sequence:
  - service: script.sauna_licht_toggle
  - service: input_boolean.turn_off
....
  1. What is refreshing the door open/closed status? I didn’t see it in the automations.yaml code. The reason I ask this is that it does seem to be still updating, which makes me wonder why we need automations to refresh the status of the light and heating.

So I sorted out the answer and solution to number 1 above - adding unique_ids fixes it. I created IDs for each item that would be very unlikely for anyone to be non-unique. Adding my updated code here which allows you to set areas for all the entities.

I just also want to say I’m standing on the shoulders of giants here, I claim no credit for any of this, @Razor109 and @fireheadman deserve the credit for anything here.

Configuration.yaml (make sure you update and to replace with your info, note the %40 in front of the domain vs. using @ directly in the shell commands)

################ HUUM #############
shell_command:
  sauna_light_toggle: 'curl "https://<EMAIL>%40gmail.com:<PASSWORD>@api.huum.eu/action/home/light"'
  sauna_off: curl -X POST https://<EMAIL>%40gmail.com:<PASSWORD>@api.huum.eu/action/home/stop

rest_command:
  sauna_on:
    url: https://api.huum.eu/action/home/start
    username: <EMAIL>
    password: <PASSWORD>
    payload: '{"targetTemperature":{{ states("input_number.temperaturesauna")|int }} }'
    verify_ssl: true
    content_type: application/json
    method: post

sensor:

  - platform: rest
    resource: https://api.huum.eu/action/home/status
    username: <EMAIL>
    password: <PASSWORD>
    authentication: basic
    unique_id: sauna_rest_status_2272022
    scan_interval: 60
    name: sauna_status
    value_template: "OK"
    json_attributes:
      - door
      - temperature
      - targetTemperature
      - statusCode
      - light


  - platform: template
    sensors:
      sauna_temperature:
        unique_id: sauna_temperature_2272023
        value_template: "{{ state_attr('sensor.sauna_status', 'temperature') }}"
        device_class: temperature
        unit_of_measurement: °C
    
      sauna_target_temperature:
        unique_id: sauna_target_temperature_2272023
        value_template: "{{ state_attr('sensor.sauna_status', 'targetTemperature') }}"
        device_class: temperature
        unit_of_measurement: °C
    
      sauna_door:
        unique_id: sauna_door_2272023
        value_template: >-
          {% set state = state_attr('sensor.sauna_status', 'door') %}
          {% if state == true %}Closed
          {% elif state == false %}Open
          {% else %}Unknown{% endif %}
    
      sauna_light:
        unique_id: sauna_light_2272023
        value_template: >-
          {% set state = state_attr('sensor.sauna_status', 'light') %}
          {% if state == 0 %}off
          {% elif state == 1 %}on
          {% else %}Unknown{% endif %}

      sauna_statuscode:
        unique_id: sauna_statuscode_2272023
        value_template: >-
          {% set state = state_attr('sensor.sauna_status', 'statusCode') %}
          {% if state == 230 %}Offline
          {% elif state == 231 %}on
          {% elif state == 232 %}off
          {% elif state == 233 %}Blocked
          {% elif state == 240 %}Emergency       
          {% else %}Unknown{% endif %}

light:
  - platform: template
    lights:
      sauna:
        unique_id: sauna_light_2272023
        friendly_name: "Sauna Light"
        value_template: "{{ states('input_boolean.sauna_lightstate') }}"
        turn_on: 
          service: script.sauna_light_on
        turn_off:
          service: script.sauna_light_off_dupe

switch:
  - platform: template
    switches:
      sauna:
        unique_id: sauna_heater_2272023
        friendly_name: "Sauna Heater"
        value_template: "{{ states('input_boolean.sauna_state') }}"
        turn_on:
          service: script.sauna_on
        turn_off:
          service: script.sauna_off

input_number:
  temperaturesauna:
    name: Sauna Set Temperature
    initial: 75
    min: 41
    max: 108
    step: 1

input_boolean:
  sauna_state:
    name: Status Sauna
  sauna_lightstate:
    name: Status Sauna Light

scripts.yaml

sauna_light_toggle:
  alias: '[SAUNA] Light toggle'
  sequence:
  - service: shell_command.sauna_light_toggle
  mode: single
sauna:
  alias: '[SAUNA] Sauna Off'
  sequence:
  - service: shell_command.sauna_off
  mode: single
sauna_sauna_on_75:
  alias: '[SAUNA] Sauna on 75 degrees C'
  sequence:
  - service: rest_command.sauna_on
  mode: single
sauna_on:
  alias: sauna on
  sequence:
  - service: script.sauna_sauna_on_75
  - service: input_boolean.turn_on
    target:
      entity_id: input_boolean.sauna_state
  - delay:
      hours: 0
      minutes: 1
      seconds: 0
      milliseconds: 0
  - choose:
    - conditions:
      - condition: state
        entity_id: sensor.sauna_statuscode
        state: 'on'
      sequence:
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.sauna_state
    - conditions:
      - condition: state
        entity_id: sensor.sauna_statuscode
        state: 'off'
      sequence:
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.sauna_state
    default: []
  mode: single
sauna_off:
  alias: sauna off
  sequence:
  - service: script.sauna
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.sauna_state
  - delay:
      hours: 0
      minutes: 1
      seconds: 0
      milliseconds: 0
  - choose:
    - conditions:
      - condition: state
        entity_id: sensor.sauna_statuscode
        state: 'on'
      sequence:
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.sauna_state
    - conditions:
      - condition: state
        entity_id: sensor.sauna_statuscode
        state: 'off'
      sequence:
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.sauna_state
    default: []
  mode: single
sauna_light_off_dupe:
  alias: 'sauna light off '
  sequence:
  - service: script.sauna_light_toggle
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.sauna_lightstate
  - delay:
      hours: 0
      minutes: 1
      seconds: 0
      milliseconds: 0
  - choose:
    - conditions:
      - condition: state
        entity_id: sensor.sauna_light
        state: 'on'
      sequence:
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.sauna_lightstate
    - conditions:
      - condition: state
        entity_id: sensor.sauna_light
        state: 'off'
      sequence:
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.sauna_lightstate
    default: []
  mode: single
sauna_light_on:
  alias: sauna light on
  sequence:
  - service: script.sauna_light_toggle
  - service: input_boolean.turn_on
    target:
      entity_id: input_boolean.sauna_lightstate
  - delay:
      hours: 0
      minutes: 1
      seconds: 0
      milliseconds: 0
  - choose:
    - conditions:
      - condition: state
        entity_id: sensor.sauna_light
        state: 'on'
      sequence:
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.sauna_lightstate
    - conditions:
      - condition: state
        entity_id: sensor.sauna_light
        state: 'off'
      sequence:
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.sauna_lightstate
    default: []
  mode: single

Automations.yaml (I’m not 100% convinced this is needed)

- id: '1640475808847'
  alias: Sauna Status Update
  description: ''
  trigger:
  - platform: state
    entity_id: sensor.sauna_statuscode
  - platform: time_pattern
    seconds: '30'
  condition: []
  action:
  - choose:
    - conditions:
      - condition: state
        entity_id: sensor.sauna_statuscode
        state: 'on'
      sequence:
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.sauna_state
        data: {}
    - conditions:
      - condition: state
        entity_id: sensor.sauna_statuscode
        state: 'off'
      sequence:
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.sauna_state
        data: {}
    default: []
  mode: single
- id: '1640476303582'
  alias: Sauna Light Update
  description: ''
  trigger:
  - platform: state
    entity_id: sensor.sauna_light
  - platform: time_pattern
    seconds: '30'
  condition: []
  action:
  - choose:
    - conditions:
      - condition: state
        entity_id: sensor.sauna_light
        state: 'on'
      sequence:
      - service: input_boolean.turn_on
        target:
          entity_id: input_boolean.sauna_lightstate
        data: {}
    - conditions:
      - condition: state
        entity_id: sensor.sauna_light
        state: 'off'
      sequence:
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.sauna_lightstate
        data: {}
    default: []
  mode: single

Feedback welcome, and hope this is helpful for someone

Thanks for your input.

Lately I adjusted my template for the sauna light and switch, to be more responsive. I’v found out how to put the contents of the scripts and automations directly in the template. In the old situation it could take a while for the status to change. Now it triggers an REST update directly after you flip te switch so it will update the status accordingly.

It also makes the automatisations and scripts unnecessary.

The status of the door is being refreshed when the REST sensor gets updated.

light:
  - platform: template
    lights:
      saunalight:
        value_template: >-
          {% set state = state_attr('sensor.sauna_status', 'light') %}
          {% if state == 0 %}Off
          {% elif state == 1 %}On
          {% else %}Onbekend{% endif %}
        turn_on:
          - service: shell_command.sauna_light_toggle
          - delay: 00:00:01
          - service: homeassistant.update_entity
            target:
               entity_id: sensor.sauna_status
        turn_off:
          - service: shell_command.sauna_light_toggle
          - delay: 00:00:01
          - service: homeassistant.update_entity
            target:
               entity_id: sensor.sauna_status
        friendly_name: Sauna Lampen

switch:
  - platform: template
    switches:
      sauna:
        value_template: >-
          {% set state = state_attr('sensor.sauna_status', 'statusCode') %}
          {% if state == 230 %}Offline
          {% elif state == 231 %}On
          {% elif state == 232 %}Off
          {% elif state == 233 %}Blocked
          {% elif state == 240 %}Emergency       
          {% else %}Unknown{% endif %}
        turn_on:
          - service: rest_command.sauna_on
          - delay: 00:00:01
          - service: homeassistant.update_entity
            target:
               entity_id: sensor.sauna_status
        turn_off:
          - service: shell_command.sauna_off
          - delay: 00:00:01
          - service: homeassistant.update_entity
            target:
               entity_id: sensor.sauna_status
        friendly_name: Sauna

This is my lovelace config:

      type: vertical-stack
      cards:

        - type: entities
          show_header_toggle: false
          entities:
            - entity: sensor.sauna_door
              name: Deur
            - entity: sensor.sauna_statuscode
              name: Status
            - entity: sensor.sauna_temperature
              name: Huidige temperatuur
            - type: conditional
              conditions:
                - entity: sensor.sauna_target_temperature
                  state_not: unknown
              row:
                entity: sensor.sauna_target_temperature
                name: Ingestelde temperatuur
            - entity: light.saunalight
            - entity: switch.sauna
              icon: mdi:radiator
            - entity: input_number.temperatuursauna
            - entity: media_player.sauna
              type: custom:mini-media-player
              toggle_power: true
              shortcuts:
                buttons:
                  - name: ClassicFM
                    type: service
                    id: squeezebox.call_method
                    data:
                      entity_id: media_player.sauna
                      command: playlist
                      parameters:
                        - play
                        - classicfm
                  - name: Skyradio
                    type: service
                    id: squeezebox.call_method
                    data:
                      entity_id: media_player.sauna
                      command: playlist
                      parameters:
                        - play
                        - >-
                          http://opml.radiotime.com/Tune.ashx?id=s224032&formats=aac,ogg,mp3,wmpro,wma,wmvoice&partnerId=16&serial=7a068b625989fd18b463b262fd7dd78f
                  - name: 100.5 Das Hitradio
                    type: service
                    id: squeezebox.call_method
                    data:
                      entity_id: media_player.sauna
                      command: playlist
                      parameters:
                        - play
                        - >-
                          http://opml.radiotime.com/Tune.ashx?id=s2730&formats=aac,ogg,mp3,wmpro,wma,wmvoice&partnerId=16&serial=7a068b625989fd18b463b262fd7dd78f
                  - name: X-Mas Hits
                    type: service
                    id: squeezebox.call_method
                    data:
                      entity_id: media_player.sauna
                      command: playlist
                      parameters:
                        - play
                        - >-
                          https://stream06.dotpoint.nl:8006/stream?DIST=TuneIn&TGT=TuneIn&maxServers=2&partnertok=eyJhbGciOiJIUzI1NiIsImtpZCI6InR1bmVpbiIsInR5cCI6IkpXVCJ9.eyJ0cnVzdGVkX3BhcnRuZXIiOnRydWUsImlhdCI6MTY0MDE5ODY2OSwiaXNzIjoidGlzcnYifQ.c0w2YmufQO-tadk_CrRWQgeSNnFGD0ywrPwwlaiM49s
          title: Sauna

Offcourse add your unique_id / friendly_name to meet your needs, since i dont use them in my own config.

it makes the following unnecessary

light:
- platform: template
lights:
sauna:
unique_id: sauna_light_2272023
friendly_name: “Sauna Light”
value_template: “{{ states(‘input_boolean.sauna_lightstate’) }}”
turn_on:
service: script.sauna_light_on
turn_off:
service: script.sauna_light_off_dupe

switch:
- platform: template
switches:
sauna:
unique_id: sauna_heater_2272023
friendly_name: “Sauna Heater”
value_template: “{{ states(‘input_boolean.sauna_state’) }}”
turn_on:
service: script.sauna_on
turn_off:
service: script.sauna_off

input_boolean:
sauna_state:
name: Status Sauna
sauna_lightstate:
name: Status Sauna Light

and the automations and scripts are also not necessary anymore.