Honeywell CH/DHW via RF - evohome, sundial, hometronics, chronotherm

oke, nothing i can change about i think, or is it a signal problem of the antenna because there are many of these messages in the log?

Hi David - I’ve noticed an issue with the latest version (I was a few released behind until this week). Previously when I called climate.set_temperature, the default behaviour was a temporary override - which was perfect for my needs. It seems that the new default behaviour is permanent override… again not a problem because in theory I should just be able to call climate.set_mode to “temporary” on that zone. However, when I try that it throws 2 errors in the log. It’s like somewhere behind the scenes the set_mode requires a setpoint temperature to also be specified, but the HA service call doesn’t allow any additional parameters.


Logger: homeassistant.helpers.script.websocket_api_script
Source: custom_components/evohome_cc/climate.py:272 
First occurred: 13:23:23 (1 occurrences) 
Last logged: 13:23:23

websocket_api script: Error executing script. Unexpected error for call_service at pos 1: Invalid args: For temporary_override, setpoint cant be None
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 359, in _async_step
    await getattr(self, handler)()
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 559, in _async_call_service_step
    await service_task
  File "/usr/src/homeassistant/homeassistant/core.py", line 1480, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1515, in _execute_service
    await handler.job.target(service_call)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 206, in handle_service
    await self.hass.helpers.service.entity_service_call(
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 649, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 692, in async_request_call
    await coro
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 686, in _handle_entity_call
    await result
  File "/usr/src/homeassistant/homeassistant/components/climate/__init__.py", line 450, in async_set_preset_mode
    await self.hass.async_add_executor_job(self.set_preset_mode, preset_mode)
  File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/evohome_cc/climate.py", line 245, in set_preset_mode
    self.svc_set_zone_mode(mode=ZoneMode.TEMPORARY)
  File "/config/custom_components/evohome_cc/climate.py", line 272, in svc_set_zone_mode
    self._device.set_mode(mode=mode, setpoint=setpoint, until=until)
  File "/usr/local/lib/python3.8/site-packages/evohome_rf/zones.py", line 799, in set_mode
    cmd = Command.set_zone_mode(self._ctl.id, self.idx, mode, setpoint, until)
  File "/usr/local/lib/python3.8/site-packages/evohome_rf/command.py", line 513, in set_zone_mode
    raise ValueError(f"Invalid args: For {mode}, setpoint cant be None")
ValueError: Invalid args: For temporary_override, setpoint cant be None

Logger: homeassistant.components.websocket_api.http.connection
Source: custom_components/evohome_cc/climate.py:272 
Integration: Home Assistant WebSocket API (documentation, issues) 
First occurred: 13:23:23 (1 occurrences) 
Last logged: 13:23:23

[140626796560000] Error handling message: Unknown error
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/decorators.py", line 18, in _handle_async_response
    await func(hass, connection, msg)
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 439, in handle_execute_script
    await script_obj.async_run(context=context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 1195, in async_run
    await asyncio.shield(run.async_run())
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 341, in async_run
    await self._async_step(log_exceptions=False)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 359, in _async_step
    await getattr(self, handler)()
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 559, in _async_call_service_step
    await service_task
  File "/usr/src/homeassistant/homeassistant/core.py", line 1480, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1515, in _execute_service
    await handler.job.target(service_call)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 206, in handle_service
    await self.hass.helpers.service.entity_service_call(
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 649, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 692, in async_request_call
    await coro
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 686, in _handle_entity_call
    await result
  File "/usr/src/homeassistant/homeassistant/components/climate/__init__.py", line 450, in async_set_preset_mode
    await self.hass.async_add_executor_job(self.set_preset_mode, preset_mode)
  File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/evohome_cc/climate.py", line 245, in set_preset_mode
    self.svc_set_zone_mode(mode=ZoneMode.TEMPORARY)
  File "/config/custom_components/evohome_cc/climate.py", line 272, in svc_set_zone_mode
    self._device.set_mode(mode=mode, setpoint=setpoint, until=until)
  File "/usr/local/lib/python3.8/site-packages/evohome_rf/zones.py", line 799, in set_mode
    cmd = Command.set_zone_mode(self._ctl.id, self.idx, mode, setpoint, until)
  File "/usr/local/lib/python3.8/site-packages/evohome_rf/command.py", line 513, in set_zone_mode
    raise ValueError(f"Invalid args: For {mode}, setpoint cant be None")
ValueError: Invalid args: For temporary_override, setpoint cant be None

I should have read back through the thread before I posted… I am passing my zones through to Apple HomeKit and if I change the setpoint temperature of a zone via Siri then it ends up as a permanent override in the latest version. Whilst I’m aware of what I’ve done and can remember to change it back, my family are annoyed at me because their bedroom heating is staying on all the time!

@zxdavb - I think that as this particular behaviour is subjective to each individual user, it might be an idea to have a configuration parameter in YAML to set the preferred override type. Obviously if your preference is for PermanentOverride then leave that as the default, but a config parameter that allows AdvancedOverride would be most welcome. Is that something that’s easy to implement? Looking at phdlodder’s example to manually fix it, it looks like it could be a simple tweak. I’m going to have to roll back to 0.6.6 for a while because of shouty children :frowning:

(Edit 2021-04-11): I’ve dropped back to 0.7.6 which seems to use a temporary override by default and works in a way compatible with me for the moment! I get a warning about evohome_rf using the mismatched 0.8.0 and that being a bad thing (I can’t figure out why that newer version is being picked up?), but most things appear to actually work despite that.

I would like the temp override also as default, noticed that it is indeed permanent Now what is not practical.

Hi! Allow me to introduce myself here, well at least my setup. I think it may be interesting for development purposes. Please confirm?
1x HGI80 and 2 independent Evohome thermostats here (+ suspected neighboring systems).
Thermostat A controls Boiler A with 8 HR92’s in 8 zones, Thermostat B controls boiler B with 5 HR92’s in 4 zones.
The HGI80 is running on a new Home Assistant Blue Odroid N2+ version which I will keep for testing purposes for now. (I plan to migrate my main home assistant instance to the blue odroid once the integration is deemed mature enough.)
For me - I would like to be able to control both my thermostats with my single HGI-80. Would i.e. declaring two schema’s work out? Would it require two instances? Something else? Looking forward to any replies.

interesting to note:
I have enabled one schema by declaring one controller ID in evohome_cc.
It appears to report other devices as well. Like: 18 temperature sensors, 23 window sensors, 6 actuators, a bunch of battery sensors, heat demand sensors, fanrate, humidity. I didnt count them but multiple are reported.

edit: at least one neighbouring system is confirmed. I am seeing 3 systems with my HGI80, two of them are my own…
edit2: despite providing one schema (one controller) the integration continues to pick up devices bound to other controllers…

TL;DR: a TRV is not a zone.

evohome_rf (and thus evohome_cc) deals with two distinct classes of ‘thing’:

  • devices that broadcast using the RAMSES-II protocol (e.g. temperature sensors, battery levels, etc.)
  • temperature control systems (TCS, e.g. an evohome controller) and their zones

Unlike an allow list or a block list, which deal exclusively with devices, the schema deals with these TCSs. It simply outlines the architecture of a heating system, identifying which devices perform which roles within that system (the temp sensor for each zone, which relay for the heating appliance, etc.).

There are 3 ways that a schema can be made:

  • manually (recommended to specify your controller),
  • via discovery (recommended, on by default), and
  • via eavesdropping (do not use unless you have to - e.g. HR80s).

The truth is, a schema is only needed for two reasons:

  • to ‘calculate’ a zone’s heat demand from the aggregate demand of that zone’s actuators (there is no other way of obtaining this information - the controller simply wont tell)
  • to nominate which controller (and so which TCS) is the one you’re interested in - the ‘default’ TCS (this also decreases discovery time but up to 3-5 minutes).

In fact, evhome_rf tracks all TCSs that it can see (unless a allow/block list gets in the way) - it’s just that evohome_cc only exposes the default TCS to you (by default, I mean the first one that evohome_rf encounters)…

… as well as all the devices it can see, regardless of which TCS they are from.

There is no support for multiple TCSs per evohome_cc - and I do not expect to take on this burden for so few people.

You could run multiple instances of evohome_cc (but one will have to be ‘renamed’) - each with it’s own schema (i.e. it’s own controller), along with an allow list to exclude the other TCS’s devices (you could theoretically do this with a block list, but don’t - allow lists are much better).

Additionally, the challenge with using a single USB interface for this will be a problem (you can get past this with ser2net), especially if it is a HGI80 - it will quickly exceed it’s duty cycle with so many devices (but you can get past this with the restore state feature).

I would strongly recommend buying another dongle - it will just be eaiser:

The other alternative is to use two copies of the official evohome integration for each TCS, and evohome_cc for the devices.

Well, today, I unplugged my Honeywell Internet gateway - we have not needed to use the app for at least a month, and no more searching Twitter every morning to check @honeywell_home to see what the latest outage was, or when they’d next be performing maintenance during peak time.

So, @zxdavb many thanks to the amount of time you’ve put into this project, it is much appreciated.

Thank you @zxdavb for all the work you have put into this, it’s amazing!

I have a Honeywell Sundial RF Pack in my house and I’ve been able to get parts of it into Home Assistant using evohome_cc installed via HACS.

I can see packets flowing in my packets.log and by pressing buttons and watching the log I can see that the I, RQ, RP and W packets seem to match the “common codes” listed in your EvoHome protocol wiki.

Unfortunately it seems that my Schema isn’t generating properly, despite me trying to force the controller and allow list in my configuration.yaml. I believe it is because my ST9120C Wireless Enabled Timer isn’t in your DEVICE_MAP - it has a device ID of 23:xxxxxx (Although I do see a CTL: 23 referenced in devices.py but as a programmer rather than a controller). The DT92E is discovered correctly as a Thermostat (Device ID 22:xxxxxx) - although I did also see some entries for a device starting with 12: and I am not sure what that is as it only seems to broadcast I messages.

2021-04-15T16:30:29.406132 074  I --- --:------ --:------ 12:xxxxxx 1030 016 01C80137C9010FCA0196CB010FCC0101
2021-04-15T16:30:30.399172 073  I --- --:------ --:------ 12:xxxxxx 1030 016 01C80137C9010FCA0196CB010FCC0101
2021-04-15T16:30:31.399137 074  I --- --:------ --:------ 12:xxxxxx 1030 016 01C80137C9010FCA0196CB010FCC0101
2021-04-15T16:30:31.415172 072  I --- --:------ --:------ 12:xxxxxx 0008 002 00C8
2021-04-15T16:30:32.389174 073  I --- --:------ --:------ 12:xxxxxx 0008 002 00C8
2021-04-15T16:30:33.389091 073  I --- --:------ --:------ 12:xxxxxx 0008 002 00C8
2021-04-15T16:30:34.394453 072  I --- --:------ --:------ 12:xxxxxx 313F 009 0038031E710F0407E5

Here is my Schema:
Schema = {'controller': '23:xxxxxx', 'system': {'heating_control': None}, 'orphans': [], 'stored_hotwater': {}, 'underfloor_heating': {}, 'zones': {}}

Any idea how I can get this imported correctly?

I don’t know if someone is interested in, but I created some customized lovelace cards using
simple-thermostat and service override to custom scripts
custom button:card and custom-slider

and two simple scripts that I created, which are used to forward the commands to evohome_cc

Here is a small example. If there is interest I can post the scripts and lovelace setting.
Screenshot 2021-04-15 at 21.39.38

4 Likes

Looks nice, I’m interested! Perhaps create a page on the wiki Home · zxdavb/evohome_cc Wiki · GitHub ?

@kardiologia Amazing! Please do put it on the wiki if you could.

The Sundial support is ‘experimental’ - mainly because I have had no active testers.

… which is, sadly, very out of date.

First step is to provide me with a packet log. From startup of HA and for 10 minutes after that, with the restore_state feature disabled and no schema.

Or, if you are able:

python client.py monitor /dev/ttyACM0 -o packet.log

I created the wiki for the customized lovelace cards.

I hope it will be helpful.

2 Likes

I would happily be a tester!

Here is the output from client.py monitor

Schema[23:145582 (programmer)] = {
    "controller": "23:145582",
    "system": {
        "heating_control": null
    },
    "orphans": [],
    "stored_hotwater": {},
    "underfloor_heating": {},
    "zones": {}
}

Params[23:145582 (programmer)] = {
    "system": {
        "tpi_params": null,
        "system_mode": null,
        "language": null
    },
    "stored_hotwater": {},
    "underfloor_heating": {},
    "zones": {}
}

Status[23:145582 (programmer)] = {
    "system": {
        "heat_demand": null,
        "datetime": null,
        "heat_demands": null,
        "relay_demands": null,
        "relay_failsafes": null
    },
    "devices": {},
    "stored_hotwater": {},
    "underfloor_heating": {},
    "zones": {}
}

Status[gateway] = {
    "orphans": {
        "12:051585": {
            "temperature": null,
            "setpoint": null,
            "battery_state": null
        },
        "22:084317": {
            "temperature": null,
            "setpoint": null,
            "battery_state": null
        },
        "22:084660": {
            "temperature": null,
            "setpoint": 21.5,
            "battery_state": null
        },
        "23:145691": {}
    }
}
Schema[devices] = {
    "devices": {
        "12:051585": {},
        "22:084317": {},
        "22:084660": {},
        "23:145582": {},
        "23:145691": {}
    }
}

I have two heating circuits/zones. Each one is controlled by a ST9120C paired with a DT92E, connected to a single Valiant combi boiler. I am aware that your evohome_cc only supports one controller, so I was hoping to get it working with one using an allow_list and then install another instance of your custom_component under a different name for the other.

Let me know if you need anything else!

Can you perhaps add a screenshot on the wiki?

I tried but I didn’t find an option to upload images, only as an external link

I’ve tried to install the component to get my new ssm-32u4 working. I can see logging in the packet.log that suggests something is happening within evohome_rf:

2021-04-17T00:00:23.413567 070  I --- --:------ --:------ 10:124973 1FD4 003 0047E5 
2021-04-17T00:00:24.329733 074  I --- 22:206071 --:------ 22:206071 30C9 003 0006F7 
2021-04-17T00:00:25.023847 059 RQ --- 01:081046 10:124973 --:------ 3EF0 001 00 
2021-04-17T00:00:25.051746 071 RP --- 10:124973 01:081046 --:------ 3EF0 006 000010020000 
2021-04-17T00:00:28.329247 073  I --- 22:206071 --:------ 22:206071 30C9 003 0006F7 
2021-04-17T00:00:39.329702 067  I --- 22:210057 --:------ 22:210057 30C9 003 000696 
2021-04-17T00:00:40.328820 066  I --- 22:210057 --:------ 22:210057 30C9 003 000696 
2021-04-17T00:00:53.625625 070  I --- --:------ --:------ 10:124973 1FD4 003 0047E6 
2021-04-17T00:01:08.818700 066  I --- 04:254312 --:------ 04:254312 30C9 003 0008C8

But when loading the evohome_cc component I get the following error:

Logger: homeassistant.setup
Source: custom_components/evohome_cc/__init__.py:195
First occurred: April 16, 2021, 11:37:42 PM (1 occurrences)
Last logged: April 16, 2021, 11:37:42 PM

Error during setup of component evohome_cc
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/setup.py", line 242, in _async_setup_component
    result = await task
  File "/config/custom_components/evohome_cc/__init__.py", line 105, in async_setup
    await broker.async_restore_client_state()
  File "/config/custom_components/evohome_cc/__init__.py", line 195, in async_restore_client_state
    await self.client._set_state(**app_storage["client_state"])
  File "/usr/local/lib/python3.8/site-packages/evohome_rf/__init__.py", line 303, in _set_state
    _, tmp_transport = create_pkt_stack(
  File "/usr/local/lib/python3.8/site-packages/evohome_rf/transport.py", line 800, in create_pkt_stack
    ser_instance = serial_for_url(serial_port, **serial_config)
  File "/usr/local/lib/python3.8/site-packages/serial/__init__.py", line 90, in serial_for_url
    instance.open()
  File "/usr/local/lib/python3.8/site-packages/serial/serialposix.py", line 316, in open
    raise SerialException("Port must be configured before it can be used.")
serial.serialutil.SerialException: Port must be configured before it can be used.

Any help much appreciated. I’ve had home assistant up and running for a while but this is the first custom component I’ve tried to setup.

What’s the difference between “heat demand” and “relay demand”?

One is a request (heat), the other is an action (relay) - the latter, for the FC domain, is known as a ‘call for heat’.

Each actuator can send the controller a heat demand (a call for heat) - the controller constructs the heat demand for each zone from that information, moderated by any optimizations, etc. (this is not trivial). The system heat demand is then calculated from that, and becomes a relay demand instruction to the boiler controller (FC, +/- any other relays F9/FA). The relay will announce the demand it is doing (which hopefully matches what was asked of it).

[ TIP: the primary difference between an electric zone, and a zone valve zone is that an EZ’s heat demand is ignored when constructing the system’s overall heat demand. That is, an EZ cannot cause a call for heat.]

what percentage of time it needs to…

Of course, for OpenTherm, the system heat demand is not a % of the TPI cycle, but is more complex.

The controller object has a heat_demands object, and a relay_demands object - have a look. Be clear - there is a difference between what the zones request, what the controller instructs, and what the relays (FC, and F9/FA) do.

The oddity was that I also see a relay demand on the room thermostats

An anachronism.

For the “window open” feature I like that it’s exposed as a sensor, but Domoticz seemed to use that to adjust the “mode” of the zone to “Open Window”

Open window is a state, not a mode. it ‘overrules’ any zone override, which itself takes precedence over any schedule/mode pair (e.g. scheduled setpoint,/Away mode).

The controller does not reveal if a zone is ever in OpenWindow state, or not - it is inferred by evohome_rf via eavesdropping packets sent from TRVs… If evhome_rf picks up one of these packets, and the controller does not, then is evohome_rf wrong (c.f. controller UI) or right (c.f. TRV behaviour)?

Please consider adding the following to your configuration.yaml:

sensor:
  - platform: template
    sensors:
      f9_heat_demand:
        friendly_name: "F9 Heat Demand"
        unit_of_measurement: "%"
        value_template: "{{ state_attr('climate.controller', 'heat_demands').F9 }}"

      fa_heat_demand:
        friendly_name: "FA Heat Demand"
        unit_of_measurement: "%"
        value_template: "{{ state_attr('climate.controller', 'heat_demands').FA }}"

      fc_heat_demand:
        friendly_name: "FC Heat Demand"
        unit_of_measurement: "%"
        value_template: "{{ state_attr('climate.controller', 'heat_demands').FC }}"

      fc_relay_demand:
        friendly_name: "FC Relay Demand"
        unit_of_measurement: "%"
        value_template: "{{ state_attr('climate.controller', 'relay_demands').FC }}"

Please provide a copy of the evohome_cc section of your configuration.yaml

# Evohome
evohome_cc:
  serial_port: /dev/serial/by-id/usb-SparkFun_evofw3_atmega32u4-if00
  packet_log: packet.log
#  schema:
#    controller: 01:081046
  
logger:
  logs:
    evohome_rf.packet_log: debug

With the serial port lifted from the following:

~ $ ls /dev/serial/by-id/
usb-SparkFun_evofw3_atmega32u4-if00