斗禾便携取暖器改智能取暖器接入Homeassistant
几年前买了个便携取暖器,正常使用方式应该是放在桌面使用,我是坐在电脑桌前使用电脑时放地上烘脚,就造成开关不方便,而且经常忘记关,浪费电费,改智能取暖器的想法早就有,esp8266的板子也已经买回来扔角落两年多,拖延症一直没动,最近折腾劲又起来了趁机搞好了。

### 一、方案选择和电路焊接
取暖器上有四个按键分别是开关、挡位、自然风、摇头,点按方式,非开关式。

上面面板式卡扣式,拆开上面面板后可以看到控制电路板,每个按键有四个脚,上排两个脚位电压为5v,下排两个脚位为接地,点按式接地拉低电平为0v,智能化改造就是要通过软件控制电路模拟点按按键,改造方式有多种方式,可以使用专门的风扇智能化干接点模块、继电器模块,我这里选择使用esp8266模块,主要便宜、体积小,风扇智能化模块找了一下都要三四十以上,esp8266才十几块。

现在控制焊接点知道了,只要把esp8266的四个控制脚分别接到按键的5v脚进行控制,另外还要找到给esp8266使用的合适取电点位。esp8266使用5v电源,通过测量、分析,在电路板背面主控芯片旁有给芯片供电的5v电源和接地的测试点,就是下图桔色线焊接处电压5v,桔白色线焊接处为接地,线需要6条比较多,四根控制线和两根电源线,直接用的网线,粗细刚好,另外蓝、蓝白、绿、绿白四根线分别接四个按键的5v脚。

现在取暖器端的线接好了,esp8266端因为其控制脚电压都是3.3v,不能直接接,需要电压转换,我选择用下面这种支持3.3v和5v双向互转的电平转换模块,一片不到一块钱,便宜好用,选择最右边的四路模块,模块3.3v端电源和接地分别从esp8266 3.3v输出脚和接地脚接,5v端电源从esp8266的5v供电并联接入,四个控制脚和取暖器四个按键的接线通过转换模块一一对应连接即可。


以下是电路连接示意图:

esp8266端接线:

线路接好后把esp8266模块塞到底座凹槽里:

### 二、esphome代码
借助AI写的代码,使用的homeassistant的按钮组件,而不是用开关,因为按钮没有状态,开关有状态,这样代码会比较简单,四个控制脚都同时作为输入和输出,输入状态用于感知点按取暖器硬件按键,esphome代码会生成四个按钮和四个传感器,传感器用于存储设备状态,方便与点按取暖器按键时同步机器状态,代码如下:
esphome:
name: heater-controller
name_add_mac_suffix: false
project:
name: "esphome.heater-controller"
version: "1.0"
# 新版ESP8266平台配置
esp8266:
board: nodemcuv2
# Enable logging
logger:
# Enable Home Assistant API
api:
# WiFi配置
wifi:
ssid: "xxxxxx"
password: "xxxxxx"
ap:
ssid: "Heater Fallback Hotspot"
password: "xxxxxx"
# OTA配置
ota:
platform: esphome
# ==================== 全局变量:存储设备状态 ====================
globals:
- id: power_state
type: bool
restore_value: false
initial_value: "false" # 初始关机状态
- id: gear_level
type: int
restore_value: false
initial_value: "0" # 0=低档 1=高档
- id: natural_wind_state
type: bool
restore_value: false
initial_value: "false" # 自然风关闭
- id: swing_state
type: bool
restore_value: false
initial_value: "false" # 摇摆关闭
# ==================== 状态传感器:可视化设备状态 ====================
# 1. 电源状态传感器
text_sensor:
- platform: template
name: "Heater Power Status"
id: power_status_sensor
update_interval: 500ms
lambda: |-
if (id(power_state)) {
return {"ON"};
} else {
return {"OFF"};
}
# 2. 工作模式传感器(核心状态展示)
- platform: template
name: "Heater Work Mode"
id: work_mode_sensor
update_interval: 500ms
lambda: |-
if (!id(power_state)) {
return {"SHUTDOWN"};
} else if (id(natural_wind_state)) {
return {"NATURAL WIND"};
} else if (id(gear_level) == 1) {
return {"HIGH GEAR"};
} else {
return {"LOW GEAR"};
}
# 3. 挡位数值传感器
sensor:
- platform: template
name: "Heater Gear Level"
id: gear_level_sensor
unit_of_measurement: "档"
accuracy_decimals: 0
update_interval: 500ms
lambda: |-
return id(gear_level);
# 4. 摇摆状态传感器(0=关 1=开)
- platform: template
name: "Heater Swing Status"
id: swing_status_sensor
unit_of_measurement: "State"
accuracy_decimals: 0
update_interval: 500ms
lambda: |-
return id(swing_state) ? 1 : 0;
# ==================== 二进制传感器:检测物理按键 ====================
binary_sensor:
# 物理电源键(D1)
- platform: gpio
pin:
number: D1
mode: INPUT_PULLUP
inverted: false # 按下拉低=触发
id: physical_power_btn
internal: false
on_press:
- lambda: |-
// 切换电源状态
id(power_state) = !id(power_state);
if (!id(power_state)) {
// 关机:复位所有状态
id(gear_level) = 0;
id(natural_wind_state) = false;
id(swing_state) = false;
} else {
// 开机:默认低档
id(gear_level) = 0;
}
# 物理挡位键(D2)
- platform: gpio
pin:
number: D2
mode: INPUT_PULLUP
inverted: false
id: physical_gear_btn
internal: false
on_press:
- if:
condition:
lambda: |-
return id(power_state);
then:
- lambda: |-
// 切换挡位
if (id(natural_wind_state)) {
id(natural_wind_state) = false;
id(gear_level) = 0;
} else {
id(gear_level) = !id(gear_level);
}
# 物理自然风键(D5)
- platform: gpio
pin:
number: D5
mode: INPUT_PULLUP
inverted: false
id: physical_natural_wind_btn
internal: false
on_press:
- if:
condition:
lambda: |-
return id(power_state); // 仅开机生效
then:
- lambda: |-
// 切换自然风状态 + 互斥关闭挡位
id(natural_wind_state) = true;
id(gear_level) = 0; // 自然风开→强制低档
# 物理摇摆键(D6)
- platform: gpio
pin:
number: D6
mode: INPUT_PULLUP
inverted: false
id: physical_swing_btn
internal: false
on_press:
- if:
condition:
lambda: |-
return id(power_state); // 仅开机生效
then:
- lambda: |-
// 切换摇摆状态
id(swing_state) = !id(swing_state);
# ==================== Button模板:智能点按控制 ====================
button:
# 1. 电源按键(点按开关机)
- platform: template
name: "Heater Power Button"
id: power_btn
on_press:
- lambda: |-
if (id(power_state)) {
// 当前开机→点按关机
id(power_state) = false;
id(gear_level) = 0;
id(natural_wind_state) = false;
id(swing_state) = false;
} else {
// 当前关机→点按开机(默认低档)
id(power_state) = true;
id(gear_level) = 0;
}
// 模拟物理按键点按
pinMode(D1, OUTPUT);
digitalWrite(D1, LOW);
delay(10);
digitalWrite(D1, HIGH);
pinMode(D1, INPUT_PULLUP);
# 2. 挡位按键(点按切换高低档,区分自然风状态执行不同逻辑)
- platform: template
name: "Heater Gear Button"
id: gear_btn
on_press:
- if:
condition:
lambda: |-
return id(power_state); // 仅开机状态生效
then:
# 嵌套if:判断自然风状态
- if:
condition:
lambda: |-
return id(natural_wind_state); // 自然风开启状态
then:
- lambda: |-
// 1. 仅修改自然风状态为关闭,不操作自然风脚位
id(natural_wind_state) = false;
// 2. 强制挡位为低档
id(gear_level) = 0;
// 3. 操作挡位脚位,切换到低档
pinMode(D2, OUTPUT);
digitalWrite(D2, LOW);
delay(10);
digitalWrite(D2, HIGH);
pinMode(D2, INPUT_PULLUP);
else:
- lambda: |-
// 1. 切换挡位状态(0→1 / 1→0)
id(gear_level) = !id(gear_level);
// 2. 操作挡位脚位,执行切换
pinMode(D2, OUTPUT);
digitalWrite(D2, LOW);
delay(10);
digitalWrite(D2, HIGH);
pinMode(D2, INPUT_PULLUP);
# 3. 自然风按键(点按开关,开启时强制挡位为低档)
- platform: template
name: "Heater Natural Wind Button"
id: natural_wind_btn
on_press:
- if:
condition:
lambda: |-
return id(power_state); // 仅开机生效
then:
- lambda: |-
// 1. 切换自然风状态
id(natural_wind_state) = true;
// 2. 只有自然风开启时,才操作脚位 + 强制低档
if(id(natural_wind_state)) {
// 控制自然风脚位,开启自然风
pinMode(D5, OUTPUT);
digitalWrite(D5, LOW);
delay(10);
digitalWrite(D5, HIGH);
pinMode(D5, INPUT_PULLUP);
// 强制挡位为低档
id(gear_level) = 0;
}
# 4. 摇摆按键(点按开关)
- platform: template
name: "Heater Swing Button"
id: swing_btn
on_press:
- if:
condition:
lambda: |-
return id(power_state); // 仅开机生效
then:
- lambda: |-
// 切换摇摆状态
id(swing_state) = !id(swing_state);
// 模拟按键点按
pinMode(D6, OUTPUT);
digitalWrite(D6, LOW);
delay(10);
digitalWrite(D6, HIGH);
pinMode(D6, INPUT_PULLUP);
homeassistant系统设备、实体:

上面取暖器开关是关联用模板风扇创建的实体:

平常只用开关机和挡位,自然风和摇头不用,取暖器控制面板就设置了三个挡位用于控制:

设置模板风扇时卡在风速设置这部分,一直无法设置成功,研究了两个晚上才弄好,设置为三个挡位:高档、低档、关闭,百分比设置代码如下:
{% if states('sensor.heater_controller_heater_power_status') != 'ON' %}
0
{% elif states('sensor.heater_controller_heater_gear_level') | int == 0 %}
50
{% else %}
100
{% endif %}
设置百分比时的动作设置如下:
choose:
- conditions:
- condition: template
value_template: "{{ percentage == 0 }}"
sequence:
- if:
- condition: state
entity_id: sensor.heater_controller_heater_power_status
state:
- "ON"
then:
- action: button.press
data: {}
metadata: {}
target:
entity_id: button.heater_controller_heater_power_button
- conditions:
- condition: template
value_template: "{{ percentage == 50 }}"
sequence:
- else:
- if:
- condition: state
entity_id: sensor.heater_controller_heater_gear_level
state:
- "1.0"
then:
- action: button.press
data: {}
metadata: {}
target:
entity_id: button.heater_controller_heater_gear_button
if:
- condition: state
entity_id: sensor.heater_controller_heater_power_status
state:
- "OFF"
then:
- action: button.press
data: {}
metadata: {}
target:
entity_id: button.heater_controller_heater_power_button
- conditions:
- condition: template
value_template: "{{ percentage == 100 }}"
sequence:
- else:
- if:
- condition: state
entity_id: sensor.heater_controller_heater_gear_level
state:
- "0.0"
then:
- action: button.press
data: {}
metadata: {}
target:
entity_id: button.heater_controller_heater_gear_button
if:
- condition: state
entity_id: sensor.heater_controller_heater_power_status
state:
- "OFF"
then:
- action: button.press
data: {}
metadata: {}
target:
entity_id: button.heater_controller_heater_power_button
- action: button.press
data: {}
metadata: {}
target:
entity_id: button.heater_controller_heater_gear_button
这里要注意传感器的参数值字符串是区分大小写的,数值是区分整数、浮点数的,UI界面显示0和1,但实际值是0.0和1.0,可以到开发者工具--状态部分获取实体信息,查看这些参数值的真实情况,因为这个一直设置不成功,折腾了两个晚上。
速度计数填:2,至此完成整个改造,现在可以开始玩一些自动化设置了,比如打开取暖器时根据室温自动切换高低档、无人在房间时自动关机、电脑开机时根据室温自动打开取暖器等等,依据个人使用情况折腾。
作者声明本文无利益相关,欢迎值友理性交流,和谐讨论~
