1786380840
This commit is contained in:
@@ -297,7 +297,7 @@ impl HaClient {
|
||||
}
|
||||
}
|
||||
upcoming.sort_by_key(|event| event.sort_epoch);
|
||||
upcoming.truncate(7);
|
||||
upcoming.truncate(6);
|
||||
Ok(upcoming)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,44 +9,8 @@ import {
|
||||
} from "material/material.slint";
|
||||
import { Icons } from "material/ui/icons/icons.slint";
|
||||
|
||||
export struct CalendarEventData {
|
||||
id: string,
|
||||
date: string,
|
||||
time: string,
|
||||
summary: string,
|
||||
calendar: string,
|
||||
}
|
||||
|
||||
export component Calendar inherits Rectangle {
|
||||
in property <[CalendarEventData]> calendar-events;
|
||||
|
||||
VerticalLayout {
|
||||
for event[index] in calendar-events: ListTile {
|
||||
avatar_icon: Icons.calendar_today;
|
||||
avatar_foreground: Palette.foreground;
|
||||
text: event.date;
|
||||
supporting-text: event.summary;
|
||||
height: 72px;
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
horizontal_stretch: 1;
|
||||
MaterialText {
|
||||
text: event.time;
|
||||
horizontal-alignment: right;
|
||||
color: MaterialPalette.on_surface;
|
||||
overflow: elide;
|
||||
style: MaterialTypography.title_medium;
|
||||
}
|
||||
|
||||
MaterialText {
|
||||
text: "";
|
||||
horizontal-alignment: right;
|
||||
overflow: elide;
|
||||
style: MaterialTypography.body_medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { }
|
||||
}
|
||||
Rectangle { }
|
||||
}
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,14 @@ import {
|
||||
StandardTableView,
|
||||
} from "std-widgets.slint";
|
||||
|
||||
import { Climate, WeatherData, ThermostatData } from "./climate.slint";
|
||||
import { CalendarEventData } from "./calendar.slint";
|
||||
import {
|
||||
CalendarEventData,
|
||||
MainPage,
|
||||
WeatherData,
|
||||
ThermostatData,
|
||||
} from "./main_page.slint";
|
||||
|
||||
// import { } from "./calendar.slint";
|
||||
|
||||
import { Calendar } from "calendar.slint";
|
||||
import { Energy } from "energy.slint";
|
||||
@@ -181,10 +187,8 @@ export component AppWindow inherits Window {
|
||||
Rectangle {
|
||||
width: (root.width / 4) * 3;
|
||||
VerticalLayout {
|
||||
if tab-bar.current-index == 0: Climate {
|
||||
next-event-date: next-event-date;
|
||||
next-event-time: next-event-time;
|
||||
next-event-summary: next-event-summary;
|
||||
MainPage {
|
||||
calendar-events: calendar-events;
|
||||
|
||||
thermostats: thermostats;
|
||||
is-night: is-night;
|
||||
@@ -193,24 +197,16 @@ export component AppWindow inherits Window {
|
||||
toggle_thermostat(entity_id) => { toggle_thermostat(entity_id) }
|
||||
}
|
||||
|
||||
if tab-bar.current-index == 1: Calendar {
|
||||
calendar-events: calendar-events;
|
||||
}
|
||||
|
||||
if tab-bar.current-index == 2: Energy {
|
||||
render-plot(iteration) => render-plot(iteration);
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
tab-bar := TabBar {
|
||||
width: parent.width;
|
||||
items: [
|
||||
{ icon: Icons.wb_sunny, text: "Climate" },
|
||||
{ icon: Icons.calendar_today, text: "Calendar" },
|
||||
{ icon: Icons.energy_savings_leaf, text: "Energy" }
|
||||
];
|
||||
}
|
||||
}
|
||||
// Rectangle {
|
||||
// tab-bar := TabBar {
|
||||
// width: parent.width;
|
||||
// items: [
|
||||
// { icon: Icons.wb_sunny, text: "Climate" },
|
||||
// { icon: Icons.calendar_today, text: "Calendar" },
|
||||
// { icon: Icons.energy_savings_leaf, text: "Energy" }
|
||||
// ];
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
241
dashboard/ui/main_page.slint
Normal file
241
dashboard/ui/main_page.slint
Normal file
@@ -0,0 +1,241 @@
|
||||
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 struct CalendarEventData {
|
||||
id: string,
|
||||
date: string,
|
||||
time: string,
|
||||
summary: string,
|
||||
calendar: string,
|
||||
}
|
||||
|
||||
export component MainPage inherits Rectangle {
|
||||
in property <WeatherData> weather;
|
||||
in property <[ThermostatData]> thermostats;
|
||||
in property <bool> is-night;
|
||||
|
||||
in property <[CalendarEventData]> calendar-events;
|
||||
|
||||
// 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 {
|
||||
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;
|
||||
enabled: thermostat.available;
|
||||
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;
|
||||
enabled: thermostat.available;
|
||||
clicked => { root.set_temperature(thermostat.id, thermostat.temperature + 0.5) }
|
||||
}
|
||||
|
||||
if thermostat.state == "OFF": ElevatedButton {
|
||||
icon: Icons.mode_heat_off;
|
||||
enabled: thermostat.available;
|
||||
clicked => { root.toggle_thermostat(thermostat.id) }
|
||||
}
|
||||
|
||||
if thermostat.state == "HEAT": FilledButton {
|
||||
icon: Icons.mode_heat;
|
||||
clicked => { root.toggle_thermostat(thermostat.id) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
VerticalLayout {
|
||||
for event[index] in calendar-events: ListTile {
|
||||
avatar_icon: Icons.calendar_today;
|
||||
avatar_foreground: Palette.foreground;
|
||||
text: event.date;
|
||||
supporting-text: event.summary;
|
||||
height: 72px;
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
horizontal_stretch: 1;
|
||||
MaterialText {
|
||||
text: event.time;
|
||||
horizontal-alignment: right;
|
||||
color: MaterialPalette.on_surface;
|
||||
overflow: elide;
|
||||
style: MaterialTypography.title_medium;
|
||||
}
|
||||
|
||||
MaterialText {
|
||||
text: "";
|
||||
horizontal-alignment: right;
|
||||
overflow: elide;
|
||||
style: MaterialTypography.body_medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user