Skip to content

Commit 1e0daf9

Browse files
committed
Add external sensor example
1 parent da59083 commit 1e0daf9

File tree

4 files changed

+210
-1
lines changed

4 files changed

+210
-1
lines changed

.github/workflows/build-example.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ jobs:
2121
- name: Compile example
2222
working-directory: esphome
2323
run: esphome compile example.yaml
24+
- name: Compile example external sensor
25+
working-directory: esphome
26+
run: esphome compile example-external-sensor.yaml

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ This code is inspired by [@absalom-muc's MHI-AC-Ctrl](https://github.com/absalom
2424

2525
## Getting started
2626

27-
This project is used in ESPHome as external component. The [ESPHome documentation](https://esphome.io/guides/getting_started_hassio) will help you getting started with ESPHome. You can base off your configuration from the [`example.yaml`](esphome/example.yaml) included in this repository.
27+
This project is used in ESPHome as external component. The [ESPHome documentation](https://esphome.io/guides/getting_started_hassio) will help you getting started with ESPHome. You can base off your configuration from the [`example.yaml`](esphome/example.yaml) included in this repository. Once you got that going and want to an external temperature sensor, have a look at [`example-external-sensor.yaml`](esphome/example-external-sensor.yaml).
2828

2929
In general, the latest version of ESPHome should work. See the [build workflow](.github/workflows/build-example.yml#L17) for the latest version of ESPHome that was tested.
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# For all configuration options, see the example.yaml file. This file only
2+
# shows what you can do with an external Bluetooth temperature sensor.
3+
#
4+
# Compared to some other projects, this project does not alter the external
5+
# sensor value. It passes the rounded value (0.25 degrees Celsius precision) to
6+
# the AC. That means all the trickery has to happen in the yaml file, which
7+
# this example will show.
8+
substitutions:
9+
mhi_device_id: "mhi_ac_example"
10+
# Unique device ID in HA
11+
device_id: "ac_example"
12+
# Unique device name in HA (sensor names will be prefixed by this name)
13+
device_name: "AC Example"
14+
external_temperature_sensor_id: atc_temperature
15+
16+
esphome:
17+
name: ac-example
18+
platformio_options:
19+
board_build.flash_mode: dio
20+
21+
external_components:
22+
- source: components
23+
# Or, directly refer to the Git repository
24+
# - source: github://hberntsen/mhi-ac-ctrl-esp32@master
25+
26+
esp32:
27+
board: esp32-c3-devkitm-1
28+
# Run CPU at slower speed to save power
29+
cpu_frequency: 80MHz
30+
framework:
31+
type: esp-idf
32+
# Add Bluetooth 5 support
33+
sdkconfig_options:
34+
CONFIG_BT_BLE_50_FEATURES_SUPPORTED: y
35+
36+
esp32_ble_tracker:
37+
scan_parameters:
38+
active: false
39+
40+
MhiAcCtrl:
41+
id: ${mhi_device_id}
42+
mosi_pin: GPIO7
43+
miso_pin: GPIO2
44+
sclk_pin: GPIO6
45+
use_long_frame: true
46+
# Send the value of this temperature sensor to the AC (degrees Celsius)
47+
external_temperature_sensor: ${device_id}_calculated_external_sensor
48+
49+
climate:
50+
- platform: MhiAcCtrl
51+
mhi_ac_ctrl_id: ${mhi_device_id}
52+
id: ${device_id}
53+
name: "${device_name}"
54+
visual:
55+
temperature_step:
56+
# Upgrade to 0.5 degrees Celsius, we're adding support for this by
57+
# manipulating the external sensor in the lambda later below.
58+
target_temperature: 0.5
59+
current_temperature: 0.25
60+
on_control:
61+
- then:
62+
- lambda: id(${device_id}_calculated_external_sensor).update();
63+
64+
sensor:
65+
- platform: MhiAcCtrl
66+
mhi_ac_ctrl_id: ${mhi_device_id}
67+
# The current temperature in the climate component will use the filtered
68+
# value from this sensor.
69+
#
70+
# Use case: filtering out internal temperature sensor jitter and overriding
71+
# climate temperature with external, when available.
72+
climate_current_temperature:
73+
# Only id, no name so it won't be sent to home assistant as separate sensor
74+
id: climate_current_temperature
75+
accuracy_decimals: 2
76+
filters:
77+
# Override the climate's temperature with our external sensor, if it is
78+
# available. Otherwise, our manipulated sensor value is shown on the
79+
# Climate card in Home Assistant.
80+
- lambda: |-
81+
float Troom = id(${external_temperature_sensor_id}).state;
82+
if(!isnan(Troom)) {
83+
return Troom;
84+
}
85+
return x;
86+
# Check for temperature changes only every 10 seconds
87+
- heartbeat: 10s
88+
# The climate component will only send updates if the value is different
89+
90+
# The return_air_temperature sensor will show the external (or internal if
91+
# the external sensor is unavailable) temperature value we have sent to the
92+
# AC.
93+
return_air_temperature:
94+
name: Return air temperature
95+
# When the setpoint changes, also update the external sensor's value
96+
set_temperature:
97+
name: Set temperature
98+
id: ${device_id}_set_temperature
99+
on_value:
100+
- then:
101+
- lambda: id(${device_id}_calculated_external_sensor).update();
102+
103+
# Example: use this Bluetooth sensor as external temperature sensor
104+
- platform: pvvx_mithermometer
105+
mac_address: "AA:BB:CC:DD:EE:FF"
106+
temperature:
107+
id: atc_temperature
108+
name: "ATC Temperature"
109+
filters:
110+
# Stop using this sensor when it did not update in the last 10 minutes.
111+
- timeout: 10min
112+
# Filter out 0 degrees, some bug makes it sometimes send 0.
113+
- filter_out: 0
114+
on_value:
115+
- then:
116+
- lambda: id(${device_id}_calculated_external_sensor).update();
117+
118+
- platform: template
119+
name: Calculated external sensor
120+
id: ${device_id}_calculated_external_sensor
121+
unit_of_measurement: "°C"
122+
entity_category: diagnostic
123+
lambda: |-
124+
float Troom = id(${external_temperature_sensor_id}).state;
125+
float Tset = id(${device_id}).target_temperature;
126+
127+
// Use the internal sensor when the external one is not available, or we have no set point
128+
if (isnan(Troom) || isnan(Tset)) {
129+
return NAN;
130+
}
131+
132+
float TsetInternal = max(18.f, Tset);
133+
float TsetReported = id(${device_id}_set_temperature).state;
134+
135+
// The AC internally won't go lower than 18, support lower temperatures through
136+
// increasing the external temperature
137+
Troom += TsetInternal - Tset;
138+
139+
// The AC increases your set point internally. The internal set point is set_temperature in the operation data.
140+
// Known reasons for increases:
141+
// * The AC internally adds 2 degrees when heating, unless turned off (excessive heating compensation).
142+
// * 3D auto heating adds 1 degree
143+
//
144+
// We undo these compensations, so it will heat to exactly the set point we want
145+
if (!isnan(TsetReported)) {
146+
Troom += TsetReported - TsetInternal;
147+
}
148+
149+
150+
/*
151+
Limit the max temperature delta via a slider. When heating/cooling, the
152+
AC will see a temperature that is closer to your set point. Some AC
153+
models will reduce their power when they are made to believe they are
154+
almost there. Some might ramp up since it takes too long to get there. So
155+
you need to test with this. You can safely remove this block and the
156+
slider if you don't want to use this.
157+
*/
158+
float TmaxDelta = id(${device_id}_max_temperature_delta).state;
159+
if (id(${device_id}).mode == climate::CLIMATE_MODE_HEAT) {
160+
// Heating: clamp downward only (don't suppress a hot room reading)
161+
Troom = max(Troom, TsetReported - TmaxDelta);
162+
} else if (id(${device_id}).mode == climate::CLIMATE_MODE_COOL) {
163+
// Cooling: clamp upward only (don't suppress a cold room reading)
164+
Troom = min(Troom, TsetReported + TmaxDelta);
165+
}
166+
167+
// Use our new calculated value.
168+
return Troom;
169+
170+
number:
171+
# This slider defines the limit of how much degrees Celsius of a difference
172+
# between the external sensor and the AC's setpoint you want to be sent to
173+
# the AC. It is used by the calculated external sensor lambda above.
174+
#
175+
# For example:
176+
# * Your external temperature sensor says it is 18 degrees
177+
# * You set your AC to 20 degrees in heating mode
178+
# * You set this slider to 10 degrees
179+
#
180+
# Then, the AC will see 18 degrees as room temperature, since 20-18 > 10.
181+
#
182+
# Suppose you now set this slider to 1.0, the AC will receive 19 degrees room
183+
# temperature instead of 18. You might be able to use this to reduce power
184+
# consumption and let your room heat/cool more gradually.
185+
- platform: template
186+
id: ${device_id}_max_temperature_delta
187+
name: Max temperature delta
188+
min_value: 0
189+
max_value: 10
190+
step: 0.25
191+
initial_value: 2
192+
optimistic: true
193+
on_value:
194+
- then:
195+
- lambda: id(${device_id}_calculated_external_sensor).update();
196+
197+
switch:
198+
- platform: MhiAcCtrl
199+
mhi_ac_ctrl_id: ${mhi_device_id}
200+
active_mode:
201+
name: Active mode
202+
id: ${device_id}_active_mode
203+

esphome/example.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ MhiAcCtrl:
3434
# Optional config, default values listed here
3535
# Set to false on older AC units that don't support the longer frames
3636
use_long_frame: true
37+
# Send the value of this temperature sensor to the AC (degrees Celsius)
38+
# See example-external-sensor.yaml
39+
# external_temperature_sensor:
3740

3841
climate:
3942
- platform: MhiAcCtrl

0 commit comments

Comments
 (0)