Files
dashboard/dashboard/ui/climate.slint
2026-08-09 22:24:01 +02:00

199 lines
6.5 KiB
Plaintext

import { Palette, HorizontalBox, VerticalBox } from "std-widgets.slint";
import { Icons } from "material/ui/icons/icons.slint";
import {
ListTile,
Vertical,
ElevatedButton,
SegmentedButton,
MaterialPalette,
FilledButton,
MaterialText,
MaterialTypography,
MaterialStyleMetrics,
} from "material/material.slint";
export struct WeatherData {
icon-index: int,
label: string,
temperature: float,
temperature_unit: string,
}
export struct ThermostatData {
id: string,
name: string,
current: float,
temperature: float,
unit: string,
target: int,
remainder: int,
state: string,
available: bool,
}
export component Climate inherits Rectangle {
in property <WeatherData> weather;
in property <[ThermostatData]> thermostats;
in property <bool> is-night;
in property <string> next-event-date;
in property <string> next-event-time;
in property <string> next-event-summary;
property <int> icon-index: weather.icon-index == 1 && is-night ? 2 : weather.icon-index;
callback set_temperature(string, float);
callback toggle_thermostat(string);
property <[image]> icons: [
@image-url("icons/sun.svg"),
@image-url("icons/cloud-sun.svg"),
@image-url("icons/cloud-moon.svg"),
@image-url("icons/cloud.svg"),
@image-url("icons/cloud-rain.svg"),
@image-url("icons/cloud-rain-wind.svg"),
@image-url("icons/cloud-lightning.svg"),
@image-url("icons/snowflake.svg"),
@image-url("icons/cloud-fog.svg"),
@image-url("icons/moon.svg"),
];
VerticalLayout {
spacing: MaterialStyleMetrics.spacing_16;
HorizontalLayout {
Vertical {
ListTile {
avatar_icon: Icons.event_upcoming;
avatar_foreground: Palette.foreground;
text: next-event-date;
supporting-text: next-event-summary;
height: 72px;
enabled: false;
VerticalLayout {
alignment: center;
horizontal_stretch: 1;
MaterialText {
text: next-event-time;
color: MaterialPalette.on_surface;
overflow: elide;
horizontal-alignment: right;
style: MaterialTypography.title_medium;
}
MaterialText {
text: "";
overflow: elide;
horizontal-alignment: right;
style: MaterialTypography.body_medium;
}
}
}
}
Vertical {
Text {
text: "\{weather.temperature}\{weather.temperature-unit}";
font-size: 36px;
font-weight: 700;
vertical-alignment: center;
horizontal-alignment: right;
}
Text {
text: weather.label;
font-size: 24px;
font-weight: 500;
vertical-alignment: center;
horizontal-alignment: right;
}
}
Image {
colorize: Palette.foreground;
width: 120px;
height: 105px;
source: icons[icon-index];
image-fit: contain;
}
}
for thermostat[index] in thermostats: ListTile {
avatar_icon: Icons.thermostat;
avatar_foreground: Palette.foreground;
text: thermostat.name;
supporting-text: thermostat.available ? thermostat.state : "UNAVAILABLE";
height: 72px;
enabled: false;
ElevatedButton {
icon: Icons.remove;
clicked => { root.set_temperature(thermostat.id, thermostat.temperature - 0.5) }
}
Rectangle {
width: 80px;
VerticalLayout {
HorizontalLayout {
Text {
text: thermostat.target;
font-size: 40px;
vertical-alignment: center;
horizontal-alignment: right;
font-weight: 700;
height: 40px;
}
VerticalLayout {
padding-top: 2px;
Text {
text: thermostat.unit;
font-size: 14px;
horizontal-alignment: left;
vertical-alignment: top;
font-weight: 500;
height: 18px;
}
Text {
text: ".\{thermostat.remainder}";
// color: MaterialPalette.primary;
font-size: 14px;
horizontal-alignment: left;
vertical-alignment: bottom;
font-weight: 500;
height: 18px;
}
}
}
Text {
text: "\{thermostat.current} \{thermostat.unit}";
color: MaterialPalette.secondary;
font-size: 12px;
vertical-alignment: top;
horizontal-alignment: center;
font-weight: 500;
}
}
}
ElevatedButton {
icon: Icons.add;
clicked => { root.set_temperature(thermostat.id, thermostat.temperature + 0.5) }
}
if thermostat.state == "OFF": ElevatedButton {
icon: Icons.mode_heat_off;
clicked => { root.toggle_thermostat(thermostat.id) }
}
if thermostat.state == "HEAT": FilledButton {
icon: Icons.mode_heat;
clicked => { root.toggle_thermostat(thermostat.id) }
}
}
Rectangle { }
}
}