1786298825

This commit is contained in:
2026-08-09 20:07:05 +02:00
parent bc19895faa
commit b8e235a93e
10 changed files with 475 additions and 140 deletions

226
Cargo.lock generated
View File

@@ -346,6 +346,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]]
name = "bincode"
version = "2.0.1"
@@ -684,6 +690,35 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "cookie"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie_store"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
dependencies = [
"cookie",
"document-features",
"idna",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "copypasta"
version = "0.10.2"
@@ -849,12 +884,14 @@ dependencies = [
"dotenvy",
"hass-rs",
"paste",
"serde",
"serde_json",
"slint",
"slint-build",
"thiserror 2.0.20",
"tokio",
"trekstor",
"ureq",
"url",
]
@@ -870,6 +907,12 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
[[package]]
name = "derive_more"
version = "2.1.1"
@@ -950,6 +993,15 @@ dependencies = [
"libloading",
]
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]]
name = "dotenvy"
version = "0.15.7"
@@ -1445,6 +1497,17 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
@@ -2513,6 +2576,12 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -2788,6 +2857,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-derive"
version = "0.4.2"
@@ -3459,6 +3534,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@@ -3841,6 +3922,20 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rowan"
version = "0.16.1"
@@ -3922,6 +4017,41 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.23"
@@ -4352,6 +4482,12 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svgtypes"
version = "0.16.1"
@@ -4521,6 +4657,36 @@ dependencies = [
"zune-jpeg",
]
[[package]]
name = "time"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
name = "time-macros"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tiny-skia"
version = "0.12.0"
@@ -4855,12 +5021,49 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "unty"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
[[package]]
name = "ureq"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d"
dependencies = [
"base64 0.23.1",
"cookie_store",
"log",
"percent-encoding",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"ureq-proto",
"utf8-zero",
"webpki-roots",
]
[[package]]
name = "ureq-proto"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613"
dependencies = [
"base64 0.23.1",
"http",
"httparse",
"log",
]
[[package]]
name = "url"
version = "2.5.8"
@@ -4879,7 +5082,7 @@ version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d46cf96c5f498d36b7a9693bc6a7075c0bb9303189d61b2249b0dc3d309c07de"
dependencies = [
"base64",
"base64 0.22.1",
"data-url",
"flate2",
"fontdb",
@@ -4901,6 +5104,12 @@ dependencies = [
"xmlwriter",
]
[[package]]
name = "utf8-zero"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -5074,6 +5283,15 @@ dependencies = [
"web-sys",
]
[[package]]
name = "webpki-roots"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.12"
@@ -5694,6 +5912,12 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
[[package]]
name = "zerotrie"
version = "0.2.4"

View File

@@ -14,6 +14,8 @@ url = "2.5.8"
chrono = "0.4.45"
thiserror = "2.0.20"
paste = "1.0.15"
ureq = { version = "3.4.0", default-features = false, features = ["json", "rustls"] }
serde = { version = "1.0.229", features = ["derive"] }
[features]
dev = ["slint/backend-winit-x11"]

View File

@@ -1,4 +1,3 @@
#[allow(clippy::all)]
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("HASS_URL is not set")]

View File

@@ -44,7 +44,7 @@ impl HassEntityExt for HassEntity {
impl From<&HassEntity> for LightData {
fn from(value: &HassEntity) -> Self {
Self {
id: value.entity_id.clone().into(),
id: value.entity_id.to_shared_string(),
name: value
.friendly_name()
.replace(" Lights", "")
@@ -61,7 +61,7 @@ impl From<&HassEntity> for ThermometerData {
let v: f32 = value.state.parse().unwrap_or_default();
Self {
id: value.entity_id.clone().into(),
id: value.entity_id.to_shared_string(),
name: value.friendly_name().into(),
value: format!("{:.1}", v).into(),
unit: value.unit_of_measurement().into(),
@@ -71,21 +71,19 @@ impl From<&HassEntity> for ThermometerData {
impl From<&HassEntity> for DateTimeData {
fn from(value: &HassEntity) -> Self {
let mut data = Self {
id: value.entity_id.to_shared_string(),
..Default::default()
};
if let Ok(datetime) =
chrono::NaiveDateTime::parse_from_str(&value.state, "%Y-%m-%dT%H:%M:%S")
{
return Self {
id: value.entity_id.clone().into(),
date: datetime.format("%a %m %b").to_shared_string(),
time: datetime.format("%H:%M").to_shared_string(),
};
data.date = datetime.format("%a %m %b").to_shared_string();
data.time = datetime.format("%H:%M").to_shared_string();
}
Self {
id: value.entity_id.clone().into(),
date: "0-0-0000".into(),
time: "00:00".into(),
}
data
}
}
@@ -139,64 +137,6 @@ impl From<&HassEntity> for WeatherData {
let label = pretty_label(&value.state).to_shared_string();
let attribution = value
.attributes
.get("attribution")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_shared_string();
let cloud_coverage = value
.attributes
.get("cloud_coverage")
.and_then(|x| x.as_i64())
.unwrap_or_default() as i32;
let dew_point = value
.attributes
.get("dew_point")
.and_then(|x| x.as_f64())
.unwrap_or_default() as f32;
let friendly_name = value
.attributes
.get("friendly_name")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_shared_string();
let humidity = value
.attributes
.get("humidity")
.and_then(|x| x.as_i64())
.unwrap_or_default() as i32;
let precipitation_unit = value
.attributes
.get("precipitation_unit")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_shared_string();
let pressure = value
.attributes
.get("pressure")
.and_then(|x| x.as_f64())
.unwrap_or_default() as f32;
let pressure_unit = value
.attributes
.get("pressure_unit")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_shared_string();
let supported_features = value
.attributes
.get("supported_features")
.and_then(|x| x.as_i64())
.unwrap_or_default() as i32;
let temperature = value
.attributes
.get("temperature")
@@ -210,57 +150,24 @@ impl From<&HassEntity> for WeatherData {
.unwrap_or_default()
.to_shared_string();
let uv_index = value
.attributes
.get("uv_index")
.and_then(|x| x.as_f64())
.unwrap_or_default() as f32;
let visibility_unit = value
.attributes
.get("visibility_unit")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_shared_string();
let wind_bearing = value
.attributes
.get("wind_bearing")
.and_then(|x| x.as_f64())
.unwrap_or_default() as f32;
let wind_speed = value
.attributes
.get("wind_speed")
.and_then(|x| x.as_f64())
.unwrap_or_default() as f32;
let wind_speed_unit = value
.attributes
.get("wind_speed_unit")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_shared_string();
Self {
icon_index,
label,
attribution,
cloud_coverage,
dew_point,
friendly_name,
humidity,
precipitation_unit,
pressure,
pressure_unit,
supported_features,
temperature,
temperature_unit,
uv_index,
visibility_unit,
wind_bearing,
wind_speed,
wind_speed_unit,
}
}
}
// weird spot for this but okay
impl From<&crate::home_assistant::CalendarEvent> for CalendarEventData {
fn from(value: &crate::home_assistant::CalendarEvent) -> Self {
Self {
calendar: value.calendar.to_shared_string(),
date: value.date.to_shared_string(),
id: value.entity_id.to_shared_string(),
summary: value.summary.to_shared_string(),
time: value.time.to_shared_string(),
}
}
}

View File

@@ -1,8 +1,13 @@
use chrono::{DateTime, Local, NaiveDate};
use dotenvy::var;
use hass_rs::{HassClient, HassEntity};
use serde::Deserialize;
use serde_json::json;
use std::collections::HashMap;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use std::{collections::HashMap, time::Duration};
use tokio::{
sync::mpsc::{UnboundedReceiver, UnboundedSender},
time::sleep,
};
use crate::{
error::AppError,
@@ -114,6 +119,18 @@ pub async fn hass(
initial_state.sort_by(|a, b| a.entity_id.cmp(&b.entity_id));
s2c_tx.send(EventMessage::InitialState(initial_state)).ok();
tokio::spawn({
let s2c_tx = s2c_tx.clone();
async move {
loop {
let ha_client = HaClient::from_environment().unwrap();
let events = ha_client.fetch_calendar_events().unwrap();
s2c_tx.send(EventMessage::CalendarUpdated(events)).ok();
sleep(Duration::from_secs(30)).await;
}
}
});
let mut event_receiver = client
.subscribe_event("state_changed")
.await
@@ -162,3 +179,143 @@ fn toggle_action(current_state: &str, on_state: &str) -> &'static str {
"turn_on"
}
}
const REQUEST_TIMEOUT: Duration = Duration::from_secs(12);
#[derive(Clone)]
pub struct HaClient {
pub agent: ureq::Agent,
pub base_url: String,
pub token: String,
}
#[derive(Deserialize)]
pub struct HaCalendar {
pub entity_id: String,
pub name: String,
}
#[derive(Deserialize)]
pub struct HaCalendarEvent {
pub uid: String,
pub summary: String,
pub start: HaCalendarTime,
}
#[derive(Deserialize)]
pub struct HaCalendarTime {
#[serde(rename = "dateTime")]
pub date_time: Option<String>,
pub date: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CalendarEvent {
pub entity_id: String,
pub summary: String,
pub date: String,
pub time: String,
pub calendar: String,
pub sort_epoch: i64,
}
impl HaClient {
pub fn from_environment() -> Result<Self, String> {
let base_url = std::env::var("HASS_URL")
.map_err(|_| "HASS_URL is not configured".to_string())?
.trim_end_matches('/')
.to_string();
let token =
std::env::var("HASS_TOKEN").map_err(|_| "HASS_TOKEN is not configured".to_string())?;
let agent = ureq::Agent::new_with_config(
ureq::config::Config::builder()
.timeout_send_request(Some(REQUEST_TIMEOUT))
.timeout_recv_response(Some(REQUEST_TIMEOUT))
.timeout_send_body(Some(REQUEST_TIMEOUT))
.build(),
);
Ok(Self {
agent,
base_url,
token,
})
}
fn get_calendars(&self) -> Result<Vec<HaCalendar>, String> {
self.agent
.get(&format!("{}/api/calendars", self.base_url))
.header("Authorization", &format!("Bearer {}", self.token))
.header("Accept", "application/json")
.call()
.map_err(|error| format!("calendars: {error}"))?
.body_mut()
.read_json()
.map_err(|error| format!("calendars: invalid response: {error}"))
}
fn get_calendar_events(
&self,
entity_id: &str,
start: &str,
end: &str,
) -> Result<Vec<HaCalendarEvent>, String> {
self.agent
.get(&format!("{}/api/calendars/{entity_id}", self.base_url))
.query("start", start)
.query("end", end)
.header("Authorization", &format!("Bearer {}", self.token))
.header("Accept", "application/json")
.call()
.map_err(|error| format!("{entity_id}: {error}"))?
.body_mut()
.read_json()
.map_err(|error| format!("{entity_id}: invalid response: {error}"))
}
pub fn fetch_calendar_events(&self) -> Result<Vec<CalendarEvent>, String> {
let now = Local::now();
let start = now.to_rfc3339();
let end = (now + chrono::Duration::days(30)).to_rfc3339();
let mut upcoming = Vec::new();
for calendar in self.get_calendars()? {
let Ok(events) = self.get_calendar_events(&calendar.entity_id, &start, &end) else {
continue;
};
for event in events {
let Some((sort_epoch, date, time)) = format_calendar_time(&event.start) else {
continue;
};
upcoming.push(CalendarEvent {
entity_id: event.uid,
summary: event.summary,
date,
time,
calendar: calendar.name.clone(),
sort_epoch,
});
}
}
upcoming.sort_by_key(|event| event.sort_epoch);
upcoming.truncate(7);
Ok(upcoming)
}
}
pub fn format_calendar_time(value: &HaCalendarTime) -> Option<(i64, String, String)> {
if let Some(date_time) = value.date_time.as_deref() {
let parsed = DateTime::parse_from_rfc3339(date_time).ok()?;
return Some((
parsed.timestamp(),
parsed.format("%a %d %b").to_string().to_uppercase(),
parsed.format("%H:%M").to_string().to_uppercase(),
));
}
let date = NaiveDate::parse_from_str(value.date.as_deref()?, "%Y-%m-%d").ok()?;
let sort_epoch = date.and_hms_opt(0, 0, 0)?.and_utc().timestamp();
Some((
sort_epoch,
date.format("%a %d %b").to_string().to_uppercase(),
"ALL DAY".into(),
))
}

View File

@@ -1,5 +1,7 @@
use hass_rs::HassEntity;
use crate::home_assistant::CalendarEvent;
#[derive(Clone)]
pub enum CommandMessage {
ToggleLight { entity_id: String },
@@ -14,4 +16,5 @@ pub enum EventMessage {
InitialState(Vec<HassEntity>),
EntityUpdated(HassEntity),
EntityRemoved(HassEntity),
CalendarUpdated(Vec<CalendarEvent>),
}

View File

@@ -2,6 +2,7 @@ slint::include_modules!();
use crate::{
ha_ext::*,
home_assistant::CalendarEvent,
messages::{CommandMessage, EventMessage},
};
@@ -96,6 +97,18 @@ pub fn apply_thermostats(app: &AppWindow, entities: &[HassEntity]) {
});
}
pub fn apply_calendars(app: &Weak<AppWindow>, entities: Vec<CalendarEvent>) {
if let Err(err) = app.upgrade_in_event_loop(move |app| {
let mut entities = entities.clone();
entities.sort_by_key(|a| a.sort_epoch);
let entities = entities.iter().map(Into::into).collect::<Vec<_>>();
app.set_calendar_events(ModelRc::new(VecModel::<CalendarEventData>::from(entities)));
}) {
eprintln!("Failed to upgrade in event loop.\n\t{:?}", err);
}
}
pub fn apply_state(app: &Weak<AppWindow>, entities: Vec<HassEntity>) {
if let Err(err) = app.upgrade_in_event_loop(move |app| {
apply_theme(&app, &entities);
@@ -161,6 +174,9 @@ pub async fn update_listener(
EventMessage::EntityRemoved(hass_entity) => {
apply_state(&app_weak, vec![hass_entity]);
}
EventMessage::CalendarUpdated(events) => {
apply_calendars(&app_weak, events);
}
}
}
}

View File

@@ -1,7 +1,41 @@
import { FilledButton, SnackBar } from "material/material.slint";
import { Palette } from "std-widgets.slint";
import {
FilledButton,
SnackBar,
ListTile,
MaterialText,
MaterialPalette,
MaterialTypography,
} 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;
MaterialText {
text: event.time;
color: MaterialPalette.on_surface;
overflow: elide;
style: MaterialTypography.title_medium;
}
}
Rectangle { }
}
}

View File

@@ -1,4 +1,5 @@
import { Palette, HorizontalBox, VerticalBox } from "std-widgets.slint";
import { Icons } from "material/ui/icons/icons.slint";
import {
ListTile,
Vertical,
@@ -7,26 +8,12 @@ import {
MaterialPalette,
FilledButton,
} from "material/material.slint";
import { Icons } from "material/ui/icons/icons.slint";
export struct WeatherData {
icon-index: int,
label: string,
temperature: float,
dew_point: float,
temperature_unit: string,
humidity: int,
cloud_coverage: int,
uv_index: float,
pressure: float,
pressure_unit: string,
wind_bearing: float,
wind_speed: float,
wind_speed_unit: string,
visibility_unit: string,
precipitation_unit: string,
attribution: string,
friendly_name: string,
supported_features: int,
}
export struct ThermostatData {
@@ -114,7 +101,6 @@ export component Climate inherits Rectangle {
HorizontalLayout {
Text {
text: thermostat.target;
// color: MaterialPalette.primary;
font-size: 40px;
vertical-alignment: center;
horizontal-alignment: right;
@@ -126,7 +112,6 @@ export component Climate inherits Rectangle {
padding-top: 2px;
Text {
text: thermostat.unit;
// color: MaterialPalette.primary;
font-size: 14px;
horizontal-alignment: left;
vertical-alignment: top;

View File

@@ -9,6 +9,8 @@ import {
} from "std-widgets.slint";
import { Climate, WeatherData, ThermostatData } from "./climate.slint";
import { CalendarEventData } from "./calendar.slint";
import { Calendar } from "calendar.slint";
import { Energy } from "energy.slint";
import {
@@ -132,6 +134,8 @@ export component AppWindow inherits Window {
callback set_temperature(string, float);
callback toggle_thermostat(string);
in property <[CalendarEventData]> calendar-events;
in property <[ThermometerData]> thermometers;
in property <[LightData]> lights;
@@ -177,7 +181,11 @@ export component AppWindow inherits Window {
set_temperature(entity_id, value) => { root.set_temperature(entity_id, value); }
toggle_thermostat(entity_id) => { toggle_thermostat(entity_id) }
}
if tab-bar.current-index == 1: Calendar { }
if tab-bar.current-index == 1: Calendar {
calendar-events: calendar-events;
}
if tab-bar.current-index == 2: Energy { }
Rectangle {