Created
October 23, 2025 05:02
-
-
Save Staars/1f1bfa3b5471efdfa74c484761638f40 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #- 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