Esphome / Hardware / IOT / SmartHome · 2022-04-16 0

HeimVision A80S唤醒灯接入Home Assistant

一.关于HeimVision A80S

HeimVision是一个功能还算比较丰富的唤醒灯。唤醒灯作为夜灯的亮度也相当不错。自带收音机。

主要功能如下:

  • 唤醒灯 20档亮度
  • 2个闹钟
  • 收音机
  • 时钟 LED
  • WIFI

二.改造背景

A80S官方没有app。IOT功能使用的是中国厂商TuyaSmart的方案。可以使用TuyaSmart和Smart Life这两个app来控制各项功能。我家里的所有智能家居设备都是通过自建的HomeAssistant来控制的。虽然Hass也支持Tuya接入。但是支持的比较残废。一直在尝试有无更好的远程控制方案。

三.改造思路

通过拆解A80S发现其使用的Wi-Fi模块是这样的。

这个TYWE3S呢,是Tuya的IOT方案模块。查阅了一下厂商接入Tuya的方式。如下图所示。♑

MCU 低代码开发

这是IOT行业的一种LowCode的方案。智能设备厂商只需对自己已有的产品稍加改造。按着Tuya Module的协议通过UART接受TuyaModule的控制即可。在Tuya的后台配置好控制模型。设备接入Wi-Fi和用户端app都是用Tuya的生态。

也就是说上门的TYWE30S里面运行的应该是Tuya的代码,复杂联网和向MCU发送控制指令。

这颗TYWE3S其实就是一颗ESP8266。

这样就简单了。ESP8266可以直接用ESPHome重新注入灵魂。通过UART来控制这颗MCU就好了。

ESPHome,是ESP系列模组爱好者发起的一个开源项目。只需写一写Yaml文件就可以自动生成ESP系列芯片的固件。而且还集成了自动更新固件和web Server等等方便的功能。ESPHome也提供API直接接入到HASS。

翻阅ESPHome文档的时候发现,ESPHome已经支持TuyaMCU了。

那就更简单了。

四.改造过程

1. ESP8266刷Flash线路

这个步骤也可以使用 Tuya Convert 这个项目来实现。主要优点是免拆机,利用tuya 模块漏洞来实现固件替换。但是对当前固件版本有要求。

ESP8266的引脚定义如下。

Full pinout

根据芯片手册,通过UART写入Flash需要在Boot时把GPIO0接地。设计如下线路图

需要准备一条USB to TTL的数据线把ESP8266和电脑连接起来。

3.编译固件替换

https://esphome.io/

先写个最简单的yaml文件,保存为esp_light.yaml,目的是让esphome连接我们的Wi-Fi路由器。之后再更新固件就可以通过Wi-Fi了。而不连接线路。

esphome:
  name: esp_light
  platform: ESP8266
  board: esp12e
  platformio_options:
    board_build.ldscript: eagle.flash.2m.ld

wifi:
  ssid: "YOUR_WIFI_SSID"
  password: "YOU_WIFI_PASSWORD"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Fallback Hotspot"
    password: "RANDOM"

# 这个功能就是通过Wi-Fi更新固件
ota:
  password: "YOUR_OTA_PASS"

安装esphome

pip3 install -U esphome

编译固件

esphome run esp_light.yaml

这个命令执行过程中,esphome会根据你的yaml定义自动生成代码,并且编译好固件。固件编译OK后。后自动开始写入固件,固件写入开始的时候需要重启一下ESP8266。这时候按下GPIO0上的button。点按一下rest线上的button。顺利的话固件就写入成功了。

写入成功后设备会自动重启。这时候可以拔掉所有线路,把A80S安装好。之后刷写固件通过WI-FI就OK了。还是同样的命令。

4.ESPhome实现A80S的各项功能

yaml文件添加如下段落

uart:
  rx_pin: GPIO3
  tx_pin: GPIO1
  baud_rate: 9600

time:
  - platform: sntp
    timezone: "Asia/Tokyo"
    id: sntp_time

tuya: # 开启tuyaMCU功能
  id: light_tuya
  time_id: sntp_time
  on_datapoint_update:
    - sensor_datapoint: 107
      datapoint_type: raw
      then:
        - lambda: |-
            ESP_LOGD("tuya", "on_datapoint_update %s", hexencode(x).c_str());

switch:
  - platform: "tuya"
    name: "Radio"
    icon: mdi:radio
    switch_datapoint: 105
  - platform: "tuya"
    name: "Alarm Clock 1"
    icon: mdi:alarm
    switch_datapoint: 109
  - platform: "tuya"
    icon: mdi:alarm
    name: "Alarm Clock 2"
    switch_datapoint: 122

light:
  - platform: "tuya"
    name: "Backgrand Light"
    dimmer_datapoint: 104
    min_value: 1
    max_value: 2
  - platform: "tuya"
    name: "Breathing Light"
    switch_datapoint: 101
    dimmer_datapoint: 102
    min_value: 20
    max_value: 1
  - platform: "tuya"
    name: "Wake Light"
    switch_datapoint: 121

text_sensor:
  - platform: "tuya"
    id: Radio_channel_name
    name: "Radio Channel"
    sensor_datapoint: 107

number: # 收音机音量调节
  - platform: "tuya"
    name: "Radio Volumn"
    icon: "mdi:volumn-medium"
    number_datapoint: 106
    min_value: 1
    max_value: 12
    step: 1

button:
  - platform: template    # 实现频道切换按钮
    name: "Radio Channel +" 
    icon: "mdi:access-point-plus"
    on_press:
      - lambda: |-
            auto cur_channel_name = id(Radio_channel_name).state.substr(3,2).c_str();
            unsigned int channel_num = 0;
            sscanf(cur_channel_name, "%x", &channel_num);
            ESP_LOGD("tuya", "cur_channel_name %s, num %d", cur_channel_name, channel_num);
            channel_num = channel_num + 1;
            if (channel_num == 0x0e) {
              channel_num = 0x01;
            }
            std::vector<uint8_t> raw;
            raw.push_back(0x0d);
            raw.push_back(channel_num);
            id(light_tuya).set_raw_datapoint_value(107, raw);
  - platform: template
    name: "Radio Channel -"
    icon: "mdi:access-point-minus"
    on_press:
      - lambda: |-
            auto cur_channel_name = id(Radio_channel_name).state.substr(3,2).c_str();
            unsigned int channel_num = 0;
            sscanf(cur_channel_name, "%x", &channel_num);
            ESP_LOGD("tuya", "cur_channel_name %s, num %d", cur_channel_name, channel_num);
            channel_num = channel_num - 1;
            if (channel_num == 0) {
              channel_num = 0x0d;
            }
            std::vector<uint8_t> raw;
            raw.push_back(0x0d);
            raw.push_back(channel_num);
            id(light_tuya).set_raw_datapoint_value(107, raw);

web_server: # 启动一个web server。这样也可以在没有home assistant时使用
  port: 80
  ota: false

api: # home assistant api
  password: "PASSOWRD"

logger:
  baud_rate: 0 # 关闭uart日志,因为urat用来和TuyaMCU通信了

保存后,运行

esphome run esp_light.yaml

esphome会帮你重新编译固件并升级。成功之后你可以访问A80S的IP,得到如下界面

这里面就可以控制A80S的大部分功能了。

5. 接入home assistant

打开home assistant,系统会自动发现esphome integration。输入yaml里的api密码就连接成功了

如下: