This is so cool! But...couple of questions?
Opened this issue · 19 comments
Hi,
I have this running to control three fans on my inverter. Soon I will expand to cover a second inverter with a second set of fans (once I move out of breadboard stage). You made it so super easy to get up and running!
Question 1:
The main issue with inverter cooling is that I need a responsive cooling system but one that is skewed - I need a fast(ish) response and the long slow tail....is this where kd comes in?
Question 2:
I have a DHT dangling over the back of the inverter radiator to read the air temperature, but really I need something better to read the radiator fin temperature directly, or a way of pushing the inverter internal temperature (which HA knows) to the fan controller...but with the option to still use DHT incase HA fails for some reason. That is, use inverter internal temp reading and if null revert to DHT temperature. Is this possible? if so how the heck could I pull that off?
Question 3:
I'd really like to bring in the tachometer data so that I can detect fan failure. I can do all the logic in HA/NodeRed, just need to plumb up the pins and get a reading....any tips? I've seen a few ways of doing it with resistors, logic level shifters, capacitors etc....but it seems a little complicated? From the looks of things I have exactly the same fans as you... :-)
Thanks heaps!
CP.
Lolin D32, 2 Groups witch 2 Arctic P12 PWM PST each, 2 Dallas DS18B20.
for the PWM signal a 3.3 to 5V level shifter, it doesn't work without.
Q3:
The speed can also be read directly with an ESP. i use the simple attached circuit for this, but with one change. my Lolin D32 has internal pullups, so the external 10k resistor is not needed.
for your board you have to check yourself which GPIO have a pullup or use the external resistor.
sensor:
# RpM Fans outward
- platform: pulse_counter
pin:
number: GPIO13
mode: INPUT_PULLUP # activate internal pullup
name: ${nodename}" RPM outward"
unit_of_measurement: "UpM"
accuracy_decimals: 0
id: ${nodename}_rpm_outward
update_interval: 5s
filters:
- multiply: 0.5 # 2 pulse per rotation
# RpM Fans inward
- platform: pulse_counter
pin:
number: GPIO27
mode: INPUT_PULLUP
name: ${nodename}" RPM inward
unit_of_measurement: "UpM"
accuracy_decimals: 0
id: ${nodename}_rpm_inward
update_interval: 5s
filters:
- multiply: 0.5
Q2:
you should get the temperature from HA with the API to ESP, but I haven't dealt with that yet.
https://esphome.io/components/api.html
https://esphome.io/components/sensor/homeassistant.html
but the problem is then when the connection to HA is lost
in your case, i would probably rather replace the DHT with a Dalls DS18b20, which I would screw directly to the heatsink.
maybe with something like this: https://aixontec.com/Cable-fastening-clamp-according-to-DIN-72573-two-layer-galvanized-steel-5-mm-10PACK
for a slightly better reading, the sensor is also available without stainless steel housing.
the control runs through the ESP and with Dallas temperature sensor and HA only gives a warning when the temperature exceeds a certain value.
maybe also add a buzzer to the ESP and give an alarm in case of an error.
https://esphome.io/components/rtttl.html
Q1:
i don't know.
Most excellent! I have now hot both inverters with three fans each running. :-)
I have the DHT still in place as a backup but the cooling logic is running off the internal inverter temperature for now...I will look for a place to mount a thermocouple on the inverter but from what I can see without taking if off the wall there's not too many easy options...dangling it in space as with the DHT will not be as effective.
RPMs worked a treat too! A fun little project.
At present using KP of 0.1, KD and KI are both zero. Inverter heat is a bit chaotic as sun condition can cause wild fluctuations in generation and therefore heat output. So KP does a simple job...may add a little KD atcsomecstagecwhen I have more data. Initial testing shows the fans will knock 20-30°C off the running temperature.
Thanks for your help and inspiration! Got me well and truly underway...
CP.
can you show the code on how to get the temperature from the HA to the ESP?
Sure thing! Code below. :-)
I do have a small section to define the "effective temperature" which is basically a 'best-of' the inverter internal or DHT temperature....that part needs a little more work when I get some more time. The external air temperature lags the internal temperature and is much lower - so I will need to fudge that (e.g. air temp + 20C to 'estimate' internal temperature) as I cannot drive the inverter set point temperature (based on internal temp) against the air temperature as the air is so much cooler. Either that or I need to create a second 'mode' - cool against internal temp, OR, cool against air temp. As it is just a backup in case HA goes away and I lose the internal temp data the first approach (+20C) seems easier.....but I've parked it for now until the weekend as I have some other things to do. :-)
substitutions:
devicename: esp32-inverter-cooling
friendly_name: Inverter Fan Control
esphome:
name: $devicename
globals:
- id: dhttemp
type: float
restore_value: yes
initial_value: '0'
#########################
# ESP32 AND NETWORK SETUP
esp32:
board: esp32dev
framework:
type: arduino
# pid climate log update is noisy, dial it back to warn
logger:
level: DEBUG
logs:
climate: ERROR
dht: WARN
# default HA integration, OTA updater and backup http web portal
api:
ota:
captive_portal:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.20.92
gateway: 192.168.20.1
subnet: 255.255.255.0
dns1: 192.168.1.6
number:
## RECEIVE kp,ki and kd parameters from input_text.kx helpers in
# Home Assistant. See the PID controller below
# These helper values will get saved to flash thus permanently over-riding
# the initial values set in the PID below.
#Inverter PID Values
# KP
- platform: template
name: $friendly_name KP
icon: mdi:chart-bell-curve
restore_value: true
min_value: 0
max_value: 50
step: 0.001
set_action:
lambda: |-
// ESP_LOGI("main", "!!!!!! kp from boot %d", id("n_w_inverter_fan_kp") );
// id(n_w_inverter_thermostat).set_kp( id("$friendly_name Kp") );
id(n_w_inverter_thermostat).set_kp( x );
// ESP_LOGI("main", "!!!!!! kp from boot %d", id("n_e_inverter_fan_kp") );
// id(n_e_inverter_thermostat).set_kp( id("$friendly_name Kp") );
id(n_e_inverter_thermostat).set_kp( x );
# KI
- platform: template
name: $friendly_name KI
icon: mdi:chart-bell-curve
restore_value: true
min_value: 0
max_value: 50
step: 0.0001
set_action:
lambda: id(n_w_inverter_thermostat).set_ki( x );
id(n_e_inverter_thermostat).set_ki( x );
# KD
- platform: template
name: $friendly_name KD
icon: mdi:chart-bell-curve
restore_value: true
min_value: -50
max_value: 50
step: 0.001
set_action:
lambda: id(n_w_inverter_thermostat).set_kd( x );
id(n_e_inverter_thermostat).set_kd( x );
text_sensor:
# Send IP Address
- platform: wifi_info
ip_address:
name: $friendly_name IP Address
# Send Uptime in raw seconds
- platform: template
name: $friendly_name Uptime
id: uptime_human
icon: mdi:clock-start
sensor:
# Send WiFi signal strength & uptime to HA
- platform: wifi_signal
name: $friendly_name WiFi Strength
update_interval: 60s
# Human readable uptime string
- platform: uptime
name: $friendly_name Uptime
id: uptime_sensor
update_interval: 60s
on_raw_value:
then:
- text_sensor.template.publish:
id: uptime_human
# Custom C++ code to generate the result
state: !lambda |-
int seconds = round(id(uptime_sensor).raw_state);
int days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600);
int hours = seconds / 3600;
seconds = seconds % 3600;
int minutes = seconds / 60;
seconds = seconds % 60;
return (
(days ? to_string(days) + "d " : "") +
(hours ? to_string(hours) + "h " : "") +
(minutes ? to_string(minutes) + "m " : "") +
(to_string(seconds) + "s")
).c_str();
# Pull in temperatures from HA
- platform: homeassistant
name: "N/W Inverter Internal Temperature"
id: n_w_inverter_internal_temperature
entity_id: sensor.solis_n_w_inverter_temperature
- platform: homeassistant
name: "N/E Inverter Internal Temperature"
id: n_e_inverter_internal_temperature
entity_id: sensor.solis_n_e_inverter_temperature
#Pull in DC power from HA
- platform: homeassistant
name: "N/W Inverter DC Power"
id: n_w_inverter_dc_power
entity_id: sensor.solis_n_w_inverter_dc_power
- platform: homeassistant
name: "N/E Inverter DC Power"
id: n_e_inverter_dc_power
entity_id: sensor.solis_n_e_inverter_dc_power
#######################################################################################################
# Fan Monitoring
- platform: pulse_counter
pin:
number: 18
mode: INPUT_PULLUP
id: nw_fan_1_speed
name: "NW Fan 1 Speed"
update_interval: 5s
filters:
- multiply: 0.5
accuracy_decimals: 0
- platform: pulse_counter
pin:
number: 19
mode: INPUT_PULLUP
id: nw_fan_2_speed
name: "NW Fan 2 Speed"
update_interval: 5s
filters:
- multiply: 0.5
accuracy_decimals: 0
- platform: pulse_counter
pin:
number: 21
mode: INPUT_PULLUP
id: nw_fan_3_speed
name: "NW Fan 3 Speed"
update_interval: 5s
filters:
- multiply: 0.5
accuracy_decimals: 0
- platform: pulse_counter
pin:
number: 22
mode: INPUT_PULLUP
id: ne_fan_1_speed
name: "NE Fan 1 Speed"
update_interval: 5s
filters:
- multiply: 0.5
accuracy_decimals: 0
- platform: pulse_counter
pin:
number: 23
mode: INPUT_PULLUP
id: ne_fan_2_speed
name: "NE Fan 2 Speed"
update_interval: 5s
filters:
- multiply: 0.5
accuracy_decimals: 0
- platform: pulse_counter
pin:
number: 25
mode: INPUT_PULLUP
id: ne_fan_3_speed
name: "NE Fan 3 Speed"
update_interval: 5s
filters:
- multiply: 0.5
accuracy_decimals: 0
#######################################################################################################
# FAN CONTROLLER SETUP
# N/W Inverter
- platform: template
name: $friendly_name N/W p term
id: pid_nw_p_term
unit_of_measurement: "%"
accuracy_decimals: 2
- platform: template
name: $friendly_name N/W i term
id: pid_nw_i_term
unit_of_measurement: "%"
accuracy_decimals: 2
- platform: template
name: $friendly_name N/W d term
id: pid_nw_d_term
unit_of_measurement: "%"
accuracy_decimals: 2
- platform: template
name: $friendly_name N/W output value
unit_of_measurement: "%"
id: pid_nw_o_term
accuracy_decimals: 2
- platform: template
name: $friendly_name N/W error value
id: pid_nw_e_term
accuracy_decimals: 2
- platform: template
name: $friendly_name N/W zero
id: pid_nw_zero_value
update_interval: 60s
lambda: |-
return 0;
- platform: template
name: $friendly_name N/W zero percent
unit_of_measurement: "%"
id: pid_nw_zero_value_percent
update_interval: 60s
lambda: |-
return 0;
# N/E Inverter
- platform: template
name: $friendly_name N/E p term
id: pid_ne_p_term
unit_of_measurement: "%"
accuracy_decimals: 2
- platform: template
name: $friendly_name N/E i term
id: pid_ne_i_term
unit_of_measurement: "%"
accuracy_decimals: 2
- platform: template
name: $friendly_name N/E d term
id: pid_ne_d_term
unit_of_measurement: "%"
accuracy_decimals: 2
- platform: template
name: $friendly_name N/E output value
unit_of_measurement: "%"
id: pid_ne_o_term
accuracy_decimals: 2
- platform: template
name: $friendly_name N/E error value
id: pid_ne_e_term
accuracy_decimals: 2
- platform: template
name: $friendly_name N/E zero
id: pid_ne_zero_value
update_interval: 60s
lambda: |-
return 0;
- platform: template
name: $friendly_name N/E zero percent
unit_of_measurement: "%"
id: pid_ne_zero_value_percent
update_interval: 60s
lambda: |-
return 0;
#######################################################################################################
# GET TEMP/HUMIDITY FROM DHT22 --> N/W
- platform: dht
pin: GPIO13
temperature:
name: "N/W Inverter Temperature (DHT)"
id: n_w_inverter_dht_temperature
accuracy_decimals: 1
#Smooth the readings to stop jumpy control
filters:
- exponential_moving_average:
alpha: 0.1
send_every: 5
update_interval: 1.5s
# GET TEMP/HUMIDITY FROM DHT22 --> N/E
- platform: dht
pin: GPIO14
temperature:
name: "N/E Inverter Temperature (DHT)"
id: n_e_inverter_dht_temperature
accuracy_decimals: 1
#Smooth the readings to stop jumpy control
filters:
- exponential_moving_average:
alpha: 0.1
send_every: 5
update_interval: 1.5s
#######################################################################################################
# Set the temperature value to use (internal or DHT temperature)
# N/W Inverter
- platform: template
name: "N/W Inverter Temperature (Effective)"
id: n_w_inverter_temperature_effective
lambda: 'return (id(n_w_inverter_internal_temperature).has_state() ? id(n_w_inverter_internal_temperature).state : id(n_w_inverter_dht_temperature).state);'
update_interval: 60s
# N/E Inverter
- platform: template
name: "N/E Inverter Temperature (Effective)"
id: n_e_inverter_temperature_effective
lambda: 'return (id(n_e_inverter_internal_temperature).has_state() ? id(n_e_inverter_internal_temperature).state : id(n_e_inverter_dht_temperature).state);'
update_interval: 60s
#######################################################################################################
# Take the "COOL" value of the pid and send
# it to the frontend to graph the output voltage
- platform: pid
name: "N/W Inverter Fan Speed (PWM_V)"
climate_id: n_w_inverter_thermostat
accuracy_decimals: 0
type: COOL
- platform: pid
name: "N/E Inverter Fan Speed (PWM_V)"
climate_id: n_e_inverter_thermostat
accuracy_decimals: 0
type: COOL
#######################################################################################################
output:
# Wire this pin into the PWM pin the 12v fan
# ledc is the name of the pwm output system on an esp32
# N/W Inverter
- platform: ledc
id: n_w_inverter_fan_speed
pin: 16
# 25KHz is standard PC fan frequency, minimises buzzing
frequency: "25000 Hz"
# The fans stop working below 12% signal (at 13% one will still try).
min_power: 11%
max_power: 100%
# N/E Inverter
- platform: ledc
id: n_e_inverter_fan_speed
pin: 17
# 25KHz is standard PC fan frequency, minimises buzzing
frequency: "25000 Hz"
# The fans stop working below 13% signal.
min_power: 13%
max_power: 100%
#######################################################################################################
# Expose a PID-controlled Thermostat
# Manual: https://esphome.io/components/climate/pid.html
climate:
# N/W Inverter
- platform: pid
name: "N/W Inverter Thermostat"
id: n_w_inverter_thermostat
sensor: n_w_inverter_temperature_effective
# 30c is a decent target as the inverter will be about 40C on the inside at that point.
default_target_temperature: 30°C
cool_output: n_w_inverter_fan_speed
on_state:
- sensor.template.publish:
id: pid_nw_p_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_proportional_term() * 100.0;'
- sensor.template.publish:
id: pid_nw_i_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_integral_term()* 100.0;'
- sensor.template.publish:
id: pid_nw_d_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_derivative_term()* 100.0;'
- sensor.template.publish:
id: pid_nw_o_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_output_value()* 100.0;'
- sensor.template.publish:
id: pid_nw_e_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_error_value();'
# The extents of the HA Thermostat
visual:
min_temperature: 20 °C
max_temperature: 70 °C
# See the README for setting up these parameters.
# These are over ridden by the number templates above.
control_parameters:
kp: 0.1
ki: 0.0
kd: 0.0
max_integral: 0.0
#----------------------------------------------------------------------------------------------------
# N/E Inverter
- platform: pid
name: "N/E Inverter Thermostat"
id: n_e_inverter_thermostat
sensor: n_e_inverter_temperature_effective
# 30c is a decent target as the inverter will be about 40C on the inside at that point.
default_target_temperature: 30°C
cool_output: n_e_inverter_fan_speed
on_state:
- sensor.template.publish:
id: pid_ne_p_term
state: !lambda 'return -id(n_e_inverter_thermostat).get_proportional_term() * 100.0;'
- sensor.template.publish:
id: pid_ne_i_term
state: !lambda 'return -id(n_e_inverter_thermostat).get_integral_term()* 100.0;'
- sensor.template.publish:
id: pid_ne_d_term
state: !lambda 'return -id(n_e_inverter_thermostat).get_derivative_term()* 100.0;'
- sensor.template.publish:
id: pid_ne_o_term
state: !lambda 'return -id(n_e_inverter_thermostat).get_output_value()* 100.0;'
- sensor.template.publish:
id: pid_ne_e_term
state: !lambda 'return -id(n_e_inverter_thermostat).get_error_value();'
# The extents of the HA Thermostat
visual:
min_temperature: 20 °C
max_temperature: 70 °C
# See the README for setting up these parameters.
# These are over ridden by the number templates above.
control_parameters:
kp: 0.1
ki: 0.0
kd: 0.0
max_integral: 0.0
#######################################################################################################
# Manual Fan Control.
fan:
# N/W Fans
- platform: speed
output: n_w_inverter_fan_speed
name: "N/W Inverter Fan Speed"
# N/E Fans
- platform: speed
output: n_e_inverter_fan_speed
name: "N/E Inverter Fan Speed"
#######################################################################################################
# Expose an ESP32 restart button to HA
switch:
- platform: restart
name: "Inverter Cooling ESP32 Restart"
a suggestion.
the temperature at the DHT is surely lower than the internal temperature. So if it waits for 30 degrees at the DHT, the internal temperature could already be too high. Maybe add an offset to the DHT when selecting the effective temperature.
I have just looked at the data (boring work meeting) and have added "offset: 15" to the DHT configuration....seems to be working nicely and reasonably aligned with he internal temperature. ;-)
why can't you put the sensor inside the inverter? Why does the inverter not have its own cooling? what is the motivation behind / problem this project?
Question 1: - The main issue with inverter cooling is that I need a responsive cooling system but one that is skewed - I need a fast(ish) response and the long slow tail....is this where kd comes in?
Crank up the KP it'll respond quickly, but it might oscillate the power up and down until it finds equilibrium. Try PID Autotune, it might work for you if it is a fast response system.
Question 2:
I have a DHT dangling over the back of the inverter radiator to read the air temperature, but really I need something better to read the radiator fin temperature directly, or a way of pushing the inverter internal temperature (which HA knows) to the fan controller...but with the option to still use DHT incase HA fails for some reason. That is, use inverter internal temp reading and if null revert to DHT temperature. Is this possible? if so how the heck could I pull that off?
Attach the DHT directly to the fin? I wouldn't try to get the reading from HA into your ESP32 because then your cooling is dependent on all of the systems being connected together. But if you do want to do that, just expose a number template to HA and then with some HA automation you could one value to the other.
Question 3:
I'd really like to bring in the tachometer data so that I can detect fan failure. I can do all the logic in HA/NodeRed, just need to plumb up the pins and get a reading....any tips? I've seen a few ways of doing it with resistors, logic level shifters, capacitors etc....but it seems a little complicated? From the looks of things I have exactly the same fans as you... :-)
I like @DunklesKaltesNichts 's suggestion, just wire the tach directly to another pin. I'd be keen to hear how you get on with it.
BTW those fan mounts like amazing. well done. space age.
So the inverter has a temperature sensor and I use MODBUS to read this out and give it to HA. This i can pass back to the esp32 so it can run to this temperature as it's the best data I can get from the heart of the machine. The inverter doesn't have it's own active cooling system - it has a passive radiator/ heatsink on the back (between the inverter and the wall) and it relies on passive air movement and the draft that's created as the hot air rises. During peak production the temperature of the inverter went to over 70C which is bad for efficiency and not good for the life of the machine. So adding some fans to draw air up and out makes the heatsink super effective - the max temperature now is about 42C....30C less. :-) I can't put a probe inside the inverter (void warranty, plus it's all solid state electronics so don't want to mess with it) and there's not really much room to neatly attach a probe to the heat sink either....but I can dangle a DHT over the heatsink and measure the air temperature from the rising warm air. There's a max of about a 15-18C temperature difference between core temperature and air temperature when the fan is running. I want this because if HA goes away (dead, crashed while I'm on holiday, being rebuilt, etc) I still want the controlled cooling - there'd be no HA-based data feed of the internal temperature, but I can still get the DHT temperature locally and I can run and control the fans with that... Meaning the fans will stop when it's cool and work harder when it's hot (versus not running at all or running at full speed). I use the fan rpm data (or will when I get there) to detect fan or 12v PSU failure, and if my ESP32 or 5v dies the fans will run at 100% (no PWM) but HA can tell me it's gone offline (or, again, it will when I get there). Make sense?
…
On Tue, 31 Jan 2023, 16:33 Patrick Collins, @.> wrote: why can't you put the sensor inside the inverter? Why does the inverter not have its own cooling? what is the motivation behind / problem this project? — Reply to this email directly, view it on GitHub <#10 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AUIP7ZXQCZ5BKEWSMZVHPCTWVCBXNANCNFSM6AAAAAAUFUNCV4 . You are receiving this because you authored the thread.Message ID: @.>
Yep, makes sense! You definitely want this running independently of HA. Checkout the new deadband features I added to PID Climate.
So the inverter has a temperature sensor and I use MODBUS to read this out and give it to HA
esphome can also read modbus. do i read my solar charge controllers and send the data to HA.
https://esphome.io/components/modbus_controller.html
tere is an example of this in the cookbook.
So I did actually try to apply some dead band config to my YAML (based on what was here: https://esphome.io/components/climate/pid.html) but I couldn't get the config to save and deploy - it kept telling me that the config wasn't valid (e.g. 'dead band' is not a valid tag in this section). I wanted to do this to flatten the fan response so it ramps up quickly and but doesn't back off so fast by making a lopsided dead band range. But had to give up as it just wouldn't work. Have you got an example?
I know that ESPHome can do some MODBUS, but my inverter will only permit one connection at a time - so I've kicked it off the Internet by applying a blocking rule on the FW to that there is no "phone home" connection, and now I talk to it via HA....and all the data I get from that is useful so the loopback of the temperature back to the ESP is still the easiest. The DHT-based temperature (fudged or otherwise) isn't ideal but it's only a backup....and sometimes near enough is good enough....
Can you post the YAML you were using to try and implement deadband?
So I copied the example from the link above:
# Example configuration entry
climate:
- platform: pid
name: "PID Climate Controller"
sensor: temperature_sensor
default_target_temperature: 21°C
heat_output: heater
control_parameters:
kp: 0.49460
ki: 0.00487
kd: 12.56301
output_averaging_samples: 5 # smooth the output over 5 samples
derivative_averaging_samples: 5 # smooth the derivative value over 10 samples
deadband_parameters:
threshold_high: 0.5°C # deadband within +/-0.5°C of target_temperature
threshold_low: -0.5°C
Making my section look like this:
climate:
# N/W Inverter
- platform: pid
name: "N/W Inverter Thermostat"
id: n_w_inverter_thermostat
sensor: n_w_inverter_temperature_effective
# 35C is a decent target!
default_target_temperature: 35°C
cool_output: n_w_inverter_fan_speed
on_state:
- sensor.template.publish:
id: pid_nw_p_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_proportional_term() * 100.0;'
- sensor.template.publish:
id: pid_nw_i_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_integral_term()* 100.0;'
- sensor.template.publish:
id: pid_nw_d_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_derivative_term()* 100.0;'
- sensor.template.publish:
id: pid_nw_o_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_output_value()* 100.0;'
- sensor.template.publish:
id: pid_nw_e_term
state: !lambda 'return -id(n_w_inverter_thermostat).get_error_value();'
# The extents of the HA Thermostat
visual:
min_temperature: 25 °C
max_temperature: 50 °C
# See the README for setting up these parameters.
# These are over ridden by the number templates above.
control_parameters:
kp: 0.1
ki: 0.0
kd: 0.0
max_integral: 0.0
deadband_parameters:
threshold_high: 0.5°C # deadband within +/-0.5°C of target_temperature
threshold_low: -1.0°C
It gives the error:
[deadband_parameters] is an invalid option for [climate.pid]. Did you mean [control_parameters]?
But if I move under control_parameters it says:
[deadband_parameters] is an invalid option for [control_parameters]. Please check the indentation.
So I got nowhere fast....I try re-organising the block by moving the on_state: section to the bottom of the pid definition etc etc but all I go was errors. I simply couldn't find a way to swallow the config..... sometime I could get past the editor error, but then the compile would fail...so something somewhere isn't quite right....
Aha, you don't have the latest version of esphome.
pip3 install -U esphome