├── .gitignore ├── secrets.example.yaml ├── fonts ├── Orbitron-Regular.ttf └── materialdesignicons-webfont.ttf ├── README.md ├── Makefile ├── calibrate.yaml └── cyd_container.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /.esphome 2 | /secrets.yaml 3 | -------------------------------------------------------------------------------- /secrets.example.yaml: -------------------------------------------------------------------------------- 1 | wifi_ssid: 2 | wifi_pwd: 3 | ota_pwd: 4 | api_key: 5 | -------------------------------------------------------------------------------- /fonts/Orbitron-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buzz/cyd-container/main/fonts/Orbitron-Regular.ttf -------------------------------------------------------------------------------- /fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buzz/cyd-container/main/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ESPHome CYD Thermostat example 2 | https://gist.github.com/GuySie/3721b7851c6a2de644453f0570dab87c 3 | 4 | Extended IO/GPIO pins 5 | https://randomnerdtutorials.com/esp32-cheap-yellow-display-cyd-pinout-esp32-2432s028r/#extended-io 6 | https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display/blob/main/PINS.md#broken-out-pins 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CONFIG = cyd_container.yaml 2 | SERIAL_PORT = /dev/ttyUSB0 3 | OTA_HOST = cyd-container 4 | FIRMWARE = .esphome/build/cyd-container/.pioenvs/cyd-container/firmware.ota.bin 5 | 6 | $(FIRMWARE): $(CONFIG) 7 | esphome compile $(CONFIG) 8 | 9 | .PHONY: flash-usb 10 | flash-usb: $(FIRMWARE) 11 | esphome upload --device $(SERIAL_PORT) $(CONFIG) 12 | 13 | .PHONY: flash-ota 14 | flash-ota: $(FIRMWARE) 15 | esphome upload --device $(OTA_HOST) $(CONFIG) 16 | 17 | .PHONY: logs-usb 18 | logs-usb: 19 | esphome logs --device $(SERIAL_PORT) $(CONFIG) 20 | -------------------------------------------------------------------------------- /calibrate.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | device_name: yellowtft1 3 | friendly_name: TFT Test 4 | 5 | esphome: 6 | name: $device_name 7 | friendly_name: $friendly_name 8 | 9 | esp32: 10 | board: esp32dev 11 | framework: 12 | type: esp-idf 13 | 14 | logger: 15 | level: INFO 16 | 17 | ota: 18 | platform: esphome 19 | password: !secret ota_pwd 20 | 21 | wifi: 22 | ssid: !secret wifi_ssid 23 | password: !secret wifi_pwd 24 | 25 | # Enable fallback hotspot (captive portal) in case wifi connection fails 26 | ap: 27 | ssid: $device_name Fallback Hotspot 28 | password: !secret ap_pwd 29 | 30 | captive_portal: 31 | 32 | font: 33 | - file: fonts/Arimo-Regular.ttf 34 | id: arimo20 35 | size: 20 36 | glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ abcdefghijklmnopqrstuvwxyzåäö" 37 | 38 | # Create Home Assistant blue color 39 | color: 40 | - id: ha_blue 41 | hex: 51c0f2 42 | 43 | output: 44 | - platform: ledc 45 | pin: GPIO21 46 | id: backlight_pwm 47 | 48 | light: 49 | - platform: monochromatic 50 | output: backlight_pwm 51 | name: Display Backlight 52 | id: backlight 53 | restore_mode: ALWAYS_ON 54 | 55 | globals: 56 | - id: x_min 57 | type: int 58 | initial_value: '500' 59 | - id: x_max 60 | type: int 61 | initial_value: '500' 62 | - id: y_min 63 | type: int 64 | initial_value: '500' 65 | - id: y_max 66 | type: int 67 | initial_value: '500' 68 | 69 | spi: 70 | - id: tft 71 | clk_pin: GPIO14 72 | mosi_pin: GPIO13 73 | miso_pin: GPIO12 74 | - id: touch 75 | clk_pin: GPIO25 76 | mosi_pin: GPIO32 77 | miso_pin: GPIO39 78 | 79 | display: 80 | - platform: ili9xxx 81 | model: ILI9341 82 | spi_id: tft 83 | cs_pin: GPIO15 84 | dc_pin: GPIO2 85 | invert_colors: false 86 | color_palette: 8BIT 87 | auto_clear_enabled: true 88 | rotation: 0 89 | dimensions: 90 | width: 240 91 | height: 320 92 | lambda: |- 93 | auto touch = id(spi_touchscreen)->get_touch(); 94 | if (touch) // or touch.has_value() 95 | it.filled_circle(touch.value().x, touch.value().y, 10, ha_blue); 96 | 97 | touchscreen: 98 | platform: xpt2046 99 | id: spi_touchscreen 100 | spi_id: touch 101 | cs_pin: GPIO33 102 | interrupt_pin: GPIO36 103 | calibration: 104 | x_min: 290 105 | x_max: 3940 106 | y_min: 298 107 | y_max: 3550 108 | transform: 109 | mirror_x: true 110 | on_touch: 111 | - lambda: |- 112 | const auto &t = touch; 113 | 114 | if (t.x_raw > id(x_max)) id(x_max) = t.x_raw; 115 | if (t.x_raw < id(x_min)) id(x_min) = t.x_raw; 116 | if (t.y_raw > id(y_max)) id(y_max) = t.y_raw; 117 | if (t.y_raw < id(y_min)) id(y_min) = t.y_raw; 118 | 119 | ESP_LOGI("cal", "x_min=%d x_max=%d y_min=%d y_max=%d", 120 | id(x_min), id(x_max), id(y_min), id(y_max)); 121 | -------------------------------------------------------------------------------- /cyd_container.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | device_name: cyd-container 3 | friendly_name: CYD Container 4 | wifi_ssid: !secret wifi_ssid 5 | wifi_pwd: !secret wifi_pwd 6 | thermostat_min: 5 7 | thermostat_max: 25 8 | 9 | esphome: 10 | name: $device_name 11 | friendly_name: $friendly_name 12 | 13 | esp32: 14 | board: esp32dev 15 | framework: 16 | type: esp-idf 17 | 18 | logger: 19 | # level: INFO 20 | level: DEBUG 21 | 22 | ota: 23 | platform: esphome 24 | password: !secret ota_pwd 25 | 26 | wifi: 27 | ssid: $wifi_ssid 28 | password: $wifi_pwd 29 | 30 | # Enable Home Assistant API 31 | api: 32 | encryption: 33 | key: !secret api_key 34 | 35 | ############################################################################### 36 | # i2c DHT20/ 37 | ############################################################################### 38 | 39 | i2c: 40 | sda: 27 41 | scl: 22 42 | scan: true 43 | id: bus_dht20 44 | 45 | ############################################################################### 46 | # Sensors 47 | ############################################################################### 48 | 49 | sensor: 50 | # - platform: homeassistant 51 | # id: thermostat_current_temperature 52 | # entity_id: sensor.thermostat_current_temperature 53 | # internal: true 54 | # on_value: 55 | # then: 56 | # - lvgl.label.update: 57 | # id: label_info_current_temp 58 | # text: 59 | # format: "%.1f °C" 60 | # args: [x] 61 | # - lvgl.label.update: 62 | # id: label_thermostat_current_temp 63 | # text: 64 | # format: "%.1f °C" 65 | # args: [x] 66 | # - platform: homeassistant 67 | # id: thermostat_target_temperature 68 | # entity_id: sensor.thermostat_current_temperature 69 | # internal: true 70 | 71 | # DHT20 72 | - platform: aht10 73 | variant: AHT20 74 | id: dht20 75 | i2c_id: bus_dht20 76 | address: 0x38 77 | update_interval: 15s 78 | temperature: 79 | name: Temperature 80 | on_value: 81 | then: 82 | - lvgl.label.update: 83 | id: label_info_current_temp 84 | text: 85 | format: "%.1f °C" 86 | args: [x] 87 | - lvgl.label.update: 88 | id: label_thermostat_current_temp 89 | text: 90 | format: "%.1f °C" 91 | args: [x] 92 | humidity: 93 | name: Humidity 94 | on_value: 95 | then: 96 | - lvgl.label.update: 97 | id: label_info_humidity 98 | text: 99 | format: "%.1f %%" 100 | args: [x] 101 | 102 | # Set up sensor for LDR (light meter) 103 | # - platform: adc 104 | # id: board_ldr 105 | # name: board_ldr 106 | # pin: GPIO34 107 | # update_interval: 10s 108 | # entity_category: diagnostic 109 | 110 | # Reports the light brightness in % 111 | # - platform: copy 112 | # id: ldr_percent 113 | # name: Light Percent 114 | # source_id: board_ldr 115 | # filters: 116 | # - lambda: return min(max((100 - (((x - 0.075) / (0.49 - 0.075)) * 100)), 0.0), 100.0); 117 | # unit_of_measurement: Light % 118 | # on_value: 119 | # then: 120 | # - lvgl.label.update: 121 | # id: label_info_ldr_percent 122 | # text: 123 | # format: "%.0f %%" 124 | # args: [x] 125 | 126 | # Reports the WiFi signal strength/RSSI in dB 127 | - platform: wifi_signal 128 | id: wifi_signal_db 129 | name: WiFi Signal dB 130 | update_interval: 60s 131 | entity_category: diagnostic 132 | 133 | # Reports the WiFi signal strength in % 134 | - platform: copy 135 | id: wifi_signal_percent 136 | name: WiFi Signal Percent 137 | source_id: wifi_signal_db 138 | filters: 139 | - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0); 140 | unit_of_measurement: Signal % 141 | entity_category: diagnostic 142 | on_value: 143 | then: 144 | - lvgl.label.update: 145 | id: label_info_wifi_signal_percent 146 | text: 147 | format: "%.0f %%" 148 | args: [x] 149 | 150 | # Thermostat arc 151 | - platform: lvgl 152 | id: lvgl_arc_thermostat_temp 153 | widget: arc_thermostat_temp 154 | internal: true 155 | on_value: 156 | then: 157 | - lvgl.label.update: 158 | id: label_thermostat_temp 159 | text: 160 | format: "%.0f °C" 161 | args: [x] 162 | # - homeassistant.action: 163 | # action: climate.set_temperature 164 | # data: 165 | # temperature: !lambda return x; 166 | # entity_id: climate.room_thermostat 167 | 168 | text_sensor: 169 | - platform: uptime 170 | id: uptime_text 171 | internal: true 172 | on_value: 173 | then: 174 | - lvgl.label.update: 175 | id: label_info_uptime 176 | text: !lambda return id(uptime_text).state; 177 | 178 | - platform: homeassistant 179 | id: thermostat_hvac_action 180 | entity_id: sensor.thermostat_hvac_action 181 | internal: true 182 | 183 | - platform: homeassistant 184 | id: thermostat_preset_mode 185 | entity_id: sensor.thermostat_preset_mode 186 | internal: true 187 | 188 | ############################################################################### 189 | # Hardware setup 190 | ############################################################################### 191 | 192 | output: 193 | - platform: ledc 194 | pin: GPIO21 195 | id: backlight_pwm 196 | 197 | light: 198 | - platform: monochromatic 199 | output: backlight_pwm 200 | name: Display Backlight 201 | id: backlight 202 | restore_mode: ALWAYS_ON 203 | 204 | spi: 205 | - id: tft 206 | clk_pin: GPIO14 207 | mosi_pin: GPIO13 208 | miso_pin: 209 | number: 12 210 | ignore_strapping_warning: true 211 | - id: touch 212 | clk_pin: GPIO25 213 | mosi_pin: GPIO32 214 | miso_pin: GPIO39 215 | 216 | # NOTE: much slower, and `color_depth: 8` doesn't seem to work 217 | # [E][display.mipi_spi:192]: Unsupported color depth or bit order 218 | # display: 219 | # - platform: mipi_spi 220 | # model: ILI9341 221 | # id: spi_display 222 | # spi_id: tft 223 | # cs_pin: 224 | # number: 15 225 | # ignore_strapping_warning: true 226 | # dc_pin: 227 | # number: 2 228 | # ignore_strapping_warning: true 229 | # invert_colors: false 230 | # color_depth: 16 231 | # dimensions: 232 | # width: 240 233 | # height: 320 234 | 235 | display: 236 | - platform: ili9xxx 237 | model: ILI9341 238 | id: spi_display 239 | spi_id: tft 240 | cs_pin: 241 | number: 15 242 | ignore_strapping_warning: true 243 | dc_pin: 244 | number: 2 245 | ignore_strapping_warning: true 246 | invert_colors: false 247 | color_palette: NONE 248 | auto_clear_enabled: false 249 | rotation: 0 250 | dimensions: 251 | width: 240 252 | height: 320 253 | 254 | touchscreen: 255 | platform: xpt2046 256 | id: spi_touchscreen 257 | spi_id: touch 258 | cs_pin: GPIO33 259 | interrupt_pin: GPIO36 260 | calibration: 261 | x_min: 290 262 | x_max: 3940 263 | y_min: 298 264 | y_max: 3550 265 | transform: 266 | mirror_x: true 267 | 268 | ############################################################################### 269 | # Colors 270 | ############################################################################### 271 | 272 | color: 273 | - id: state_climate_heat 274 | hex: ff6f22 275 | 276 | ############################################################################### 277 | # Fonts 278 | ############################################################################### 279 | 280 | font: 281 | - file: fonts/Orbitron-Regular.ttf 282 | id: orbitron_14 283 | size: 14 284 | bpp: 2 285 | glyphs: '!"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ abcdefghijklmnopqrstuvwxyzäöü' 286 | extras: 287 | - file: fonts/materialdesignicons-webfont.ttf 288 | glyphs: 289 | - "\U000F0140" # mdi-chevron-down 290 | - "\U000F050F" # mdi-thermometer 291 | - file: fonts/Orbitron-Regular.ttf 292 | id: orbitron_20 293 | size: 20 294 | bpp: 2 295 | glyphs: '!"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ abcdefghijklmnopqrstuvwxyzäöü' 296 | - file: fonts/materialdesignicons-webfont.ttf 297 | id: icons_32 298 | size: 32 299 | bpp: 4 300 | glyphs: 301 | - "\U000F0238" # mdi-fire 302 | - "\U000F02FC" # mdi-information 303 | - "\U000F050F" # mdi-thermometer 304 | - "\U000F0425" # mdi-power 305 | - "\U000F11EC" # mdi-plus-thick 306 | - "\U000F1639" # mdi-minus-thick 307 | - "\U000F05A9" # mdi-wifi 308 | 309 | ############################################################################### 310 | # LVGL UI 311 | ############################################################################### 312 | 313 | lvgl: 314 | displays: 315 | - spi_display 316 | touchscreens: 317 | - spi_touchscreen 318 | buffer_size: 25% 319 | disp_bg_color: black 320 | default_font: orbitron_14 321 | 322 | pages: 323 | - id: thermostat_page 324 | styles: page_style 325 | widgets: 326 | - container: 327 | align: TOP_MID 328 | width: 100% 329 | height: 280 330 | scrollable: false 331 | layout: 332 | type: FLEX 333 | flex_flow: COLUMN 334 | flex_align_cross: CENTER 335 | widgets: 336 | - label: 337 | text: Heating 338 | text_font: orbitron_20 339 | - container: 340 | align: CENTER 341 | width: 100% 342 | height: 120 343 | scrollable: false 344 | widgets: 345 | - arc: 346 | id: arc_thermostat_temp 347 | align: CENTER 348 | y: 16 349 | height: 150 350 | pad_top: 6 351 | pad_right: 6 352 | pad_left: 6 353 | adjustable: true 354 | min_value: $thermostat_min 355 | max_value: $thermostat_max 356 | - label: 357 | id: label_thermostat_hvac_action 358 | align: CENTER 359 | y: -24 360 | text: "Idle" 361 | - label: 362 | id: label_thermostat_temp 363 | align: CENTER 364 | y: -2 365 | text_font: orbitron_20 366 | text: "21 °C" 367 | - label: 368 | align: CENTER 369 | x: -30 370 | y: 22 371 | text: "\U000F050F" 372 | - label: 373 | id: label_thermostat_current_temp 374 | align: CENTER 375 | x: 8 376 | y: 20 377 | text: " " 378 | - container: 379 | align: CENTER 380 | width: 100% 381 | height: SIZE_CONTENT 382 | pad_all: 0 383 | y: 10 384 | layout: 385 | type: FLEX 386 | flex_flow: ROW 387 | flex_align_main: SPACE_BETWEEN 388 | widgets: 389 | - button: 390 | styles: arc_button_style 391 | # on_click: 392 | # - lvgl.spinbox.decrement: spinbox_thermostat_temp 393 | widgets: 394 | - label: 395 | text: "\U000F1639" 396 | text_font: icons_32 397 | - button: 398 | styles: arc_button_style 399 | # on_click: 400 | # - lvgl.spinbox.increment: spinbox_thermostat_temp 401 | widgets: 402 | - label: 403 | text: "\U000F11EC" 404 | text_font: icons_32 405 | - container: 406 | align: CENTER 407 | width: 100% 408 | height: SIZE_CONTENT 409 | pad_top: 4 410 | pad_bottom: 14 411 | layout: 412 | type: FLEX 413 | flex_flow: ROW 414 | flex_align_main: SPACE_EVENLY 415 | widgets: 416 | - button: 417 | styles: thermostat_button_style 418 | widgets: 419 | - label: 420 | text: "\U000F0425" 421 | text_font: icons_32 422 | - button: 423 | styles: thermostat_button_style 424 | state: 425 | checked: true 426 | checked: 427 | bg_color: state_climate_heat 428 | bg_grad_dir: NONE 429 | border_color: state_climate_heat 430 | widgets: 431 | - label: 432 | text: "\U000F0238" 433 | text_font: icons_32 434 | - container: 435 | align: CENTER 436 | width: 100% 437 | height: SIZE_CONTENT 438 | layout: 439 | type: FLEX 440 | flex_flow: ROW 441 | flex_align_main: SPACE_EVENLY 442 | flex_align_cross: CENTER 443 | widgets: 444 | - label: 445 | text: Preset 446 | - dropdown: 447 | id: dropdown_thermostat_presets 448 | pad_left: 16 449 | pad_right: 16 450 | dir: TOP 451 | symbol: "\U000F0140" # chevron-down 452 | options: 453 | - None 454 | - Away 455 | - Comfort 456 | selected_index: 0 457 | 458 | - id: wifi_page 459 | styles: page_style 460 | widgets: 461 | - container: 462 | align: TOP_MID 463 | width: 240 464 | height: 280 465 | scrollable: false 466 | layout: 467 | type: flex 468 | flex_flow: column 469 | flex_align_cross: stretch 470 | flex_align_track: center 471 | pad_row: 6 472 | widgets: 473 | - label: 474 | text: Wi-Fi 475 | text_font: orbitron_20 476 | - qrcode: 477 | size: 170 478 | light_color: white 479 | dark_color: black 480 | text: "WIFI:T:WPA;S:$wifi_ssid;P:$wifi_pwd;" 481 | - label: 482 | text: $wifi_ssid 483 | text_font: orbitron_20 484 | - label: 485 | text: $wifi_pwd 486 | text_font: orbitron_20 487 | 488 | - id: info_page 489 | styles: page_style 490 | widgets: 491 | - container: 492 | align: TOP_MID 493 | width: 240 494 | height: 280 495 | scrollable: false 496 | layout: 497 | type: grid 498 | grid_rows: [CONTENT, fr(1), fr(1), fr(1), fr(1), fr(1)] 499 | grid_columns: [fr(1), fr(1)] 500 | pad_row: 16 501 | widgets: 502 | - label: 503 | text: Info 504 | text_font: orbitron_20 505 | text_align: CENTER 506 | width: 100% 507 | grid_cell_row_pos: 0 508 | grid_cell_column_pos: 0 509 | grid_cell_column_span: 2 510 | - label: 511 | text: Uptime 512 | grid_cell_row_pos: 1 513 | grid_cell_column_pos: 0 514 | - label: 515 | id: label_info_uptime 516 | text: " " 517 | grid_cell_row_pos: 1 518 | grid_cell_column_pos: 1 519 | grid_cell_x_align: END 520 | - label: 521 | text: Wifi signal strength 522 | grid_cell_row_pos: 2 523 | grid_cell_column_pos: 0 524 | - label: 525 | id: label_info_wifi_signal_percent 526 | text: " " 527 | grid_cell_row_pos: 2 528 | grid_cell_column_pos: 1 529 | grid_cell_x_align: END 530 | # - label: 531 | # text: LDR 532 | # grid_cell_row_pos: 3 533 | # grid_cell_column_pos: 0 534 | # - label: 535 | # id: label_info_ldr_percent 536 | # text: " " 537 | # grid_cell_row_pos: 3 538 | # grid_cell_column_pos: 1 539 | # grid_cell_x_align: END 540 | - label: 541 | text: Temperature 542 | grid_cell_row_pos: 3 543 | grid_cell_column_pos: 0 544 | - label: 545 | id: label_info_current_temp 546 | text: " " 547 | grid_cell_row_pos: 3 548 | grid_cell_column_pos: 1 549 | grid_cell_x_align: END 550 | - label: 551 | text: Humidity 552 | grid_cell_row_pos: 4 553 | grid_cell_column_pos: 0 554 | - label: 555 | id: label_info_humidity 556 | text: " " 557 | grid_cell_row_pos: 4 558 | grid_cell_column_pos: 1 559 | grid_cell_x_align: END 560 | 561 | top_layer: 562 | widgets: 563 | - container: 564 | align: BOTTOM_MID 565 | height: 48 566 | width: 100% 567 | layout: 568 | type: flex 569 | flex_flow: row 570 | flex_align_main: SPACE_EVENLY 571 | widgets: 572 | - button: 573 | id: btn_thermostat 574 | styles: nav_button_style 575 | state: 576 | checked: true 577 | widgets: 578 | - label: 579 | text: "\U000F050F" 580 | on_press: 581 | then: 582 | - lvgl.page.show: thermostat_page 583 | - lvgl.widget.update: 584 | id: btn_thermostat 585 | state: 586 | checked: true 587 | - lvgl.widget.update: 588 | id: btn_wifi 589 | state: 590 | checked: false 591 | - lvgl.widget.update: 592 | id: btn_info 593 | state: 594 | checked: false 595 | - button: 596 | id: btn_wifi 597 | styles: nav_button_style 598 | widgets: 599 | - label: 600 | text: "\U000F05A9" 601 | on_press: 602 | then: 603 | - lvgl.page.show: wifi_page 604 | - lvgl.widget.update: 605 | id: btn_thermostat 606 | state: 607 | checked: false 608 | - lvgl.widget.update: 609 | id: btn_wifi 610 | state: 611 | checked: true 612 | - lvgl.widget.update: 613 | id: btn_info 614 | state: 615 | checked: false 616 | - button: 617 | id: btn_info 618 | styles: nav_button_style 619 | widgets: 620 | - label: 621 | text: "\U000F02FC" 622 | on_press: 623 | then: 624 | - lvgl.page.show: info_page 625 | - lvgl.widget.update: 626 | id: btn_thermostat 627 | state: 628 | checked: false 629 | - lvgl.widget.update: 630 | id: btn_wifi 631 | state: 632 | checked: false 633 | - lvgl.widget.update: 634 | id: btn_info 635 | state: 636 | checked: true 637 | 638 | theme: 639 | label: 640 | text_color: white 641 | button: 642 | bg_color: 0x131313 643 | bg_grad_color: 0x212121 644 | bg_grad_dir: VER 645 | bg_opa: COVER 646 | border_color: 0xcccccc 647 | border_width: 1 648 | checked: 649 | text_color: white 650 | bg_color: 0x494949 651 | bg_grad_color: 0x787878 652 | border_color: white 653 | pressed: 654 | text_color: white 655 | bg_color: 0x676767 656 | bg_grad_color: 0x989898 657 | border_color: white 658 | dropdown: 659 | text_color: white 660 | bg_color: 0x131313 661 | bg_grad_color: 0x212121 662 | bg_grad_dir: VER 663 | bg_opa: COVER 664 | border_color: 0xcccccc 665 | border_width: 1 666 | pressed: 667 | bg_color: 0x676767 668 | bg_grad_color: 0x989898 669 | border_color: white 670 | 671 | style_definitions: 672 | - id: nav_button_style 673 | height: 48 674 | text_font: icons_32 675 | - id: arc_button_style 676 | pad_top: 4 677 | pad_right: 6 678 | pad_bottom: 4 679 | pad_left: 6 680 | radius: 36 681 | - id: thermostat_button_style 682 | pad_top: 8 683 | pad_right: 12 684 | pad_bottom: 8 685 | pad_left: 12 686 | - id: page_style 687 | bg_color: black 688 | scrollbar_mode: "OFF" 689 | --------------------------------------------------------------------------------