Tasmota Journey

Just before Covid hit I embarked on a project to finally automate many things around our house and property. Back then I discovered Tasmota and thought that’s the way to go. Due to Covid supply chain problems I couldn’t get the Athom preflashed AU smart plugs that I wanted. So I went to my local Kmart and bought a bunch of smart plugs that I was going to reflash myself with Tasmota firmware. However, when I got them apart they didn’t have the requisite Espressif 8266 or ESP32, but instead a Winner Micro W600.

So I bit the bullet, decided to learn MicroPython and managed to get an old W600 firmware image loaded. From there I embraced MicroPython, never looked back at Tasmota, and now have a large ecosystem built on many different platforms and based around MQTT and all programmed in MicroPython. I didn’t really use any frameworks and just built up a part at a time. As it all got bigger it got harder to maintain, and to my horror failures started occurring at an increasing rate.

I then recalled Tasmota. I found preflashed Athom smart plus on eBay at a great price. My first batch used the ESP8266 and the second the ESP32 MCU.

There was a steep learning curve, and there still is for me. As it’s become more developed there is a real mix of old and new. Old style rules and timers to Berry scripts and LVGL capabilities. There is built in support for so many different components, modules, sensors and actuators. It’s simple to get something running with almost no code. And once you get something right it just keeps going, a lot more reliable than the stuff I wrote.

No need to worry about all the small details, they are already there. Whether it’s Wi-Fi fallback, MQTT, clocks and timing, logging, web UI, HTTP, networking or serial it works and works well without much effort.

The framework has so many ways to get code, commands, firmware, telemetry and so on communicated using serial, web based, MQTT and a ton of other ways. With their universal display driver and a new system (to me anyway) called Haspmota it has become dead easy to get great GUI’s going on cheap commodity touch screens.

I’ve been trying to get a Waveshare ESP32-S3-Touch-LCD-7 working under Tasmota. The support and help I get from Tasmota’s Discord and GitHub forums has been nothing short of fantastic. Not what I was expecting from such an open source development. There is a lot of documentation, but sometimes it is hard to find exactly what you are looking for.

As you can tell I’ve really embraced this platform. It’s perfect for my IoT home automation non-cloud setup and I’m mainly writing this piece to make others aware of Tasmota. I’m sure that many out there may also find Tasmota the ideal framework for your projects, but I haven’t seen too much mention in these forums. Do yourself a favour and check it out.

Just yesterday I wired a Monk Makes Plant Monitor to an ESP32 dev board running Tasmota. Only 16 lines of code and a few parameters and it’s now sending json packets via MQTT. And another couple of lines to get the charting looking good.

Anyway, this is what I’ve managed to get the graphics looking like on a 4 inch TFT 480 x 320 with touch with only 160 lines of Berry code and 85 json lines for 6 pages of content & controls.



7 Likes

160 lines of code to produce graphics that clean is bloody fantastic. :slight_smile:

3 Likes

Wow! I haven’t ever used Tasmota, but I have been considering setting up some basic home-automation recently. I’ll definitely be checking it out.

2 Likes

Which one is easier? Tasmota or ESPHOME?

1 Like

I haven’t tried ESPHOME. It’s probably easier to do somethings on one platform and other things on the other. I think Tasmota might have a bigger ecosystem.

Only in the last few days have the developers found the problem/bug with this display, so fingers crossed it won’t be long until it works with Tasmota, in particular Haspmota

2 Likes

And a simple clam shell enclosure finishes it off.


1 Like

Does anybody know what is the best material to use between a plastic bezel and a touch display. If it is attached too tightly it mucks with the touch sensor, in my case anyway

2 Likes

Hey @MarkMakies,

What sort of display do you have? Is it a capacitive touch or a resistive touch display?

If it is a capacitive touch display then cutting a sheet of rubber to size would be an easy way to space this out but a resistive touch display would introduce more complication to this.

1 Like

It’s resistive (uses XPT2046)

1 Like

Hmmm, this is more difficult with a resistive touch display as pressure from any object is likely to trigger it.

I have had a bit of a look around and it seems like consistent pressure on the display is the best option as a touch will only register if the display picks up a change in resistance applied to it.

Maybe some non conductive material that is more rigid than the plastic may work. Something like aluminium?

Thanks Samuel,

I’ll try a thicker bezel for rigidity and Ill make the aperture larger, as close to the glass edge as I can, so less of it is touching.

If that doesn’t work, it may also be possible to design the front so the plastic is flush with the glass edge.

I have been thinking about this but was not 100% sure what you were trying to achieve.
Where you trying to protect the entire display with something.
or
having issues as when you mount your screen, part of the screen is touching the case and causing issues ?

If the 2nd, you could provide long standoffs that you use to secure it to.

e.g. Image 1


Then you should end up with a clean “just useable” part of the screen showing. But no pressure to/from the case.

Exactly, I’m already using standoffs but want to minimize the space between the glass and plastic to avoid ingress and improve the look / aesthetics.

Hi @MarkMakies

The other thing you could look at doing is mounting the screen from the reverse side and just having the faceplate as a cover, looks like you’d need to move the ESP and BME280.

1 Like

Greetings from Brazil, could you make the code used in HASPmota available?

Hey Will, no worries. This is the code running on the display board. It’s not pretty, but has worked without error or failure for a while. Obviously you will have to write your own rules/scripts for the end points. Most of my stuff is running on Athom smart plugs.

config:

Backlog PowerOnState 0; SetOption56 1; SetOption53 1; NtpServer1 192.168.2.1; NtpServer2 192.168.2.100; NtpServer3 0;
  TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660; Latitude XX.XXXXX; Longitude XX.XXXXXX; Timezone 99; SunRise 1

Backlog MqttHost 192.168.2.100; MqttUser mymqtt; MqttPassword XXXXX; FullTopic %topic%/%prefix%/;
  Password1 XXXXX; Password2 XXXXX; TelePeriod 0; PowerRetain 1

Backlog Hostname ES119; Devicename ES119-HaspDisplay2; Topic HaspDisplay;  GroupTopic Display; SSID1 XXXXX;  SSID2 XXXXX

Template {"NAME":"YD-ESP32-S3","GPIO":[0,1,1,0,608,640,1,1,0,800,768,672,736,704,0,11008,10944,992,1024,0,0,1,6210,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1376],"FLAG":0,"BASE":1}

Module 0
DisplayModel 17

Backlog 
  Timer1 {"Enable":1, "Mode":1, "Time":"00:00", "Window":0, "Days":"SMTWTFS", "Repeat":1, "Action":3}; 
  Timer2 {"Enable":1, "Mode":2, "Time":"00:00", "Window":0, "Days":"SMTWTFS", "Repeat":1, "Action":3}; 
  Timers 1

TelePeriod 900

pages.jsonl:

{"page":0,"comment":"created by Mark Makies 2024, licensed under CC BY-SA 4.0."}
{"id":0,"text_color":"#FFFF44"}

{"id":90,"obj":"flex","x":0,"y":0,"w%":100,"h":26,"bg_color":"#606060","radius":0,"border_side":1,"border_width":2,"border_color":"#ff0000","text_color":"#FFFF00"}
  {"id":91,"obj":"label","parentid":90,"x":0,"y":0,"w":100,"long_mode":3,"text":"Some Place     ","flex_grow":1}
  {"id":81,"obj":"label","parentid":90,"x":0,"y":0,"w":100,"h%":100,"long_mode":3,"text":"Rises 00:00","text_color":"#ffffff"}
  {"id":82,"obj":"label","parentid":90,"x":0,"y":0,"w":100,"h%":100,"long_mode":3,"text":"Sets 00:00","text_color":"#ffffff"}
  {"id":92,"obj":"fixed","parentid":90,"w":102,"text_color":"#FFFF44"}
    {"id":95,"parentid":92,"comment":"-- Clock --","obj":"lv_clock","x":0,"y":3,"w":55,"radius":0,"border_side":0,"text_color":"#00FF00"}
    {"id":97,"parentid":92,"comment":"-- Wifi indicator --","obj":"lv_wifi_arcs","x":73,"y":0,"w":20,"h%":80,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#ffFFFF"}

  {"comment":"---------- Bottom buttons ----------"}

  {"id":70,"obj":"line","line_color":"#ff0000","line_width":2,"points":[[0,263],[479,263]]}

  {"id":1,"obj":"btn","x":7,  "y":270,"w":60,"h":50,"action":"p1","bg_color":"#800080","border_color":"#ffffff","text_color":"#ffffff","radius":10,"text":"\uE2dc","text_font":"montserrat-28","border_width":4,"border_side":15,"border_color":"#000000"}
  {"id":2,"obj":"btn","x":87, "y":270,"w":60,"h":50,"action":"p2","bg_color":"#d09000","border_color":"#ffffff","text_color":"#ffffff","radius":10,"text":"\ue6e8","text_font":"montserrat-28","border_width":0,"border_side":15,"border_color":"#000000"}
  {"id":3,"obj":"btn","x":167,"y":270,"w":60,"h":50,"action":"p3","bg_color":"#0000f0","border_color":"#ffffff","text_color":"#FFFFFF","radius":10,"text":"\uE96B","text_font":"montserrat-28","border_width":0,"border_side":15,"border_color":"#000000"}
  {"id":4,"obj":"btn","x":247,"y":270,"w":60,"h":50,"action":"p4","bg_color":"#c00000","border_color":"#ffffff","text_color":"#FFFFFF","radius":10,"text":"\uE1FA","text_font":"montserrat-28","border_width":0,"border_side":15,"border_color":"#000000"}
  {"id":5,"obj":"btn","x":327,"y":270,"w":60,"h":50,"action":"p5","bg_color":"#00a000","border_color":"#ffffff","text_color":"#FFFFFF","radius":10,"text":"\uE32A","text_font":"montserrat-28","border_width":0,"border_side":15,"border_color":"#000000"}
  {"id":6,"obj":"btn","x":407,"y":270,"w":60,"h":50,"action":"p6","bg_color":"#808080","border_color":"#ffffff","text_color":"#FFFFFF","radius":10,"text":"\uE493","text_font":"montserrat-28","border_width":0,"border_side":15,"border_color":"#000000"}

{"page":1,"comment":"---------- Page 1 ----------"}
  {"id":0,"bg_color":"#000000","text_color":"#FFFFFF"}

  {"comment":"---------- Temperature Labels ----------"}
  {"id":90,"obj":"fixed","x":275,"y":35,"w":203,"h":70,"radius":2,"border_side":15,"border_width":2,"border_color":"#8080f0"}
    {"id":91,"obj":"label","parentid":90,"x":5,"y":5,"text":"OUTSIDE","text_font":"montserrat-20","text_color":"#8080f0"}
    {"id":11,"obj":"label","parentid":90,"x":5,"y":30,"text":"20.0 °C" ,"text_font":"montserrat-28","text_color":"#ffffff"}
    {"id":92,"obj":"label","parentid":90,"x":120,"y":3,"text":"H" ,"text_font":"montserrat-14","text_color":"#ff8080"}
    {"id":12,"obj":"label","parentid":90,"x":140,"y":3,"text":"10" ,"text_font":"montserrat-14","text_color":"#ff8080"}
    {"id":93,"obj":"label","parentid":90,"x":120,"y":20,"text":"L","text_font":"montserrat-14","text_color":"#8080ff"}
    {"id":13,"obj":"label","parentid":90,"x":140,"y":20,"text":"30","text_font":"montserrat-14","text_color":"#8080ff"}
    {"id":14,"obj":"label","parentid":90,"x":120,"y":35,"text":"100 %" ,"text_font":"montserrat-28","text_color":"#00ff00"}

  {"id":80,"obj":"fixed","x":275,"y":110,"w":203,"h":70,"radius":2,"border_side":15,"border_width":2,"border_color":"#a0a000"}
    {"id":85,"obj":"label","parentid":80,"x":5,"y":5,"text":"INSIDE","text_font":"montserrat-20","text_color":"#ffff00"}
    {"id":21,"obj":"label","parentid":80,"x":5,"y":30,"text":"20.0 °C" ,"text_font":"montserrat-28","text_color":"#ffffff"}
    {"id":82,"obj":"label","parentid":80,"x":120,"y":3,"text":"H" ,"text_font":"montserrat-14","text_color":"#ff8080"}
    {"id":22,"obj":"label","parentid":80,"x":140,"y":3,"text":"10" ,"text_font":"montserrat-14","text_color":"#ff8080"}
    {"id":83,"obj":"label","parentid":80,"x":120,"y":20,"text":"L","text_font":"montserrat-14","text_color":"#8080ff"}
    {"id":23,"obj":"label","parentid":80,"x":140,"y":20,"text":"30","text_font":"montserrat-14","text_color":"#8080ff"}
    {"id":24,"obj":"label","parentid":80,"x":120,"y":35,"text":"100 %" ,"text_font":"montserrat-28","text_color":"#00ff00"}

  {"comment":"---------- Temperature Bar ----------"}
  {"id":70,"obj":"scale","x":0,"y":35,"w":80,"h":200,"min":-10,"max":40,"mode":4,"total_tick_count":21,"major_tick_every":4,"label_show":true,"line_color":"#757575","line_color10":"#4444FF","line_color30":"#AAAAFF","line_width":2,"line_width10":2,"line_width30":2,"length10":10,"length30":5,"text_src":["-10 °C", "0 °C", "10 °C", "20 °C", "30 °C", "40 °C"],"bg_color":"#607D8B","pad_left":16,"pad_ver":0}
  {"id":15,"obj":"bar","parentid":70,"x":-12,"h%":100,"w":10,"radius10":3,"bg_color":"#000000","bg_color10":"#ff0000","val":27}
    
  {"comment":"---------- Temperature Chart ----------"}
  {"id":16,"obj":"chart","x":70,"y":35,"w":200,"h":200,"border_color":"#ff8000","border_width":2,"radius":1,"pad_all":0,"y_min":-10,"y_max":40,"point_count":96,"height10":0,"width10":0,"v_div_line_count":7,"h_div_line_count":6,"line_color":"#555555","line_width30":2,"series1_color":"#8080f0","series2_color":"#a0a000"}
  {"id":71,"obj":"label","x":70,"y":240,"w":200,"text":"24    20    16    12     8     4      0","text_font":"montserrat-14","text_color":"#ffffff"}

  {"comment":"---------- Pressure Chart ----------"}
  {"id":30,"obj":"chart","x":275,"y":185,"w":203,"h":72,"border_color":"#ff8000","border_width":2,"radius":1,"pad_all":0,"y_min":920,"y_max":960,"point_count":96,"height10":0,"width10":0,"v_div_line_count":7,"h_div_line_count":5,"line_color":"#555555","line_width30":2,"series1_color":"#ffffff"}
  {"id":31,"obj":"label","x":280,"y":190,"text":"945.0 hPa","text_font":"montserrat-14","text_color":"#ffffff","bg_opa":255,"bg_color":"#000000"}
  {"id":33,"obj":"label","x":284, "y":238,"text":"3d 20/div","text_font":"montserrat-14","text_color":"#ffffff","bg_opa":255,"bg_color":"#000000"}

  {"page":2,"comment":"---------- Page 2 Lights ----------"}
  {"id":0,"bg_color":"#000000","text_color":"#FFFFFF"}
  {"id":1,"obj":"label","x":180,"y":30,"text":"Aziz Lights !","text_color":"#ffffff","text_font":"montserrat-20"}
  
  {"id":2,"obj":"btn","x":20,"y":60,"w":180,"h":50,"text":"Garden","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#ffff00","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":3,"obj":"btn","x":20,"y":125,"w":180,"h":50,"text":"North","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#ffff00","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":4,"obj":"btn","x":20,"y":190,"w":180,"h":50,"text":"South","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#ffff00","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}

  {"id":5,"obj":"btn","x":280,"y":190,"w":180,"h":50,"text":"Jetty","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#ffff00","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":6,"obj":"btn","x":280,"y":125,"w":180,"h":50,"text":"West","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#ffff00","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":7,"obj":"btn","x":280,"y":60,"w":180,"h":50,"text":"\ue6e8 ALL \ue6e8","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#00FFd0","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}

  {"page":3,"comment":"---------- Page 3 Watering -------------"}
  {"id":0,"bg_color":"#000000","text_color":"#FFFFFF"}
  {"id":91,"obj":"label","x":110,"y":30,"text":"Aziz Water !","text_color":"#00ff00","text_font":"montserrat-20"}
  
  {"id":1,"obj":"btn","x":10,"y":60,"w":140,"h":50,"text":"Deck","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":2,"obj":"btn","x":10,"y":125,"w":140,"h":50,"text":"Grass","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":3,"obj":"btn","x":10,"y":190,"w":140,"h":50,"text":"Inner","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":4,"obj":"btn","x":170,"y":60,"w":140,"h":50,"text":"Outer","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":5,"obj":"btn","x":170,"y":125,"w":140,"h":50,"text":"East","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  {"id":6,"obj":"btn","x":170,"y":190,"w":140,"h":50,"text":"West","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0,"enabled":false}
    
  {"id":7,"obj":"btn","x":330,"y":40,"w":150,"h":60,"text":"\uE96B ALL \uE96B","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffff00","text_color01":"#000000","border_color":"#00FFd0","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
  
  {"id":90,"obj":"fixed","x":330,"y":120,"w":150,"h":120,"bg_color":"#000000","radius":10,"border_side":15,"border_width":2,"border_color":"#00FFd0","text_color":"#FFFF00","bg_opa":100}
    {"id":8,"parentid":90,"obj":"btn","x":12,"y":15,"w":120,"h":50,"text":"GreenHouse","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-14","border_width":2,"border_side":15,"toggle":0}
    {"id":9,"parentid":90,"obj":"btn","x":12,"y":77,"w":120,"h":30,"text":"Auto Daily","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-14","border_width":2,"border_side":15,"toggle":0}

  {"page":4,"comment":"---------- Page 4 Pumps -------------"}
    {"id":0,"bg_color":"#000000","text_color":"#FFFFFF"}
    {"id":91,"obj":"label","x":110,"y":30,"text":"Pump It Up !","text_color":"#00ff00","text_font":"montserrat-20"}
    {"id":1,"obj":"btn","x":10,"y":60,"w":400,"h":50,"text":"Irrigation Dam -> Tank #6","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
    {"id":2,"obj":"btn","x":10,"y":125,"w":400,"h":50,"text":"House Tank #5 -> Tank #3","bg_color":"#000000","bg_color01":"#00ff00","text_color":"#ffffff","text_color01":"#000000","border_color":"#0000ff","radius":10,"text_font":"montserrat-28","border_width":2,"border_side":15,"toggle":0}
    
  {"page":5,"comment":"---------- Page 5 Growings -------------"}
    {"id":0,"bg_color":"#000000","text_color":"#FFFFFF"}
    
    {"id":1,"obj":"label","x":110,"y":240,"text":"---","text_font":"montserrat-20","text_color":"#ffff00"}
    {"id":2,"obj":"label","x":285,"y":240,"text":"---","text_font":"montserrat-20","text_color":"#80ff80"}
    {"id":3,"obj":"label","x":420,"y":240,"text":"---","text_font":"montserrat-20","text_color":"##ff4f00"}

    {"id":94,"obj":"label","x":50, "y":220,"text":"3days ","text_font":"montserrat-14","text_color":"#ffffff"}
    {"id":91,"obj":"label","x":45, "y":240,"text":"Temp ","text_font":"montserrat-20","text_color":"#ffff00"}
    {"id":92,"obj":"label","x":220,"y":240,"text":"Humi ","text_font":"montserrat-20","text_color":"#80ff80"}
    {"id":93,"obj":"label","x":370,"y":240,"text":"Soil ","text_font":"montserrat-20","text_color":"#ff4f00"}

    {"id":4,"obj":"scale","x":0,"y":35,"w":45,"h":200,"min":10,"max":60,"mode":2,"total_tick_count":21,"major_tick_every":4,"label_show":true,"line_color":"#757575","line_color10":"#4444FF","line_color30":"#AAAAFF","line_width":2,"line_width10":2,"line_width30":2,"length10":10,"length30":5,"text_src":["10 °C", "20 °C", "30 °C", "40 °C", "50 °C", "60 °C"],"bg_color":"#607D8B","pad_left":16,"pad_ver":0}

    {"id":5,"obj":"chart","x":45,"y":35,"w":190,"h":200,"border_width":0,"radius":0,"pad_all":0,"y_min":10,"y_max":60,"point_count":96,"height10":0,"width10":0,"v_div_line_count":7,"h_div_line_count":6,"line_color":"#555555","line_width30":2,"series1_color":"#ffff00"}

    {"id":6,"obj":"scale","x":244,"y":35,"w":45,"h":200,"min":10,"max":60,"mode":2,"total_tick_count":21,"major_tick_every":4,"label_show":true,"line_color":"#757575","line_color10":"#4444FF","line_color30":"#AAAAFF","line_width":2,"line_width10":2,"line_width30":2,"length10":10,"length30":5,"text_src":["0 %", "20 %", "40 %", "60 %", "80 %", "100 %"],"bg_color":"#607D8B","pad_left":16,"pad_ver":0}

    {"id":7,"obj":"chart","x":289,"y":35,"w":190,"h":200,"border_width":0,"radius":0,"pad_all":0,"y_min":0,"y_max":100,"point_count":96,"height10":0,"width10":0,"v_div_line_count":7,"h_div_line_count":6,"line_color":"#555555","line_width30":2,"series1_color":"#80ff80","series2_color":"#ff4f00"}

  {"page":6,"comment":"---------- Page 6 Settings -------------"}
  {"id":0,"bg_color":"#000000","text_color":"#FFFFFF"}
    {"id":91,"obj":"label","x":110,"y":30,"text":"Settings","text_color":"#00ff00","text_font":"montserrat-20"}

display.ini:

:H,ST7796U,480,320,16,SPI,1,*,*,*,*,*,*,*,40
:I
01,A0
11,A0
3A,81,55
36,81,00
21,80
13,80
29,A0
:o,28
:O,29
:A,2A,2B,2C
:R,36
:0,28,00,00,02
:1,88,00,00,03
:2,E8,00,00,00
:3,48,00,00,01
:i,20,21
:M,270,3975,227,3851
:UTI,XPT2046,S1,*,-1,*
CPR 00
RT
:UTT
XPT 300
RT
:UTX
MV 0 2
RT
:UTY
MV 2 2
RT
#

display1.be

#created by Mark Makies 2024, licensed under CC BY-SA 4.0.
import string
import mqtt
import display

display.dimmer(100)

###############################################################################
# Button border highlighter

def createHighlight(object)
	return
	  def ()
		p0b1.border_width = 0
		p0b2.border_width = 0
		p0b3.border_width = 0
		p0b4.border_width = 0
		p0b5.border_width = 0
		p0b6.border_width = 0
		object.border_width = 4
	  end
  end

  tasmota.add_rule("hasp#p0b1#event=up", createHighlight(p0b1))
  tasmota.add_rule("hasp#p0b2#event=up", createHighlight(p0b2))
  tasmota.add_rule("hasp#p0b3#event=up", createHighlight(p0b3))
  tasmota.add_rule("hasp#p0b4#event=up", createHighlight(p0b4))
  tasmota.add_rule("hasp#p0b5#event=up", createHighlight(p0b5))
  tasmota.add_rule("hasp#p0b6#event=up", createHighlight(p0b6))

###############################################################################
# Outside temp / humidity

def Otemp_msg(topic, idx, payload_s, payload_b)
	var output_str = string.format("Temperature: %f:", payload_s)
    p1b11.text = string.format("%.1f °C", payload_s)					# temp text
    p1b15.val = number(payload_s)										# temp bar
	if real(p1b11.text) > real(p1b12.text) p1b12.text = p1b11.text end	# update high
	if real(p1b11.text) < real(p1b13.text) p1b13.text = p1b11.text end	# update low
	if p1b15.val > 30 p1b15.bg_color10 = '#ff0000' 
		elif p1b15.val < 10 p1b15.bg_color10 = '#0000ff'
		else p1b15.bg_color10 = '#00ff00' end
end
mqtt.subscribe("WaterShed/stat/TEMP",Otemp_msg)

def atSunrise()	p1b12.text = p1b11.text	end			# reset temp high
tasmota.add_rule("Clock#Timer=1", atSunrise)

def atSunset()  p1b13.text = p1b11.text	end 		# reset temp low
tasmota.add_rule("Clock#Timer=2", atSunset)

def Ohumi_msg(topic, idx, payload_s, payload_b)
	var  output_str = string.format("%.0f", payload_s) + " %"
    p1b14.text = output_str
end
mqtt.subscribe("WaterShed/stat/HUMI",Ohumi_msg)

###############################################################################
# Inside temperature / humidity / pressure

def AtmoSensor(topic, idx, payload_s, payload_b)
	import json
	var sensor = json.load(payload_s)
	var temp = sensor['BME280']['Temperature']
	var humi = sensor['BME280']['Humidity']
	var duep = sensor['BME280']['DewPoint']
	var prss = sensor['BME280']['Pressure'] * 10

	p1b21.text = string.format("%.1f °C", temp)
	p1b24.text = string.format("%.0f", humi) + " %"
	p1b31.text = string.format("%.1f", prss / 10) + " hPa"

	if real(p1b21.text) > real(p1b22.text) p1b22.text = p1b21.text end	# update high
	if real(p1b21.text) < real(p1b23.text) p1b23.text = p1b21.text end	# update low
end

mqtt.subscribe("HaspDisplay/tele/SENSOR", AtmoSensor)

##############################################################################
# Sunset / Sunrise Times

def ShowSunTimes()
	var ww = tasmota.cmd('status 7')
	var a = 'Rises: ' +  ww['StatusTIM']['Sunrise']
	p0b81.text = a
	a = 'Sets: ' +  ww['StatusTIM']['Sunset']
	p0b82.text = a
end

def Every_15min()
	# update temp chart, 15 min x 96 points = 1 day
	p1b16.val = p1b15.val
	p1b16.val2 = number(p1b21.text)
	#p1b30.val = number(p1b31.text) 

	ShowSunTimes()
end

def Every_45min()
	# update plant monitor,45 min x 96 points - 3 days
	if p5b1.text != "---"	p5b5.val = number(p5b1.text) end
	if p5b2.text != "---"	p5b7.val = number(p5b2.text) end
	if p5b3.text != "---"	p5b7.val2 = number(p5b3.text) end

	p1b30.val = number(p1b31.text) # atmo pressure
end	

def DoStartUp()
	print('start up')
	ShowSunTimes()
	tasmota.set_timer(10000, Every_15min)	# update first value in charts
	tasmota.set_timer(300000, Every_45min)  # only get data every 5 min so we need to wait 300s
end

tasmota.add_cron("0 */15 * * * *", Every_15min, 'Every_15min')

tasmota.add_cron("0 0 0-23/3 * * *", Every_45min, '45a')
tasmota.add_cron("0 45 0-23/3 * * *", Every_45min, '45b')
tasmota.add_cron("0 30 1-23/3 * * *", Every_45min, '45c')
tasmota.add_cron("0 15 2-23/3 * * *", Every_45min, '45d')

tasmota.add_rule("Time#Initialized", DoStartUp)

############  Page 2 Lights

def createToggle(object, topic)
	return / -> mqtt.publish(topic, object.toggle ? 'OFF' : 'ON')
  end
  
  def createState(object)
	return
	  def (topic, idx, payload_s, payload_b)
		object.toggle = payload_s == 'ON' ? 1 : 0
	  end
  end

tasmota.add_rule("hasp#p2b2#event=up", createToggle(p2b2, 'LightsGarden/cmnd/POWER'))
mqtt.subscribe('LightsGarden/stat/POWER', createState(p2b2))
  
tasmota.add_rule("hasp#p2b3#event=up", createToggle(p2b3, 'LightsNorth/cmnd/POWER'))
mqtt.subscribe('LightsNorth/stat/POWER', createState(p2b3))

tasmota.add_rule("hasp#p2b4#event=up", createToggle(p2b4, 'LightsSouth/cmnd/POWER'))
mqtt.subscribe('LightsSouth/stat/POWER', createState(p2b4))  

tasmota.add_rule("hasp#p2b5#event=up", createToggle(p2b5, 'LightsJetty/cmnd/POWER1'))
mqtt.subscribe('LightsJetty/stat/POWER1', createState(p2b5))  

tasmota.add_rule("hasp#p2b6#event=up", createToggle(p2b6, 'LightsWest/cmnd/POWER'))
mqtt.subscribe('LightsWest/stat/POWER', createState(p2b6))  

def AllChange(state)
	mqtt.publish('LightsGarden/cmnd/POWER', state)
	mqtt.publish('LightsNorth/cmnd/POWER', state)
	mqtt.publish('LightsSouth/cmnd/POWER', state)
	mqtt.publish('LightsJetty/cmnd/POWER', state)
	mqtt.publish('LightsWest/cmnd/POWER', state)
end

def AllToggle()
	if p2b7.toggle == false	
		AllChange('ON') 
		p2b7.toggle = 1
		return
	end
	if p2b7.toggle == true 
		AllChange('OFF')
		p2b7.toggle = 0
	end
end
tasmota.add_rule("hasp#p2b7#event=up", AllToggle)

############  Page 3 Watering

tasmota.add_rule("hasp#p3b1#event=up", createToggle(p3b1, 'WaterDeck/cmnd/POWER'))	# Deck
mqtt.subscribe('WaterDeck/stat/POWER', createState(p3b1))

tasmota.add_rule("hasp#p3b2#event=up", createToggle(p3b2, 'WaterShed/cmnd/POWER3')) # Grass
mqtt.subscribe('WaterShed/stat/POWER3', createState(p3b2))

tasmota.add_rule("hasp#p3b3#event=up", createToggle(p3b3, 'WaterShed/cmnd/POWER4')) # Inner
mqtt.subscribe('WaterShed/stat/POWER4', createState(p3b3))

tasmota.add_rule("hasp#p3b4#event=up", createToggle(p3b4, 'WaterShed/cmnd/POWER2')) # Outer
mqtt.subscribe('WaterShed/stat/POWER2', createState(p3b4))

tasmota.add_rule("hasp#p3b5#event=up", createToggle(p3b5, 'WaterEast/cmnd/POWER')) # East
mqtt.subscribe('WaterEast/stat/POWER', createState(p3b5))

tasmota.add_rule("hasp#p3b6#event=up", createToggle(p3b6, 'WaterWest/cmnd/POWER')) # West
mqtt.subscribe('WaterWest/stat/POWER', createState(p3b6))

tasmota.add_rule("hasp#p3b8#event=up", createToggle(p3b8, 'WaterGreenhouse/cmnd/POWER')) # Greenhouse
mqtt.subscribe('WaterGreenhouse/stat/POWER', createState(p3b8))

def WaterAll()
	if p3b7.toggle == false	
		mqtt.publish('WaterEast/cmnd/event', 'WaterAll=1')
		p3b7.toggle = true	
		return
	end
	if p3b7.toggle == true 
		mqtt.publish('WaterEast/cmnd/event', 'WaterAll=0')
		p3b7.toggle = false	
	end
end
tasmota.add_rule("hasp#p3b7#event=up", WaterAll)

def WaterAllState(object)
	return
	  def (topic, idx, payload_s, payload_b)
		object.toggle = payload_s == '1' ? 1 : 0
	  end
  end
mqtt.subscribe('WaterEast/stat/WaterAll', WaterAllState(p3b7))

def GreenhouseAuto()
	if p3b9.toggle == true	
		mqtt.publish('WaterGreenhouse/cmnd/event', 'AUTO=0')
	end
	if p3b9.toggle == false 
		mqtt.publish('WaterGreenhouse/cmnd/event', 'AUTO=1')
	end
end
tasmota.add_rule("hasp#p3b9#event=up", GreenhouseAuto)

def GreenhouseAutoState(object)
	return
	  def (topic, idx, payload_s, payload_b)
		object.toggle = payload_s == '1' ? 1 : 0
	  end
  end
mqtt.subscribe('WaterGreenhouse/stat/AUTO', GreenhouseAutoState(p3b9))

############  Page 4 Pumps

tasmota.add_rule("hasp#p4b1#event=up", createToggle(p4b1, 'PumpDam/cmnd/POWER'))	# Deck
mqtt.subscribe('PumpDam/stat/POWER', createState(p4b1))

tasmota.add_rule("hasp#p4b2#event=up", createToggle(p4b2, 'PumpHouse/cmnd/POWER')) # Grass
mqtt.subscribe('PumpHouse/stat/POWER', createState(p4b2))

############# Page 5 Soil monitor
# it's updated every 5 min on ES118

def SoilSensor(topic, idx, payload_s, payload_b)
	import json
	var sensor = json.load(payload_s)

	var wetness = sensor['wetness']
	var temp = sensor['temp']
	var humi = sensor['humidity']
	
	p5b1.text = string.format("%.1f °C", temp)
	p5b2.text = string.format("%.0f", humi) + ' %'
	p5b3.text = string.format("%.0f", wetness) + ' %'
end

mqtt.subscribe("SoilSensor/stat/SOIL", SoilSensor)	


And autoexec.be

import haspmota
haspmota.start()

import display1