New electric water heater / boiler (integrated with HA)

Hi all,
I want to buy an electric 150L water heater/boiler, and obviously I want to have it integrated with HA. There doesn’t seem to be much info around what heater I should buy, to ensure it can be integrated with HA. I found some people using additional temperature sensor and power switch, which can indeed be a solution. However, I’m hoping there is a heater already integrated, as there is an integration called “water heater” (Water Heater - Home Assistant)…

Anyone any clue?

[EDIT]
I’m located in the Netherlands by the way… To narrow down the solutions :wink:

1 Like

I agree that integration page is super confusing, with no sample config. I assumed there would be a generic option like there is for thermostat. With a little digging I think they are referring to these (limited) options - Integrations - Home Assistant

Just so I understand, you are looking for a heater/boiler for bath consumption or home heating?

Also each country has different manufacturers and they all want you to use their “cloud” based integration, I’d suggest looking at what is popular in your area and then searching for and integration with them. For example, the biggest consumption water heater around these parts is Rheem and there is a HA integration.

I am located in the Netherlands and yeah, found Rheem as well. Rheem is a bit overdone (and priced) in my situation. When I search for electric water heaters (boiler) I can buy one for not more than €400-500 and integrate with HA myself… Of course, I am willing to pay more if it has good HA integration :upside_down_face:

yeah, try finding an Integration that has electric boilers… :wink:

Did you find a satisfactory boiler?
So far I found Tesy coming close to what we might need, but they have no open API.

No, not yet… I found Tesy as well… ModEco Cloud. No clue if that will work.

I’m looking for the same, found anything?

No, unfort… nothing to can be seamlessly integrated. Kinda put the project on hold until I found something workable.

1 Like

Hello!

Hopefully I bring good news!
you are looking for this:

I have a tesy boiler on the wall and figured all this stuff out while digging around for my daikin heatpump
(GitHub - william-sy/Daikin-BRP069A62: Creating a daikin interface other than the apps)

Now its not a integration, but it should give you a great starting point to figure some stuff out!

3 Likes

Made a simple POC of a tesy boiler:

sensor:
  - platform: rest
    resource: http://192.168.2.254/status?_=1634912123253
    name: TesyBoilerWaterTemp
    method: GET
    value_template: "{{ value_json.gradus }}"
    unit_of_measurement: "°C"

Nice!!! That is really usable

All the basic info you could want is my guess:

sensor:
  - platform: time_date
    display_options:
      - 'time'
      - 'date'
      - 'date_time'
      - 'date_time_utc'
      - 'date_time_iso'
      - 'time_date'
      - 'time_utc'
      - 'beat'
  - platform: worldclock
    name: Toronto
    time_zone: America/Toronto
  - platform: worldclock
    name: Netherlands
    time_zone: Europe/Amsterdam
  - platform: worldclock
    name: Kuala Lumpur
    time_zone: Asia/Kuala_Lumpur
  - platform: rest
    resource: http://192.168.2.254/status?_=1634912123253
    name: tesyboiler
    method: GET
    value_template: "OK"
    json_attributes:
      - gradus
      - ref_gradus
      - heater_state
      - err_flag
      - boost
      - power_sw
      - volume
      - watts
  - platform: rest
    resource: http://192.168.2.254/calcRes?_=1634912123253
    name: tesyboilerpower
    method: GET
    value_template: "OK"
    json_attributes:
      - kwh
      - ltc
      - resetDate
  - platform: template
    sensors:
      tesyboilerwatertemp:
        friendly_name: "TesyBoilerWaterTemp"
        value_template: "{{ state_attr('sensor.tesyboiler', 'gradus') }}"
        unit_of_measurement: "°C"
        icon_template: mdi:temperature-celsius
      tesyboilerwatertargettemp:
        friendly_name: "TesyBoilerWaterTargetTemp"
        value_template: "{{ state_attr('sensor.tesyboiler', 'ref_gradus') }}"
        unit_of_measurement: "°C"
        icon_template: mdi:temperature-celsius
      tesyboilerstate:
        friendly_name: "TesyBoilerState"
        value_template: "{{ state_attr('sensor.tesyboiler', 'heater_state') }}"
        icon_template: mdi:help-circle
      tesyboilerwatts:
        friendly_name: "TesyBoilerWatts"
        value_template: "{{ state_attr('sensor.tesyboiler', 'watts') }}"
        unit_of_measurement: "W"
        icon_template: mdi:power-plug
      tesyboilerwatervolume:
        friendly_name: "TesyBoilerWaterVolume"
        value_template: "{{ state_attr('sensor.tesyboiler', 'volume') }}"
        unit_of_measurement: "L"
        icon_template: mdi:cup-water
      tesyboileronoff:
        friendly_name: "TesyBoilerOnOff"
        value_template: "{{ state_attr('sensor.tesyboiler', 'power_sw') }}"
        icon_template: mdi:help-circle
      tesyboilerboost:
        friendly_name: "TesyBoilerBoost"
        value_template: "{{ state_attr('sensor.tesyboiler', 'boost') }}"
        icon_template: mdi:arrow-up-circle
      tesyboilererror:
        friendly_name: "TesyBoilerError"
        value_template: "{{ state_attr('sensor.tesyboiler', 'err_flag') }}"
        icon_template: mdi:water-boiler-alert
      tesyboilerkwhalltime:
        friendly_name: "TesyBoilerKwhAllTime"
        value_template: "{{ state_attr('sensor.tesyboilerpower', 'ltc') }}"
        unit_of_measurement: "kWh"
        icon_template: mdi:lightning-bolt
      tesyboilerkwhafterreset:
        friendly_name: "TesyBoilerKwhAfterReset"
        value_template: "{{ state_attr('sensor.tesyboilerpower', 'kwh') }}"
        unit_of_measurement: "kWh"
        icon_template: mdi:lightning-bolt
      tesyboilerkwhresetdate:
        friendly_name: "TesyBoilerKwhResetDate"
        value_template: "{{ state_attr('sensor.tesyboilerpower', 'resetDate') }}"
        icon_template: mdi:calendar-blank-outline

If you want more you can look at my repo for the URL / JSON responses.
Also if you happen to update it do share back :slight_smile:

Pushing on/off, a new temp and boost should be possible but at some point its better to have a full integration made that also can reset kWh and change schedules

Edit reason: Updated the sensor with more info and icons
Also a template for a dashboard:

type: custom:mini-graph-card
hours_to_show: 1
points_per_hour: 15
update_interval: 30
group_by: value
name: TesyBoiler
line_width: 2
entities:
  - entity: sensor.tesyboilerwatertemp
    name: Water Temp
  - entity: sensor.tesyboilerwatertargettemp
    name: Target Temp
  - entity: sensor.tesyboilerboost
    show_state: true
    show_graph: false
    name: Boost
  - entity: sensor.tesyboilerstate
    show_state: true
    show_graph: false
    name: Boost
show:
  graph: line
  name: true
  legend: true
  icon: false
  labels: true
  labels_secondary: true
state_map:
  - value: READY
    label: Standby
  - value: HEATING
    label: HeatingUp
  - value: '0'
    label: Not boosting
  - value: '0'
    label: Boosting

Screenshot 2022-09-22 at 16.04.59

1 Like

This looks really promising! The sensors are awesome!
You captured the URL’s, but was this not HTTPS (as Tesy describes the communication highly secure between the cloud and boiler)…

What type of Tesy are you testing on? The Modeco cloud, or Bellisimo cloud?

For my scenario, switching the unit on/off, or maybe switch low/mid/high consumption/temperature, is a requirement (solar energy availability) from within HA.

Https is only needed if you are connecting to the cloud. You technically dont need to do this the local webpage does all that you want its just less pretty.

Its a Model : modeco 2 with a tesy on it, See the screen shots below :slight_smile:

Switching the boiler on / off is not something I am immediately after at the moment as I am moving next year, So I am just experimenting with everything in this house to de-cloud myself. But happy to help script away if someone sets up a propper integration.

You can also re-flash the boiler with ESP home, as there is just a ESP in there anyway.

Screenshot 2022-09-24 at 11.22.38

Also If the URLS dont work, just open up a inspector page:

1 Like

Hi I tried the following for on / off / boost:

switch:
  - platform: command_line
    switches:
      tesyboostonoff:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/boostSW?mode=1&_=1634911445424"
        command_off: "/usr/bin/curl -X GET http://192.168.2.254/boostSW?mode=0&_=1634911446795"
        #command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: >
          "{{ sensor.tesyboilerboost }}"
        friendly_name: Boost Tesy!
        icon_template: >
          {% if sensor.tesyboilerboost %} mdi:rocket-launch
          {% else %} mdi:rocket-launch-outline
          {% endif %}
      tesyonoff:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/power?val=on&_=1664015398260"
        command_off: "/usr/bin/curl -X GET http://192.168.2.254/power?val=off&_=1664015398260"
        #command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: >
         "{{ sensor.tesyboilerstate }}"
        friendly_name: Tesy power switch!
        icon_template: >
          {% if sensor.tesyboilerstate %} mdi:toggle-switch
          {% else %} mdi:toggle-switch-off
          {% endif %}

But I am not happy with the state fetching seems to not really work

As you can see it assumes the state:

Ideally it just gets the state form the curl or the other sensor but no luck.

It took a bit of puzzling, but here we are its working :slight_smile:

switch:
  - platform: command_line
    switches:
      tesyboostonoff:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/boostSW?mode=1&_=1634911445424"
        command_off: "/usr/bin/curl -X GET http://192.168.2.254/boostSW?mode=0&_=1634911446795"
        command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: "{{ value_json['boost'] == '1' }}"
        friendly_name: Boost Tesy!
        icon_template: >
          {% if value_json['boost'] == '0' %} mdi:rocket
          {% elif value_json['boost'] == '1' %} mdi:rocket-launch
          {% else %} mdi:help-circle
          {% endif %}
      tesyonoff:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/power?val=on&_=1664015398260"
        command_off: "/usr/bin/curl -X GET http://192.168.2.254/power?val=off&_=1664015398260"
        command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: "{{ value_json['power_sw'] == 'on' }}"
        friendly_name: Tesy power switch!
        icon_template: >
          {% if value_json['power_sw'] == "on" %} mdi:toggle-switch
          {% else %} mdi:toggle-switch-off
          {% endif %}
3 Likes

What beautiful work!
Can you show me an example how to set the temperature with a command line

You could if you have not programmed a schedule (we have that so its hard without a integration)

A schedule looks like this:

&Mon00=32&Mon01=32&Mon02=32&Mon03=32&Mon04=32&Mon05=32&Mon06=50&Mon07=50&Mon08=50&Mon09=50&Mon10=50&Mon11=50&Mon12=50&Mon13=50&Mon14=50&Mon15=50&Mon16=60&Mon17=60&Mon18=40&Mon19=40&Mon20=40&Mon21=40&Mon22=30&Mon23=30

Repeat for all 7 days.
You would need to send it as plain text like this:

# P1 is week program 1
curl -X POST -H "Content-Type: text/plain" --data "this is raw data" http://192.168.2.254/setP1

But if you are in manual mode (or force it to be in manual mode):

# Set manual mode
http://192.168.2.254/modeSW?mode=1&_=1664177920325

Then you can send it with:

# Send temperature 25
http://192.168.2.254/setTemp?val=25&_=1664177655995

You can get the current value from the status one: http://192.168.2.254/status?_=1664177656195 and look at ref_gradus ( this is tesyboilerwatertargettemp in the above ymls)

Here are all the modes:

# manual mode
http://192.168.2.254/modeSW?mode=1&_=1634912123253
# set program 1 (P1)
http://192.168.2.254/modeSW?mode=2&_=1634912027732
# set 2 Schedule 2 (P2)
http://192.168.2.254/modeSW?mode=3&_=1634912041121
# set 3 Schedule 3 (P3)
http://192.168.2.254/modeSW?mode=4&_=1634912071235
# eco 1 ( this is a Mode you cant change settings in - see the manual )
http://192.168.2.254/modeSW?mode=5&_=1634912151669
# eco 2 ( this is a Mode you cant change settings in - see the manual )
http://192.168.2.254/modeSW?mode=6&_=1634912164061
# eco 3 ( this is a Mode you cant change settings in - see the manual )
http://192.168.2.254/modeSW?mode=7&_=1634912180802

So first thing would be to get the current mode you are in:

  - platform: rest
    resource: http://192.168.2.254/status?_=1634912123253
    name: tesyboiler
    method: GET
    value_template: "OK"
    json_attributes:
      - mode
  - platform: template
    sensors:
      tesyboilermode:
        friendly_name: "TesyBoilerMode"
        value_template: "{{ state_attr('sensor.tesyboiler', 'mode') }}"

Set manual? :

switch:
  - platform: command_line
    switches:
      tesysetmanualmode:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/modeSW?mode=1&_=1664177920325"
        command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: "{{ value_json['mode'] == '1' }}"
        friendly_name: Set Tesy manual mode
      tesysetmanualtemperature:
        # Some how get the temperature in here
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/setTemp?val=25&_=1664177655995"
        command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        # Some how this needs to evaluate to true
        value_template: "{{ value_json['ref_gradus'] ==  }}"
        friendly_name: Set Tesy manual temp

Note this is not tested, as I use the schedules

Here is a Full config yml:


rest_command:
  tsmt:
    url: "http://192.168.2.254/setTemp?val={{ temperature }}&_=1664177655995"
    method: GET

automation:
  - alias: "Tesy Temperature input automatically changed"
    trigger:
      platform: state
      entity_id: sensor.tesyboilertargettemp
    action:
      service: input_number.set_value
      target:
        entity_id: input_number.tesyboilermanualtempinput
      data:
        value: "{{ trigger.to_state.state }}"
  - alias: "Tesy Temperature input manually changed"
    trigger:
      platform: state
      entity_id: input_number.tesyboilermanualtempinput
    action:
      service: rest_command.tsmt
      data:
        temperature: "{{ trigger.to_state.state }}"

input_number:
  tesyboilermanualtempinput:
    name: Set Tesy Manual Temp
    min: 14
    max: 75
    step: 1
    unit_of_measurement: "°C"
    icon: mdi:temperature-celsius

switch:
  - platform: command_line
    switches:
      tesyboostonoff:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/boostSW?mode=1&_=1634911445424"
        command_off: "/usr/bin/curl -X GET http://192.168.2.254/boostSW?mode=0&_=1634911446795"
        command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: "{{ value_json['boost'] == '1' }}"
        friendly_name: Boost Tesy!
        icon_template: >
          {% if value_json['boost'] == '0' %} mdi:rocket
          {% elif value_json['boost'] == '1' %} mdi:rocket-launch
          {% else %} mdi:help-circle
          {% endif %}
      tesyonoff:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/power?val=on&_=1664015398260"
        command_off: "/usr/bin/curl -X GET http://192.168.2.254/power?val=off&_=1664015398260"
        command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: "{{ value_json['power_sw'] == 'on' }}"
        friendly_name: Tesy power switch!
        icon_template: >
          {% if value_json['power_sw'] == "on" %} mdi:toggle-switch
          {% else %} mdi:toggle-switch-off
          {% endif %}
      tesyresetpower:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/resetPow?_=1634912213060"
        friendly_name: Tesy power Reset!
      tesysetsetmanualmode:
        command_on: "/usr/bin/curl -X GET http://192.168.2.254/modeSW?mode=1&_=1664177920325"
        # Set a default schedule here if you dont want manual mode any longer
        command_off: "/usr/bin/curl -X GET http://192.168.2.254/modeSW?mode=2&_=1664177920325"
        command_state: "/usr/bin/curl -sX GET http://192.168.2.254/status?_=1634912123253"
        value_template: "{{ value_json['mode'] == '1' }}"
        friendly_name: Set Tesy manual mode
        icon_template: >
          {% if value_json['mode'] == '1' %} mdi:water-boiler
          {% elif  value_json['mode'] == '2' %} mdi:calendar
          {% elif  value_json['mode'] == '3' %} mdi:calendar
          {% elif  value_json['mode'] == '4' %} mdi:calendar
          {% elif  value_json['mode'] == '5' %} mdi:sprout
          {% elif  value_json['mode'] == '6' %} mdi:sprout
          {% elif  value_json['mode'] == '7' %} mdi:sprout
          {% else %} mdi:help-circle
          {% endif %}

sensor:
  - platform: rest
    resource: http://192.168.2.254/status?_=1634912123253
    name: tesyboiler
    method: GET
    value_template: "OK"
    json_attributes:
      - gradus
      - ref_gradus
      - heater_state
      - err_flag
      - boost
      - power_sw
      - volume
      - watts
      - mode
  - platform: rest
    resource: http://192.168.2.254/calcRes?_=1634912123253
    name: tesyboilerpower
    method: GET
    value_template: "OK"
    json_attributes:
      - kwh
      - ltc
      - resetDate
  - platform: template
    sensors:
      tesyboilerwatertemp:
        friendly_name: "TesyBoilerWaterTemp"
        value_template: "{{ state_attr('sensor.tesyboiler', 'gradus') }}"
        unit_of_measurement: "°C"
        icon_template: mdi:temperature-celsius
      tesyboilerwatertargettemp:
        friendly_name: "TesyBoilerWaterTargetTemp"
        value_template: "{{ state_attr('sensor.tesyboiler', 'ref_gradus') }}"
        unit_of_measurement: "°C"
        icon_template: mdi:temperature-celsius
      tesyboilertargettemp:
        friendly_name: "TesyBoilerTargetTemp"
        value_template: "{{ state_attr('sensor.tesyboiler', 'ref_gradus') }}"
      tesyboilerstate:
        friendly_name: "TesyBoilerState"
        value_template: "{{ state_attr('sensor.tesyboiler', 'heater_state') }}"
        icon_template: mdi:help-circle
      tesyboilermode:
        friendly_name: "TesyBoilerMode"
        value_template: "{{ state_attr('sensor.tesyboiler', 'mode') }}"
        icon_template: mdi:calendar
      tesyboilerwatts:
        friendly_name: "TesyBoilerWatts"
        value_template: "{{ state_attr('sensor.tesyboiler', 'watts') }}"
        unit_of_measurement: "W"
        icon_template: mdi:power-plug
      tesyboilerwatervolume:
        friendly_name: "TesyBoilerWaterVolume"
        value_template: "{{ state_attr('sensor.tesyboiler', 'volume') }}"
        unit_of_measurement: "L"
        icon_template: mdi:cup-water
      tesyboileronoff:
        friendly_name: "TesyBoilerOnOff"
        value_template: "{{ state_attr('sensor.tesyboiler', 'power_sw') }}"
        icon_template: mdi:help-circle
      tesyboilerboost:
        friendly_name: "TesyBoilerBoost"
        value_template: "{{ state_attr('sensor.tesyboiler', 'boost') }}"
        icon_template: mdi:arrow-up-circle
      tesyboilererror:
        friendly_name: "TesyBoilerError"
        value_template: "{{ state_attr('sensor.tesyboiler', 'err_flag') }}"
        icon_template: mdi:water-boiler-alert
      tesyboilerkwhalltime:
        friendly_name: "TesyBoilerKwhAllTime"
        value_template: "{{ state_attr('sensor.tesyboilerpower', 'ltc') }}"
        unit_of_measurement: "kWh"
        icon_template: mdi:lightning-bolt
      tesyboilerkwhafterreset:
        friendly_name: "TesyBoilerKwhAfterReset"
        value_template: "{{ state_attr('sensor.tesyboilerpower', 'kwh') }}"
        unit_of_measurement: "kWh"
        icon_template: mdi:lightning-bolt
      tesyboilerkwhresetdate:
        friendly_name: "TesyBoilerKwhResetDate"
        value_template: "{{ state_attr('sensor.tesyboilerpower', 'resetDate') }}"
        icon_template: mdi:calendar-blank-outline

Things that could be implemented more:

  • Rest comands:
    – Set a different mode (1 to 7)
    – Reset kWh usage (good for adding it to the energy dashboard?)
  • Full display with edit:
    – Change a actual schedule (better to have a proper implementation)

Hope it helps, From here everything should be easy enough for the next person. Ill stop posting more config :wink:

1 Like