Goodbye Tasmota

Two lines of yaml should get you a web interface to control your esphome node :point_down:

web_server:
  port: 80

I’m using Tasmota, and have wondered whether I should go with ESPHome (pretty much at a beginner level), But one thing repels me about ESPHome - YAMYL files! I’ve sat for ages looking at a YAML file, wondering why it doesn’t work, only to find that when I’ve re-entered the code exactly as before (yeah - I know about the difference between tabs and spaces) , it finally works! I find YAMYL syntax weird and non-intuitive.

This is my subtitle. Eventually you will grok.

2 Likes

I’ve been exactly where you are… i looked at “weird” yaml and went on. Not for long, though… when i needed my custom function on esp module i went to esphome where i could do it.
Yaml files are not so weird once you learn them. But, isn’t it same for each thing? Like ….autocad or altium designer: it’s super weird if you don’t know how to use it…
Of course, you must be extra carefull regarding spaces, indentation…

1 Like

‘Grocking’ doesn’t come so easily when you’re 83 years old! :tired_face:

1 Like

Well, yaml beats Tasmota scripts…
This is what i used for my 2ch dimmer in Tasmota:

;WiFi-2CH-Dimmer v1.3
;QS-WiFi-D02-2C
;MS-105B

>D
sw1=0
sw2=0
x=0
cn1=0
cn2=0
tm1=0
tm2=0
h1=0
h2=0
t1=0
t2=0
sl1=0
sl2=0
di=""
pl=2
pu=10
dd1=0
dd2=0
mp=2.2
sp=2
ll=15
ul=95
dv1=70
dv2=70
p1=0
p2=0

>B
=>Counter1 0
=>Counter2 0
=>Baudrate 9600
=#sd1(dv1)
delay(1000)
=#sd1(0)
=#sd2(0)

>F
cn1=pc[1]
cn2=pc[2]
if chg[cn1]>0
then sw1=1
else sw1=0
endif
if chg[cn2]>0
then sw2=1
else sw2=0
endif
tm1+=1
tm2+=1
if sw1==0
and tm1>pl
and tm1<pu
then
t1^=1
if t1==1
then
=#sd1(dv1)
else
=#sd1(0)
endif
endif
if sw2==0
and tm2>pl
and tm2<pu
then
t2^=1
if t2==1
then
=#sd2(dv2)
else
=#sd2(0)
endif
endif
if sw1>0
then
if h1==0
then
dd1^=1
endif
if tm1>pu
then
h1=1
if t1>0
then
if dd1>0
then
dv1+=sp
if dv1>ul
then
dv1=ul
endif
=#sd1(dv1)
else
dv1-=sp
if dv1<ll
then
dv1=ll
endif
=#sd1(dv1)
endif
endif
endif
else
tm1=0
h1=0
endif
if sw2>0
then
if h2==0
then
dd2^=1
endif
if tm2>pu
then
h2=1
if t2>0
then
if dd2>0
then
dv2+=sp
if dv2>ul
then
dv2=ul
endif
=#sd2(dv2)
else
dv2-=sp
if dv2<ll
then
dv2=ll
endif
=#sd2(dv2)
endif
endif
endif
else
tm2=0
h2=0
endif

>E
sl1=Channel1
if chg[sl1]>0
then
if sl1>0
then
dv1=sl1
=#sd1(dv1)
else
t1=0
=#sd1(0)
endif
endif
sl2=Channel2
if chg[sl2]>0
then
if sl2>0
then
dv2=sl2
=#sd2(dv2)
else
t2=0
=#sd2(0)
endif
endif

p1=pwr[1]
if p1==1
then
t1=1
=#sd1(dv1)
else
t1=0
=#sd1(0)
endif
p2=pwr[2]
if p2==1
then
t2=1
=#sd2(dv2)
else
t2=0
=#sd2(0)
endif

#sd1(x)
di="FF5501"+hn(x*mp)+"0000000A"
=>SerialSend5 %di%
=>Channel1 %x%
#sd2(x)
di="FF550200"+hn(x*mp)+"00000A"
=>SerialSend5 %di%
=>Channel2 %x%
#

As you can see, just jibberish…
And then it doesn’t even work reliable :thinking:
And this is the same in EspHome:


substitutions:
  node_name: qs-wifi-ds02
  node_id: IP_62
  friendly_node_name: "Dual Channel Dimmer"

esphome:
  name: ${node_name}
  comment: ${friendly_node_name}
  platform: ESP8266
  board: esp01_1m

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_iotpw
  power_save_mode: none

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${node_name} " FB"
    password: "fallback"

captive_portal:

# Enable Home Assistant API
api:
  encryption:
    key: !secret api_encryption

#Enable Over The Air updates
ota:

#Disable logging
logger:
  baud_rate: 0
  logs:
    sensor: ERROR
    duty_cycle: ERROR
    binary_sensor: ERROR
    light: ERROR

# Enable Web server.
web_server:
  port: 80

# Sync time with Home Assistant.
time:
  - platform: homeassistant
    id: homeassistant_time

# Binary Sensors.
binary_sensor:
  #Binary sensor (on/off) which reads duty_cyle sensor readings. CH1
  - platform: template
    id: switch1
    internal: true
    name: "${node_id} Switch Binary Sensor 1"
    # read duty_cycle, convert to on/off
    lambda: |-
      if (id(sensor_push_switch_1).state < 95.0) {
        return true;
      } else {
        return false;
      }
    # Short Click - toggle light only
    on_click:
      max_length: 300ms
      then:
        light.toggle: light_main_1
    # Generic On_Press - log press, toggle DIM Direction and reset press interval counter
    on_press:
      then:
        - logger.log: "Switch 1 Press"
        - lambda: |-
            if (id(g_direction_1) == 0) {
              id(g_direction_1) = 1;
            } else {
              id(g_direction_1) = 0;
            }
            id(g_counter_1) = 0;
  #Binary sensor (on/off) which reads duty_cyle sensor readings. CH2
  - platform: template
    id: switch2
    internal: true
    name: "${node_id} Switch Binary Sensor 2"
    # read duty_cycle, convert to on/off
    lambda: |-
      if (id(sensor_push_switch_2).state < 95.0) {
        return true;
      } else {
        return false;
      }
    # Short Click - toggle light only
    on_click:
      max_length: 300ms
      then:
        light.toggle: light_main_2
    # Generic On_Press - log press, toggle DIM Direction and reset press interval counter
    on_press:
      then:
        - logger.log: "Switch 2 Press"
        - lambda: |-
            if (id(g_direction_2) == 0) {
              id(g_direction_2) = 1;
            } else {
              id(g_direction_2) = 0;
            }
            id(g_counter_2) = 0;

# Sensors.
sensor:
# Primary template sensor to track Brightness of light object for "on_value" sending to MCU dimmer
  # CH1
  - platform: template
    name: "${node_id} Brightness Sensor CH1"
    id: sensor_g_bright_1
    internal: true
    update_interval: 20ms
    # Ensure on_value only triggered when brightness (0-255) changes
    filters:
      delta: 0.8
    # Read brightness (0 - 1) from light , convert to (0-255) for MCU
    lambda: |-
      if (id(light_main_1).remote_values.is_on()) {
        return (int(id(light_main_1).remote_values.get_brightness() * 255));
      }
      else {
        return 0;
      }
    # On Change send to MCU via UART
    on_value:
      then:
        - uart.write: !lambda |-
            return {0xFF, 0x55, 0x01, (char) id(sensor_g_bright_1).state, 0x00, 0x00, 0x00, 0x0A};
        - logger.log:
            level: INFO
            format: "CH1 Sensor Value Change sent to UART %3.1f"
            args: ["id(sensor_g_bright_1).state"]
  # Sensor to detect button push (via duty_cycle of 50hz mains signal)
  - platform: template
    name: "${node_id} Brightness Sensor CH2"
    id: sensor_g_bright_2
    internal: true
    update_interval: 20ms
    # Ensure on_value only triggered when brightness (0-255) changes
    filters:
      delta: 0.8
    # Read brightness (0 - 1) from light , convert to (0-255) for MCU
    lambda: |-
      if (id(light_main_2).remote_values.is_on()) {
        return (int(id(light_main_2).remote_values.get_brightness() * 255));
      }
      else {
        return 0;
      }
    # On Change send to MCU via UART
    on_value:
      then:
        - uart.write: !lambda |-
            return {0xFF, 0x55, 0x02, 0x00, (char) id(sensor_g_bright_2).state, 0x00, 0x00, 0x0A};
        - logger.log:
            level: INFO
            format: "CH2 Sensor Value Change sent to UART %3.1f"
            args: ["id(sensor_g_bright_2).state"]
  # Sensor to detect button push (via duty_cycle of 50hz mains signal)
  - platform: duty_cycle
    pin: GPIO13
    internal: true
    id: sensor_push_switch_1
    name: "${node_id} Sensor Push Switch 1"
    update_interval: 20ms
  - platform: duty_cycle
    pin: GPIO5
    internal: true
    id: sensor_push_switch_2
    name: "${node_id} Sensor Push Switch 2"
    update_interval: 20ms

globals:
  # Dim direction for Switch 1: 0=Up (brighten) 1=down (dim)
  - id: g_direction_1
    type: int
    restore_value: no
    initial_value: "1"
  # Counter for time pressed for switch 1
  - id: g_counter_1
    type: int
    restore_value: no
    initial_value: "0"
  # initial brightness
  # Dim direction for Switch 2: 0=Up (brighten) 1=down (dim)
  - id: g_direction_2
    type: int
    restore_value: no
    initial_value: "1"
  # Counter for time pressed for switch 2
  - id: g_counter_2
    type: int
    restore_value: no
    initial_value: "0"
  # initial brightness
  
# Uart definition to talk to MCU dimmer
uart:
  tx_pin: GPIO1
  rx_pin: GPIO3
  stop_bits: 1
  baud_rate: 9600

# Dummy light output to allow creation of light object
output:
  - platform: esp8266_pwm
    pin: GPIO14
    frequency: 800 Hz
    id: dummy_pwm1
  - platform: esp8266_pwm
    pin: GPIO16
    frequency: 800 Hz
    id: dummy_pwm2

# Primary Light object exposed to HA
light:
  - platform: monochromatic
    default_transition_length: 20ms
    restore_mode: RESTORE_DEFAULT_OFF
    name: "${node_id} Light 1"
    output: dummy_pwm1
    id: light_main_1
  - platform: monochromatic
    default_transition_length: 20ms
    restore_mode: RESTORE_DEFAULT_OFF
    name: "${node_id} Light 2"
    output: dummy_pwm2
    id: light_main_2

# Polling object for long press handling of switch for dim/brighten cycle
interval:
  - interval: 20ms
    then:
      - if:
          condition:
            binary_sensor.is_on: switch1
          then:
            # Ramp rate for dim is product of interval (20ms) * number of intervals
            # Every 20ms Dimmer is increased/decreased by 2/255
            # Lower limit = 10%
            # Upper limit = 100%
            # 100% - 10% = 90% = 230/255. Therefore 230/2 * 20ms = 2.3 seconds for full range
            # At full/min brightness - further 16x20ms = 0.32 Seconds "dwell" by resetting counter to 0
            # Initial pause for 16x20ms = 0.32s to allow "on_click" to be discounted 1st
            # g_direction_1 = 0 (Increasing brightness)
            # g_direction_1 = 1 (decreasing brightness)
            # g_counter_1 = Interval pulse counter

            lambda: |-
              float curr_bright = id(light_main_1).remote_values.get_brightness();
              id(g_counter_1) += 1; 

              // If max bright, change direction
              if (curr_bright >= 0.999 && id(g_direction_1) == 0) {
                id(g_direction_1) = 1;
                id(g_counter_1) = 0;
              }

              // If below min_bright, change direction
              if (curr_bright < 0.1 && id(g_direction_1) == 1) {
                id(g_direction_1) = 0;
                id(g_counter_1) = 0;
              }

              if (id(g_direction_1) == 0 && id(g_counter_1) > 15) {
                // Increase Bright
                auto call = id(light_main_1).turn_on();
                call.set_brightness(curr_bright + (2.0/255.0));
                call.perform();
              }

              else if(id(g_direction_1) == 1 && id(g_counter_1) > 15) {
                // Decrease Bright
                auto call = id(light_main_1).turn_on();
                call.set_brightness(curr_bright - (2.0/255.0));
                call.perform();
              }
      - if:
          condition:
            binary_sensor.is_on: switch2
          then:
            # Ramp rate for dim is product of interval (20ms) * number of intervals
            # Every 20ms Dimmer is increased/decreased by 2/255
            # Lower limit = 10%
            # Upper limit = 100%
            # 100% - 10% = 90% = 230/255. Therefore 230/2 * 20ms = 2.3 seconds for full range
            # At full/min brightness - further 16x20ms = 0.32 Seconds "dwell" by resetting counter to 0
            # Initial pause for 16x20ms = 0.32s to allow "on_click" to be discounted 1st
            # g_direction_1 = 0 (Increasing brightness)
            # g_direction_1 = 1 (decreasing brightness)
            # g_counter_1 = Interval pulse counter

            lambda: |-
              float curr_bright = id(light_main_2).remote_values.get_brightness();
              id(g_counter_2) += 1; 

              // If max bright, change direction
              if (curr_bright >= 0.999 && id(g_direction_2) == 0) {
                id(g_direction_2) = 1;
                id(g_counter_2) = 0;
              }

              // If below min_bright, change direction
              if (curr_bright < 0.1 && id(g_direction_2) == 1) {
                id(g_direction_2) = 0;
                id(g_counter_2) = 0;
              }

              if (id(g_direction_2) == 0 && id(g_counter_2) > 15) {
                // Increase Bright
                auto call = id(light_main_2).turn_on();
                call.set_brightness(curr_bright + (2.0/255.0));
                call.perform();
              }

              else if(id(g_direction_2) == 1 && id(g_counter_2) > 15) {
                // Decrease Bright
                auto call = id(light_main_2).turn_on();
                call.set_brightness(curr_bright - (2.0/255.0));
                call.perform();
              }        

Yes,it is bigger, but way easier to comprehend …and allows me to debug/improve the script (while Tasmota does not)

1 Like

Well, all i can say is… congratulations! Keep going!

Looks like there is a bit of a push for Matter in Tasmota.

Stupid question but how did you came up with this “script” in the first place? :thinking:

My Tasmota time is long over (essentially deprecated because of esphome :stuck_out_tongue:) but beside the various scattered configurations in the web ui, templates and some (for me) already gibberish commands in the cli I (luckily) never came that “far” to “work” with scripts. Is there some kind of generator for these?

Two lines of code


# Example configuration entry
web_server:
  port: 80

I didn’t made it myself…found info from arendst(tasmota); i believe the original author is thxthx0; also blackadder has some info on it.

But my point was, it is impossible to improve this script without being 100% familiar with it.

1 Like

This whole discussion seriously has internet-warrior vibes too be honest.

3 Likes

I was driven by frustration to replace my Z-Wave plugs with something reliable and easy to manage. In the end my solution with ESPHome plugs were cheaper, easier to manage, and more reliable. Now every smart plug on my network can be controlled reliably. The best part is that they actually work. That is all I ever wanted.

3 Likes

@basha2000
Can you share your part number and any links that might be relevant?

This is simple use what works for you. Both have advantages and disadvantages along with the fact both are evolving.
Esp for streamlined but it fails in the setup area rather badly with the setup since a newcomer is going to lose their mind trying to configure yaml.
Tas one size fits most things isn’t always the way to go.

The great thing is we have options and with the push to improve both things are only going to get better.

Just thinking… perhaps we would come reasonably close to “perfection” if esphome would have similar approach as HA, where you can configure cards via UI (newcomer), but you can also do it (or improve) directly by typing a code (advanced user).
But, as i said: just (crazy) thinking… since massive amount of work would be needed on UI part of ESPHome addon.

Is there a guide anywhere describing how to convert tasmota templates into esphome code?

The guide i found on the esphome website seems to only describe the esphome to tasmota firmware upload process.

To me…tasmota is jibberish…

Pretty sue it is not convertable

Don’t think so. But you find ready made yamls for various devices under this link

All you need from Tasmota are the GPIO used and what they do. Then create a new device in ESPHome.