This is a custom ESPHome integration for interfacing Tiemme 4Heat controllers.
Simply add this to your config:
external_components:
source: github://leoshusar/4heat-esphome
And configure your components. You can also skip directly to the configuration example.
'0'
)data: '1'
will send {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'}
I made the response parsing quite strict. If the parsing fails, it will log a warning and ignore the received value.
Currently only bool
and int
parsing is supported, because I haven't seen any other data types (yet).
'0'
and the last one either '0'
or '1'
.Sometimes the data might look like this: D10000SYEVO0000562
.
Someone here found that the SYEVO
part is probably useless, so I simply strip it and work with only the part after. I have never seen this in a normal operation, the D10000
datapoint is some diagnostic something (response to C10000000000000000
). But keep this in mind when creating custom parsers - the received data might be only 7 characters long.
Every component supports custom data parser. You will get std::vector<uint8_t> data
to work with, with only the data part of the received message (without the datapoint ID), and you must return a value of the correct type. If you don't want to return anything (you receive data you don't expect), return {}
- empty std::optional
.
If your parser doesn't return any value, the default one is then not called.
Use case example: Datapoint J40007 tells if external thermostat relay is ON or OFF. But it doesn't return boolean value 0 or 1, it returns 000000000100
if it's OFF and 000000000000
if it's ON. So I used custom parser like this:
binary_sensor:
- platform: fourheat
name: Room thermostat state
datapoint: J40007
parser: return data[data.size() - 3] == '0';
This code simply checks if the 3rd last char is '0'
.
As written above, datapoints usually look like XYYYYY
, where X is a letter and Y is a digit.
Components are configured using response datapoints. Query datapoint is then calculated automatically (by "decrementing" the first prefix letter by one). You can also specify your own query datapoint, which will override the calculated one.
Components like number or select are listening on both query and response datapoints. If you send a command to read/write datapoint, the controller responds immediately, but on the query datapoint. With this you will get immediate confirmation and you won't need to use optimistic mode.
That person here also found that there are different Tiemme controllers. Some will work with this integration, some might work, some won't, because they use Hayes AT commands.
You only need an ESP and an RS232 module.
My controller uses RJ 11 6P4C connector and has this pinout:
Don't use the VCC for powering your ESP though, there is not much current and your 4Heat controller will start acting strange.
You can find my full config in the example.yaml.
I am using external room thermostat. If you want to use the internal one, you should be able to use J30006
for reading room temperature (sensor component) and B20493
for reading and setting target room temperature (number component). Or you can merge them all in the full climate component.
If you have the official WiFi module and want to integrate it to HA, you can use this integration (or my fork with an attempt to handle timeouts somehow).
But I recommend switching to ESPHome, if you can. Their mobile app is... not great, the WiFi module itself often timeouts, it also SENDS DATA TO THEIR AZURE CLOUD VIA UNENCRYPTED HTTP. It also doesn't report too often, only like once or twice per minute. Even if you spam the module every second, the module just responds with cached values.
The base component which handles all UART communication. Requires UART Bus.
Be aware that UART0
is by default used by the logger
component. If you want to use it for fourheat
, you either need to disable logging into serial or change the logger
UART to other one.
# Example configuration entry
uart:
tx_pin: GPIO5
rx_pin: GPIO4
baud_rate: 9600
fourheat:
To disable serial logging:
logger:
baud_rate: 0
The fourheat
binary sensor platform creates a binary sensor from a fourheat component.
Requires FourHeat Component to be configured.
binary_sensor:
# Datapoint binary sensor
- platform: fourheat
name: My Binary Sensor
datapoint: J30000
query_datapoint: I30000
parser: return data[data.size() - 1] != '0';
# Offline sensor
- platform: fourheat
name: Module Offline Sensor
type: module_offline
binary_sensor
type. Defaults to datapoint
.
datapoint
: datapoint sensor.module_offline
: indicates if the 4Heat controller is offline.datapoint
with prefix letter decremented by one.std::vector<uint8_t> data
and must return bool
.The fourheat
button platform creates a button from a fourheat component.
Requires FourHeat Component to be configured.
Since the button platform is stateless, it doesn't query any data.
button:
- platform: fourheat
name: My Button
datapoint: J30000
press_data: '1'
'1'
.The fourheat
climate platform creates a climate from a fourheat component.
Requires FourHeat Component to be configured.
Currently only heating mode is supported, just because I don't know if 4Heat supports anything with cooling.
climate:
- platform: fourheat
name: My Climate
datapoint: J30000
current_temperature_datapoint: J30001
target_temperature_datapoint: B20000
query_datapoint: I30000
query_current_temperature_datapoint: I30001
query_target_temperature_datapoint: A20000
on_datapoint: J30010
off_datapoint: J30011
on_data: '1'
off_data: '1'
parser: return data[data.size() - 1] != '0';
current_temperature_parser: |-
std::string int_str(data.begin(), data.end());
return stoi(int_str);
target_temperature_parser: |-
std::string int_str(data.begin(), data.end());
return stoi(int_str);
datapoint
with prefix letter decremented by one.current_temperature_datapoint
with prefix letter decremented by one.target_temperature_datapoint
with prefix letter decremented by one.'1'
.'0'
.std::vector<uint8_t> data
and must return bool
.std::vector<uint8_t> data
and must return int
.std::vector<uint8_t> data
and must return int
.The fourheat
number platform creates a number from a fourheat component.
Requires FourHeat Component to be configured.
number:
- platform: fourheat
name: My Number
datapoint: B20000
query_datapoint: A20000
min_value: 0
max_value: 90
step: 1
parser: |-
std::string int_str(data.begin(), data.end());
return stoi(int_str);
datapoint
with prefix letter decremented by one.std::vector<uint8_t> data
and must return int
.The fourheat
select platform creates a select from a fourheat component.
Requires FourHeat Component to be configured.
select:
- platform: fourheat
name: My Select
datapoint: B20000
query_datapoint: A20000
optimistic: true
options:
1: "Option A"
2: "Option B"
3: "Option C"
parser: |-
std::string int_str(data.begin(), data.end());
return stoi(int_str);
datapoint
with prefix letter decremented by one.std::vector<uint8_t> data
and must return int
.The fourheat
sensor platform creates a sensor from a fourheat component.
Requires FourHeat Component to be configured.
sensor:
- platform: fourheat
name: My Sensor
datapoint: J30000
query_datapoint: I30000
parser: |-
std::string int_str(data.begin(), data.end());
return stoi(int_str);
datapoint
with prefix letter decremented by one.std::vector<uint8_t> data
and must return int
.The fourheat
switch platform creates a switch from a fourheat component.
Requires FourHeat Component to be configured.
switch:
- platform: fourheat
name: My Switch
datapoint: J30000
query_datapoint: I30000
on_datapoint: J30010
off_datapoint: J30011
on_data: '1'
off_data: '1'
parser: return data[data.size() - 1] != '0';
datapoint
with prefix letter decremented by one.'1'
.'0'
.std::vector<uint8_t> data
and must return bool
.The fourheat
text sensor platform creates a text sensor from a fourheat component.
Requires FourHeat Component to be configured.
text_sensor:
- platform: fourheat
name: My Text Sensor
datapoint: J30000
query_datapoint: I30000
options:
1: "Option A"
2: "Option B"
3: "Option C"
parser: |-
std::string int_str(data.begin(), data.end());
return stoi(int_str);
datapoint
with prefix letter decremented by one.std::vector<uint8_t> data
and must return int
.