Skip to content

Instantly share code, notes, and snippets.

@Staars
Created October 23, 2025 05:02
Show Gist options
  • Select an option

  • Save Staars/1f1bfa3b5471efdfa74c484761638f40 to your computer and use it in GitHub Desktop.

Select an option

Save Staars/1f1bfa3b5471efdfa74c484761638f40 to your computer and use it in GitHub Desktop.
#- Dashboard for Tasmota -#
#- Beautiful tile-based smart home control -#
class HomeDashboard
var scr
var hres
var vres
var tiles
var devices
var header_height
var tile_padding
var scroll_container
def init()
lv.start()
self.hres = lv.get_hor_res()
self.vres = lv.get_ver_res()
self.header_height = 60
self.tile_padding = 10
self.tiles = []
#- Device states -#
self.devices = {
"living_light": {"name": "Living Room", "type": "light", "state": false, "brightness": 75, "icon": lv.SYMBOL_HOME},
"bedroom_light": {"name": "Bedroom", "type": "light", "state": true, "brightness": 50, "icon": lv.SYMBOL_HOME},
"kitchen_light": {"name": "Kitchen", "type": "light", "state": false, "brightness": 100, "icon": lv.SYMBOL_HOME},
"thermostat": {"name": "Thermostat", "type": "climate", "state": true, "temp": 22, "icon": lv.SYMBOL_CHARGE},
"front_door": {"name": "Front Door", "type": "lock", "state": true, "icon": lv.SYMBOL_OK},
"garage": {"name": "Garage Door", "type": "door", "state": false, "icon": lv.SYMBOL_HOME},
"fan": {"name": "Ceiling Fan", "type": "fan", "state": true, "speed": 2, "icon": lv.SYMBOL_REFRESH},
"plug1": {"name": "Coffee Maker", "type": "plug", "state": false, "icon": lv.SYMBOL_POWER}
}
self.create_ui()
print(" Home Dashboard loaded!")
end
#- Create UI -#
def create_ui()
self.scr = lv.scr_act()
self.scr.set_style_bg_color(lv.color(0xF2F2F7), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Header -#
var header = lv.obj(self.scr)
header.set_size(self.hres, self.header_height)
header.set_pos(0, 0)
header.set_style_bg_color(lv.color(0xFFFFFF), lv.PART_MAIN | lv.STATE_DEFAULT)
header.set_style_border_width(0, lv.PART_MAIN | lv.STATE_DEFAULT)
header.set_style_radius(0, lv.PART_MAIN | lv.STATE_DEFAULT)
header.set_style_shadow_width(3, lv.PART_MAIN | lv.STATE_DEFAULT)
header.set_style_shadow_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
header.set_style_shadow_opa(30, lv.PART_MAIN | lv.STATE_DEFAULT)
#- Header title -#
var title = lv.label(header)
var f20 = lv.montserrat_font(20)
if f20 != nil title.set_style_text_font(f20, lv.PART_MAIN | lv.STATE_DEFAULT) end
title.set_text("Home")
title.set_pos(15, 18)
title.set_style_text_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Menu button -#
var menu_btn = lv.btn(header)
menu_btn.set_size(40, 40)
menu_btn.set_pos(self.hres - 50, 10)
menu_btn.set_style_bg_color(lv.color(0xF2F2F7), lv.PART_MAIN | lv.STATE_DEFAULT)
menu_btn.set_style_radius(20, lv.PART_MAIN | lv.STATE_DEFAULT)
menu_btn.set_style_border_width(0, lv.PART_MAIN | lv.STATE_DEFAULT)
var menu_icon = lv.label(menu_btn)
menu_icon.set_text(lv.SYMBOL_SETTINGS)
menu_icon.center()
#- Scrollable container for tiles -#
self.scroll_container = lv.obj(self.scr)
self.scroll_container.set_size(self.hres, self.vres - self.header_height)
self.scroll_container.set_pos(0, self.header_height)
self.scroll_container.set_style_bg_color(lv.color(0xF2F2F7), lv.PART_MAIN | lv.STATE_DEFAULT)
self.scroll_container.set_style_border_width(0, lv.PART_MAIN | lv.STATE_DEFAULT)
self.scroll_container.set_style_pad_all(self.tile_padding, lv.PART_MAIN | lv.STATE_DEFAULT)
self.scroll_container.set_scrollbar_mode(lv.SCROLLBAR_MODE_AUTO)
#- Create tiles -#
self.create_tiles()
end
#- Create tiles for devices -#
def create_tiles()
var tile_size = int((self.hres - self.tile_padding * 3) / 2)
var small_tile_h = int(tile_size * 0.6)
var y_offset = 0
#- Row 1: Two large tiles -#
var row1_devices = ["living_light", "bedroom_light"]
var x = 0
while x < 2
var device_key = row1_devices[x]
self.create_large_tile(device_key,
x * (tile_size + self.tile_padding),
y_offset,
tile_size,
tile_size)
x += 1
end
y_offset += tile_size + self.tile_padding
#- Row 2: Two small tiles -#
var row2_devices = ["kitchen_light", "thermostat"]
x = 0
while x < 2
var device_key = row2_devices[x]
self.create_small_tile(device_key,
x * (tile_size + self.tile_padding),
y_offset,
tile_size,
small_tile_h)
x += 1
end
y_offset += small_tile_h + self.tile_padding
#- Row 3: Two small tiles -#
var row3_devices = ["front_door", "garage"]
x = 0
while x < 2
var device_key = row3_devices[x]
self.create_small_tile(device_key,
x * (tile_size + self.tile_padding),
y_offset,
tile_size,
small_tile_h)
x += 1
end
y_offset += small_tile_h + self.tile_padding
#- Row 4: Two small tiles -#
var row4_devices = ["fan", "plug1"]
x = 0
while x < 2
var device_key = row4_devices[x]
self.create_small_tile(device_key,
x * (tile_size + self.tile_padding),
y_offset,
tile_size,
small_tile_h)
x += 1
end
end
#- Get color based on device type and state -#
def get_tile_color(device_type, state)
if !state return 0xFFFFFF end
if device_type == "light" return 0xFFD966
elif device_type == "climate" return 0xFF9966
elif device_type == "lock" return 0x66CC99
elif device_type == "door" return 0x99CCFF
elif device_type == "fan" return 0xB3B3FF
elif device_type == "plug" return 0xFFB3D9
end
return 0xCCCCCC
end
#- Create large tile -#
def create_large_tile(device_key, x, y, w, h)
var device = self.devices[device_key]
var tile = lv.obj(self.scroll_container)
tile.set_size(w, h)
tile.set_pos(x, y)
var bg_color = self.get_tile_color(device['type'], device['state'])
tile.set_style_bg_color(lv.color(bg_color), lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_radius(12, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_border_width(0, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_shadow_width(5, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_shadow_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_shadow_opa(20, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_pad_all(15, lv.PART_MAIN | lv.STATE_DEFAULT)
#- Icon -#
var icon = lv.label(tile)
var f20 = lv.montserrat_font(20)
if f20 != nil icon.set_style_text_font(f20, lv.PART_MAIN | lv.STATE_DEFAULT) end
icon.set_text(device['icon'])
icon.set_pos(0, 0)
var text_color = device['state'] ? 0x000000 : 0x8E8E93
icon.set_style_text_color(lv.color(text_color), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Device name -#
var name = lv.label(tile)
var f16 = lv.montserrat_font(16)
if f16 != nil name.set_style_text_font(f16, lv.PART_MAIN | lv.STATE_DEFAULT) end
name.set_text(device['name'])
name.set_pos(0, h - 50)
name.set_style_text_color(lv.color(text_color), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Status text -#
var status_text = device['state'] ? "On" : "Off"
if device['type'] == "light" && device['state']
status_text = f"{device['brightness']}%"
elif device['type'] == "climate" && device['state']
status_text = f"{device['temp']}°C"
elif device['type'] == "lock"
status_text = device['state'] ? "Locked" : "Unlocked"
elif device['type'] == "fan" && device['state']
status_text = f"Speed {device['speed']}"
end
var status = lv.label(tile)
var f14 = lv.montserrat_font(14)
if f14 != nil status.set_style_text_font(f14, lv.PART_MAIN | lv.STATE_DEFAULT) end
status.set_text(status_text)
status.set_pos(0, h - 30)
status.set_style_text_color(lv.color(text_color), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Click event -#
tile.add_event_cb(/obj, evt -> self.toggle_device(device_key), lv.EVENT_CLICKED, 0)
self.tiles.push(tile)
end
#- Create small tile -#
def create_small_tile(device_key, x, y, w, h)
var device = self.devices[device_key]
var tile = lv.obj(self.scroll_container)
tile.set_size(w, h)
tile.set_pos(x, y)
var bg_color = self.get_tile_color(device['type'], device['state'])
tile.set_style_bg_color(lv.color(bg_color), lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_radius(12, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_border_width(0, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_shadow_width(5, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_shadow_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_shadow_opa(20, lv.PART_MAIN | lv.STATE_DEFAULT)
tile.set_style_pad_all(12, lv.PART_MAIN | lv.STATE_DEFAULT)
#- Icon -#
var icon = lv.label(tile)
var f16 = lv.montserrat_font(16)
if f16 != nil icon.set_style_text_font(f16, lv.PART_MAIN | lv.STATE_DEFAULT) end
icon.set_text(device['icon'])
icon.set_pos(0, 0)
var text_color = device['state'] ? 0x000000 : 0x8E8E93
icon.set_style_text_color(lv.color(text_color), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Device name -#
var name = lv.label(tile)
var f14 = lv.montserrat_font(14)
if f14 != nil name.set_style_text_font(f14, lv.PART_MAIN | lv.STATE_DEFAULT) end
name.set_text(device['name'])
name.set_pos(0, h - 35)
name.set_style_text_color(lv.color(text_color), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Status indicator -#
var status = lv.label(tile)
var f12 = lv.montserrat_font(12)
if f12 != nil status.set_style_text_font(f12, lv.PART_MAIN | lv.STATE_DEFAULT) end
var status_text = device['state'] ? "On" : "Off"
if device['type'] == "lock"
status_text = device['state'] ? "Locked" : "Unlocked"
elif device['type'] == "climate" && device['state']
status_text = f"{device['temp']}°C"
end
status.set_text(status_text)
status.set_pos(0, h - 18)
status.set_style_text_color(lv.color(text_color), lv.PART_MAIN | lv.STATE_DEFAULT)
#- Click event -#
tile.add_event_cb(/obj, evt -> self.toggle_device(device_key), lv.EVENT_CLICKED, 0)
self.tiles.push(tile)
end
#- Toggle device state -#
def toggle_device(device_key)
var device = self.devices[device_key]
if device['type'] == "light"
device['state'] = !device['state']
print(f"{device['name']} turned {device['state'] ? 'ON' : 'OFF'}")
elif device['type'] == "climate"
device['state'] = !device['state']
print(f"Thermostat turned {device['state'] ? 'ON' : 'OFF'}")
elif device['type'] == "lock"
device['state'] = !device['state']
print(f"Door {device['state'] ? 'LOCKED' : 'UNLOCKED'}")
elif device['type'] == "door"
device['state'] = !device['state']
print(f"{device['name']} {device['state'] ? 'CLOSED' : 'OPENED'}")
elif device['type'] == "fan"
device['state'] = !device['state']
print(f"Fan turned {device['state'] ? 'ON' : 'OFF'}")
elif device['type'] == "plug"
device['state'] = !device['state']
print(f"{device['name']} turned {device['state'] ? 'ON' : 'OFF'}")
end
#- Rebuild UI to show changes -#
self.refresh_tiles()
end
#- Refresh tiles -#
def refresh_tiles()
#- Clear old tiles -#
var i = 0
while i < size(self.tiles)
self.tiles[i].delete()
i += 1
end
self.tiles = []
#- Recreate tiles -#
self.create_tiles()
end
end
#- Create dashboard -#
dashboard = HomeDashboard()
print(" Home Dashboard loaded! Tap tiles to control devices.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment