Heaty - a flexible heating control, facilitating schedules and manual intervention

I made an indentation mistake… Can you please refresh app.py again?

Hi,

Just refreshed and seems issue with supports_opmodes:false is now ok

2018-02-15 00:07:27.051178 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1: temperature=21.0, operation_mode=<unset>, left retries=0
2018-02-15 00:07:27.055826 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_2: temperature=21.0, operation_mode=<unset>, left retries=0
2018-02-15 00:07:27.096841 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_3: temperature=21.0, operation_mode=<unset>, left retries=0

but still it does retry

2018-02-15 00:12:05.778035 INFO heaty: --> [salon] climate.fibaro_system_fgt001_heat_controller_heating: attribute operation_mode is Heat
2018-02-15 00:12:05.789411 INFO heaty: --> [salon] climate.fibaro_system_fgt001_heat_controller_heating: attribute temperature is 18.0
2018-02-15 00:12:05.798595 INFO heaty: --> [salon] Received target temperature 18.0 from thermostat.
2018-02-15 00:12:05.808466 INFO heaty: <-- [salon] Propagating the change to all thermostats in the room.
2018-02-15 00:12:05.819704 INFO heaty: <-- [salon] Temperature set to 18.0.  <manual>
2018-02-15 00:12:05.830318 INFO heaty: --- [salon] Not sending temperature to climate.fibaro_system_fgt001_heat_controller_heating redundantly.
2018-02-15 00:12:05.845759 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 00:12:05.866364 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 00:12:05.916594 INFO heaty: --- [salon] Evaluated temperature expression 20.0 to 20.0.
2018-02-15 00:12:05.927449 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 00:12:05.939986 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 00:12:06.015868 INFO heaty: <-- Publishing state to AppDaemon.
2018-02-15 00:12:06.020700 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1.
2018-02-15 00:12:06.021486 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_2.
2018-02-15 00:12:06.023553 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_3.
2018-02-15 00:12:06.032422 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1: temperature=19.0, operation_mode=<unset>, left retries=1
2018-02-15 00:12:06.035347 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_2: temperature=19.0, operation_mode=<unset>, left retries=1
2018-02-15 00:12:06.042416 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_3: temperature=19.0, operation_mode=<unset>, left retries=1
2018-02-15 00:12:06.220940 INFO heaty: --- [salon] Re-sending to climate.danfoss_z_thermostat_heating_1_2 in 5 seconds.
2018-02-15 00:12:06.224867 INFO heaty: --- [salon] Re-sending to climate.danfoss_z_thermostat_heating_1_3 in 5 seconds.
2018-02-15 00:12:06.225728 INFO heaty: --- [salon] Re-sending to climate.danfoss_z_thermostat_heating_1 in 5 seconds.
2018-02-15 00:12:11.028795 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_2.
2018-02-15 00:12:11.030049 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_3.
2018-02-15 00:12:11.031190 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1.
2018-02-15 00:12:11.058871 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_2: temperature=19.0, operation_mode=<unset>, left retries=0
2018-02-15 00:12:11.060612 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_3: temperature=19.0, operation_mode=<unset>, left retries=0
2018-02-15 00:12:11.062142 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1: temperature=19.0, operation_mode=<unset>, left retries=0

Cheers

EDIT:
I think retries are there due to Z-Wave Thermostats temp change in HA when it receives confirmation form valve that it in fact changed it. Depending on valve it takes from 10-15sec to even 10 mins on default Danfoss setting (wakeup interval 600)

Yes, Heaty can only stop re-sending when it gets the report back from the thermostat, which is of course a problem with old Z-Wave components without FLIRS support.

But there also seems to be a race condition at the change replication. Could you please set replicate_changes: false in your room’s configuration?

Thanks.

Hi,

with replicate_changes: false:

2018-02-15 18:07:18.015011 INFO heaty: <-- Publishing state to AppDaemon.
2018-02-15 18:07:23.015405 INFO heaty: --- [salon] Re-schedule timer fired.
2018-02-15 18:07:23.030549 INFO heaty: --- [salon] Evaluated temperature expression "24.0 if app.get_state('input_boolean.heating_day_lower') == 'on' else Ignore()" to Ignore().
2018-02-15 18:07:23.044220 INFO heaty: --- [salon] Skipping this rule.
2018-02-15 18:07:23.058200 INFO heaty: --- [salon] Evaluated temperature expression 24.0 to 24.0.
2018-02-15 18:07:23.069721 INFO heaty: <-- [salon] Temperature set to 24.0.  <scheduled>
2018-02-15 18:07:23.080972 INFO heaty: --- [salon] Not sending temperature to climate.fibaro_system_fgt001_heat_controller_heating redundantly.
2018-02-15 18:07:23.092251 INFO heaty: --- [salon] Not sending temperature to climate.danfoss_z_thermostat_heating_1 redundantly.
2018-02-15 18:07:23.103497 INFO heaty: --- [salon] Not sending temperature to climate.danfoss_z_thermostat_heating_1_2 redundantly.
2018-02-15 18:07:23.115912 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_3.
2018-02-15 18:07:23.127490 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:07:23.138388 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:07:24.019533 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_3.
2018-02-15 18:07:24.031757 INFO heaty: <-- Publishing state to AppDaemon.
2018-02-15 18:07:24.039386 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_3: temperature=25.0, operation_mode=<unset>, left retries=1
2018-02-15 18:07:24.145731 INFO heaty: --- [salon] Re-sending to climate.danfoss_z_thermostat_heating_1_3 in 60 seconds.
2018-02-15 18:07:33.588634 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute operation_mode is None
2018-02-15 18:07:33.613669 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute temperature is 24.5
2018-02-15 18:07:33.633739 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:07:33.939196 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute operation_mode is None
2018-02-15 18:07:33.953500 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute temperature is 24.5
2018-02-15 18:07:33.958328 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:07:34.009027 INFO heaty: <-- Publishing state to AppDaemon.

with flag set to true:

2018-02-15 18:09:46.015358 INFO heaty: <-- Publishing state to AppDaemon.
2018-02-15 18:09:51.014671 INFO heaty: --- [salon] Re-schedule timer fired.
2018-02-15 18:09:51.028246 INFO heaty: --- [salon] Evaluated temperature expression "22.0 if app.get_state('input_boolean.heating_day_lower') == 'on' else Ignore()" to 22.0.
2018-02-15 18:09:51.040615 INFO heaty: <-- [salon] Temperature set to 22.0.  <scheduled>
2018-02-15 18:09:51.054880 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1.
2018-02-15 18:09:51.068747 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_2.
2018-02-15 18:09:51.082609 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_3.
2018-02-15 18:09:51.094688 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:09:51.103860 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:09:52.041690 INFO heaty: --- [salon] Cancelled resend timer for climate.fibaro_system_fgt001_heat_controller_heating.
2018-02-15 18:09:52.048135 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1.
2018-02-15 18:09:52.051450 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_2.
2018-02-15 18:09:52.053454 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_3.
2018-02-15 18:09:52.056399 INFO heaty: <-- Publishing state to AppDaemon.
2018-02-15 18:09:52.083692 INFO heaty: <-- [salon] Setting climate.fibaro_system_fgt001_heat_controller_heating: temperature=22.0, operation_mode=<unset>, left retries=1
2018-02-15 18:09:52.085074 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_2: temperature=23.0, operation_mode=<unset>, left retries=1
2018-02-15 18:09:52.086237 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1_3: temperature=23.0, operation_mode=<unset>, left retries=1
2018-02-15 18:09:52.092178 INFO heaty: <-- [salon] Setting climate.danfoss_z_thermostat_heating_1: temperature=23.0, operation_mode=<unset>, left retries=1
2018-02-15 18:09:52.297328 INFO heaty: --- [salon] Re-sending to climate.fibaro_system_fgt001_heat_controller_heating in 60 seconds.
2018-02-15 18:09:52.410988 INFO heaty: --- [salon] Re-sending to climate.danfoss_z_thermostat_heating_1 in 60 seconds.
2018-02-15 18:09:52.415407 INFO heaty: --- [salon] Re-sending to climate.danfoss_z_thermostat_heating_1_2 in 60 seconds.
2018-02-15 18:09:52.418398 INFO heaty: --- [salon] Re-sending to climate.danfoss_z_thermostat_heating_1_3 in 60 seconds.
2018-02-15 18:10:11.887781 INFO heaty: --> [salon] climate.danfoss_z_thermostat_heating_1_3: attribute operation_mode is None
2018-02-15 18:10:11.896564 INFO heaty: --> [salon] climate.danfoss_z_thermostat_heating_1_3: attribute temperature is 23.0
2018-02-15 18:10:11.906394 INFO heaty: --- [salon] Cancelled resend timer for climate.danfoss_z_thermostat_heating_1_3.
2018-02-15 18:10:11.915091 INFO heaty: --> [salon] Received target temperature 23.0 from thermostat.
2018-02-15 18:10:11.927237 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:10:11.941294 INFO heaty: --- [salon] Evaluated temperature expression "22.0 if app.get_state('input_boolean.heating_day_lower') == 'on' else Ignore()" to 22.0.
2018-02-15 18:10:11.951668 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:10:11.960148 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:10:12.014940 INFO heaty: <-- Publishing state to AppDaemon.
2018-02-15 18:10:28.297161 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute operation_mode is None
2018-02-15 18:10:28.313543 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute temperature is 24.5
2018-02-15 18:10:28.343777 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:10:28.679018 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute operation_mode is None
2018-02-15 18:10:28.731398 INFO heaty: --> [biuro] climate.danfossbiuro_heating_1: attribute temperature is 24.5
2018-02-15 18:10:28.749633 INFO heaty: --- Called update_publish_state_timer.
2018-02-15 18:10:29.007526 INFO heaty: <-- Publishing state to AppDaemon.

Right, then please leave replicate_changes set to false for the time being.

I’m planning to restructure core parts of Heaty that became a bit twisted as they grew, afterwards I’m going to make the replication work reliably.

Hello,

This is great! I’ve successfully made a schedule for my living room z-wave thermostat which works fine.
I’m struggling a bit more with more advanced scheduling based on “input bolean” from home assistant. I understand that I have to make a separate file and use “temp_expression_modules” which I’ve done, but probably incorrectly. In the example:(https://github.com/efficiosoft/hass_apps/blob/master/hass_apps/heaty/doc/README.rst)

Save the code as my_mod.py somewhere Python can find it. The easiest way is to store it inside AppDaemon’s apps directory.

Add the module to your temp_expression_modules config as explained before.

Should I enter exactly as the below example:

temp_expression_modules:
  math:
  time:
    as: _time
  my_custom_module:

or something like this, adding the name of the file?:

temp_expression_modules:
  math:
  time:
    as: _time
  my_custom_module:
    as: "name of file in apps folder"

I’ve tried several combinations but i get this error every time:

2018-02-16 21:37:33.979550 WARNING ------------------------------------------------------------
2018-02-16 21:37:33.979996 INFO Loading Module: /home/homeassistant/.homeassistant/conf/apps/hass_apps/hass_apps/data/hass_apps_loader.py
2018-02-16 21:37:33.980654 INFO Loading Object heaty_minimal using class HeatyApp from module hass_apps_loader
2018-02-16 21:37:33.984743 INFO heaty_minimal: --- heaty v0.10.2 initialization started
2018-02-16 21:37:33.989374 INFO heaty_minimal: --- Heaty id is: 'default'
2018-02-16 21:37:34.001783 INFO heaty_minimal: --- Getting current temperatures from thermostats.
2018-02-16 21:37:34.028707 INFO heaty_minimal: --- Initialization done
2018-02-16 21:37:34.029213 WARNING ------------------------------------------------------------
2018-02-16 21:37:34.029686 WARNING Unexpected error during loading of temp_expression_modules:
2018-02-16 21:37:34.030042 WARNING ------------------------------------------------------------
2018-02-16 21:37:34.030820 WARNING Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/appdaemon/appdaemon.py", line 909, in read_app
    if module_name == conf.app_config[name]["module"]:
KeyError: 'module'

Hi @kjetilsn

First, you don’t need to put simple temperature expressions into a separate python module. There are examples in the README showing how to include expressions this way.

If you really want to use an external module, you usually just need to add its name:

temp_expression_modules:
  my_custom_module:

The as field is only required if your module’s name collides with another name present in the execution environment. If this doesn’t mean anything to you, safely ignore it for now.

Thank you for your help.
So I don’t need external module? I’m confused.
My goal is to schedule time for bedrooms in the basement, but also to have a switch in home assitant which can turn the schedule on and off.
My config below:

Basement:
  thermostats:
    climate.termostat_basement_livingroom_heating:
#        climate.termostat_bedroom1_heating:
    climate.termostat_bedroom2_heating:
    climate.thermostat_bedroom3_heating:
  schedule:
  #  no one home, set to 16
  - { temp: 16 if app.get_state('input_boolean.home') == 'off' else Ignore() }
  # From Monday to Friday, set temperature to 21 °C from 12:00 to 21.00
  - { temp: 21, start: "12:00", end: "20:00", weekdays: 6-7 }
  # Weekends, set temperature to 21 °C from 08:00 to 22.00
  - { temp: 21, start: "08:00", end: "22:00", weekdays: 1-5 }
  - { temp: 16 }

Abowe works, but when I change the state switch nothing happens, if i restart appdaeom it sees the change.
I will go trough the example one more time, I must have missed something.

@kjetilsn I got the same question this morning…

I now added a note to the inliningexample as well to make it crystal-clear:

https://github.com/efficiosoft/hass_apps/blob/master/hass_apps/heaty/doc/README.rst#example-use-of-an-external-module

Thank you roschi!

Now it works.

Sunday afternoon, and just read the full thread with Great interest as the kids and wife are sick.

I live in a large 6000sq foot home, with 39 radiators over 3 levels divided into 4 pump zones, a 400 litre domestic hot water tank and 2 heating sources which is an oil boiler and a solid fuel stove. After a major overhaul 14 months ago by a good plumber the house is for the first time working well from a heat perspective.

To manage this environment after due consideration I installed a Honeywell Evohome solution with the HGi80 gateway connected to Domoticz and a custom LUA script to interpit the communication from the TRV to the controller. This script is responsible for engaging the heat source and also the correct pump depending on the demand source. The logic is not over complex ensuring that if the solid fule is burning and generating hot water, the the oil source is not engaged for example.

While this works very well, and I am happy to share the LUA script, I have in recent weeks a major issue with this implementation. Evohome is not a mesh and has no range extender Technology, and is operating on a similar frequency to my baby monitors (2 of them) I am seeing random failures of communication between the Evohome controller and it’s endpoints, eg the boiler relay, DHW stat or relay, or random TRV at the extreme edge of the house. This results in the controller failing to demand heat or assuming demand is still needed as the sensor has not reported back.

So. It’s with great interest I read this thread and your code, as I am going to implement a smaller PRoof of Concept using the Spirt TRVs and a Zwave controller.

At this point I was going to build the logic myself, but I am ready to grab another Zwave.me module or stick and a spare PI3 to host your solution.

My question at the end of this long introduction is wheather you believe that your solution would be appropriate option here, my initial impression is yes (although in production I would have 15 rooms I think. 12 limit in evo) along the the zone pumps and DHW, and the two sources to be called based on demand.

My python skills are currently nill, i code JS and C# but I’m willing the learn and am a quick study.

Thank you for your response, and my utmost respect for what you have built

Regards
Damian

Hi @DamianFlynn

Wow, that’s a big house. :slight_smile:

I’m not sure whether Heaty is the appropriate way to go in your scenario.

There is no way to configure something like pumps or boilers, the whole point of Heaty is scheduling different temperatures for different times and based on arbitrarily complex external conditions.

So what it does is setting thermostats in a room to a temperature resulting from the evaluation of a schedule. It can’t turn pumps or boilers on or off, because it just doesn’t know what a pump is. :slight_smile:

I’m not sure whether you are aware of this fact?

Best regards
Robert

Thanks for the reply

Yep it’s a damn monster, reading the code I had begun to deduce that nothing is present to control the heat source.

I was expecting to see some logic which reduces that all radiators have reached thier setpoint, closed the valve and no longer demanding heat, therefore turn off the heat source.

That’s pretty much all I get me Evohome, this is why I added the HGI80 with Domoticz as I listen to the communication I can determine what TRV is demanding and thus Deduct if I need to engage a pump and a heat source, and if that’s the requirement I use Domoticz to control these devices.

My current consideration is to use your script as the equivalent logic of what EVO delivered and then extend this with my custome code that I have in Domoticz.

My real question is if you think this approach is feasible and what level of effort one might have to implement this on this app and ha environment

Best regards
Damian

@DamianFlynn Ok, so you know what you can expect from Heaty and what not.

I’m currently rewriting (or better, restructuring) some core parts to make it more readable and maintainable. I plan to finish this in late February. So maybe you want to wait with diving into the code until that happened.

To answer your question:

As long as you manage to get your thermostats recognized as entities in a Home Assistant installation, I don’t see a reason you shouldn’t be able to use Heaty for scheduling.

You could then write another app for appdaemon which monitors your thermostat’s setpoints and current temperature and controls the pumps/boilers accordingly. This would then be completely independent of Heaty.

Regarding the zwave.me modules… Maybe you want to have a look at the AeoTec Z-Stick Gen 5. From my experience, that one works far more reliable and has a far greater range than the ZME_UZB1 and Razberry modules and can be backed up and restored hassle-free. But you can of course take whatever module you like.

Hi all!

I’d like to invite you to test the brand-new modularized version of Heaty, which is a lot more advanced than the legacy one, at least in terms of code quality.

The background of the rewrite is that Heaty was my first “complex” AppDaemon app and grew pretty fast beyond what I originally expected. The whole code became difficult to understand, and thus difficult to maintain as well.

With the new approach, I splitted everything up into submodules, unified and adapted naming to common naming schemes, added Python type-hints and did a lot more to ensure Heaty will run solid and reliably. This changes will also simplify and drive the further development and extension of the hass_apps project in general.

As always with changes, there is the danger of accidentally breaking some working parts. Even though the linter and type checker don’t complain and I’ve did some testing, there might be pitfalls I didn’t find yet.

That’s why I kindly ask you to try out the new hass_apps in your own environments, which are probably different to mine.

To do so, please check out the heaty-modularization branch from GitHub:

git fetch
git checkout -b heaty-modularization origin/heaty-modularization
# Now install or copy hass_apps, just as you'd always do.
pip3 install . --upgrade

And, as always, to fetch updates:

git pull
pip3 install . --upgrade

To go back to the master branch:

git checkout master
pip3 install . --upgrade

There should be no need to change anything in your existing configuration. If you do need, you found a bug you should file. :slight_smile:

Your feedback is welcome here, but please report any bug or issue directly on GitHub.

Thanks a lot for taking the time to test!

Best regards
Robert

Thank you for the advice.

I spent a little time today reading about the HA platform, and i will admit its very impressive. My two sample Sprit TRV also arrived; and I have taken your advice, and ordered a AeoTec Z-Stick Gen 5 for the PI which i will install the framework on.

I grabbed a spare PI3 and installed the HASS.io image. Not sure if that was the right call or if I should have done a manual install on Raspbian or other similar os. Comments welcome also.

Hardware due next week so I have time to learn the platform before hand

Damian

I started looking at the comments in the code (very nice!) and it looks like Heaty will work with my single zwave thermostat that has one climate entity for heating mode and another for cooling mode. Is this correct?

Is anyone using Heaty with hass.io?

Many thanks!

I was considering it. But it’s to locked down. I am going to reinstall as hasbian or Raspbian with HA.

@DamianFlynn I’d use raspbian with a manual HA installation, it’s really simple and from what you’ve written before I’d expect you to appreciate the more control you get with a manual installation, compared to the docker setup on hass.io.