From ce7a79d1de579aac9a8c43a8763cc56b88427934 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Wed, 29 May 2024 15:51:53 +0200
Subject: [PATCH 1/2] Remove guessing of active_schedule

---
 src/db/relays.rs         | 10 ----------
 src/models/controller.rs |  2 +-
 src/models/relay.rs      | 25 +++++++++++++++----------
 src/types/relay_state.rs |  2 +-
 4 files changed, 17 insertions(+), 22 deletions(-)

diff --git a/src/db/relays.rs b/src/db/relays.rs
index c7f4ca3..9045002 100644
--- a/src/db/relays.rs
+++ b/src/db/relays.rs
@@ -163,14 +163,4 @@ impl DbRelay {
 			.await?
 			.ok_or(DatabaseError::NotFound)
 	}
-
-	pub async fn get_active_schedule(
-		&self,
-		conn: &mut PoolConnection<Sqlite>,
-	) -> Result<DbSchedule, DatabaseError> {
-		let weekday = utils::get_weekday();
-		DbJunctionRelaySchedule::get_schedule(conn, self, weekday as Weekday)
-			.await?
-			.ok_or(DatabaseError::NotFound)
-	}
 }
diff --git a/src/models/controller.rs b/src/models/controller.rs
index e1a272b..6bb04e3 100644
--- a/src/models/controller.rs
+++ b/src/models/controller.rs
@@ -69,7 +69,7 @@ impl Controller {
 			.iter_mut()
 			.filter_map(|r| {
 				r.reload_active_schedule(now.weekday);
-				r.active_schedule.get_next_time(&now.time)
+				r.get_next_time(&now.time)
 			})
 			.min()
 	}
diff --git a/src/models/relay.rs b/src/models/relay.rs
index 51289ca..bc2b48b 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -19,7 +19,7 @@ pub struct Relay {
 	pub controller: DbController,
 	pub controller_id: EmgauwaUid,
 	pub schedules: Vec<DbSchedule>,
-	pub active_schedule: DbSchedule,
+	pub active_schedule: Option<DbSchedule>,
 	pub override_schedule: Option<DbSchedule>,
 	pub is_on: Option<bool>,
 	pub tags: Vec<String>,
@@ -55,7 +55,6 @@ impl FromDbModel for Relay {
 		let controller_id = cache.uid.clone();
 
 		let schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &db_model))?;
-		let active_schedule = block_on(db_model.get_active_schedule(conn))?;
 
 		let is_on = None;
 
@@ -64,7 +63,7 @@ impl FromDbModel for Relay {
 			controller: cache,
 			controller_id,
 			schedules,
-			active_schedule,
+			active_schedule: None,
 			override_schedule: None,
 			is_on,
 			tags,
@@ -79,17 +78,23 @@ impl Relay {
 		self.r = block_on(self.r.reload(conn))?;
 		self.schedules = block_on(DbJunctionRelaySchedule::get_schedules(conn, &self.r))?;
 
-		self.reload_active_schedule(utils::get_weekday());
-
 		Ok(())
 	}
 
 	pub fn is_on(&self, now: &NaiveTime) -> bool {
-		self.active_schedule.is_on(now)
+		if let Some(active_schedule) = &self.active_schedule {
+			active_schedule.is_on(now)
+		} else {
+			false
+		}
 	}
 
 	pub fn get_next_time(&self, now: &NaiveTime) -> Option<NaiveTime> {
-		self.active_schedule.get_next_time(now)
+		if let Some(active_schedule) = &self.active_schedule {
+			active_schedule.get_next_time(now)
+		} else {
+			None
+		}
 	}
 
 	pub fn check_pulsing(&mut self, now: &Instant) -> Option<Instant> {
@@ -109,7 +114,7 @@ impl Relay {
 	pub fn reload_active_schedule(&mut self, weekday: Weekday) {
 		if let Some(schedule) = &self.override_schedule {
 			if self.override_schedule_weekday == weekday {
-				self.active_schedule = schedule.clone();
+				self.active_schedule = Some(schedule.clone());
 				return;
 			}
 			if self.override_schedule_weekday != weekday {
@@ -117,11 +122,11 @@ impl Relay {
 			}
 		}
 
-		self.active_schedule = self.schedules.get(weekday as usize).unwrap().clone()
+		self.active_schedule = Some(self.schedules.get(weekday as usize).unwrap().clone())
 	}
 
 	pub fn apply_state(&mut self, state: &RelayState) {
-		self.active_schedule = state.active_schedule.clone();
+		self.active_schedule.clone_from(&state.active_schedule);
 		self.override_schedule.clone_from(&state.override_schedule);
 		self.is_on = state.is_on;
 	}
diff --git a/src/types/relay_state.rs b/src/types/relay_state.rs
index f4a2e3a..e215253 100644
--- a/src/types/relay_state.rs
+++ b/src/types/relay_state.rs
@@ -4,7 +4,7 @@ use crate::models::Relay;
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct RelayState {
-    pub active_schedule: DbSchedule,
+    pub active_schedule: Option<DbSchedule>,
     pub override_schedule: Option<DbSchedule>,
     pub is_on: Option<bool>
 }

From 277b1592004d6743d034310c9a0bc9022bdaa202 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Thu, 30 May 2024 02:37:37 +0200
Subject: [PATCH 2/2] Add setting to change "midnight" of day

---
 src/db/junction_relay_schedule.rs | 11 +++++------
 src/db/relays.rs                  |  4 +---
 src/models/relay.rs               |  8 ++++----
 src/types/emgauwa_now.rs          | 11 ++++++-----
 src/types/mod.rs                  |  2 --
 src/utils.rs                      | 22 ++++++++++++++--------
 6 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/src/db/junction_relay_schedule.rs b/src/db/junction_relay_schedule.rs
index f081a54..b8e0611 100644
--- a/src/db/junction_relay_schedule.rs
+++ b/src/db/junction_relay_schedule.rs
@@ -5,11 +5,10 @@ use sqlx::Sqlite;
 
 use crate::db::{DbRelay, DbSchedule};
 use crate::errors::DatabaseError;
-use crate::types::Weekday;
 
 pub struct DbJunctionRelaySchedule {
 	pub id: i64,
-	pub weekday: Weekday,
+	pub weekday: i64,
 	pub relay_id: i64,
 	pub schedule_id: i64,
 }
@@ -32,7 +31,7 @@ impl DbJunctionRelaySchedule {
 	pub async fn get_junction_by_relay_and_weekday(
 		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
-		weekday: Weekday,
+		weekday: i64,
 	) -> Result<Option<DbJunctionRelaySchedule>, DatabaseError> {
 		sqlx::query_as!(
 			DbJunctionRelaySchedule,
@@ -65,7 +64,7 @@ impl DbJunctionRelaySchedule {
 	pub async fn get_schedule(
 		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
-		weekday: Weekday,
+		weekday: i64,
 	) -> Result<Option<DbSchedule>, DatabaseError> {
 		sqlx::query_as!(
 			DbSchedule,
@@ -101,7 +100,7 @@ impl DbJunctionRelaySchedule {
 		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,
 		schedule: &DbSchedule,
-		weekday: Weekday,
+		weekday: i64,
 	) -> Result<DbJunctionRelaySchedule, DatabaseError> {
 		match Self::get_junction_by_relay_and_weekday(conn, relay, weekday).await? {
 			None => sqlx::query_as!(
@@ -139,7 +138,7 @@ impl DbJunctionRelaySchedule {
 		schedules: Vec<&DbSchedule>,
 	) -> Result<(), DatabaseError> {
 		for (weekday, schedule) in schedules.iter().enumerate() {
-			Self::set_schedule(conn, relay, schedule, weekday as Weekday).await?;
+			Self::set_schedule(conn, relay, schedule, weekday as i64).await?;
 		}
 		Ok(())
 	}
diff --git a/src/db/relays.rs b/src/db/relays.rs
index 9045002..8fe39e6 100644
--- a/src/db/relays.rs
+++ b/src/db/relays.rs
@@ -4,10 +4,8 @@ use serde_derive::{Deserialize, Serialize};
 use sqlx::pool::PoolConnection;
 use sqlx::Sqlite;
 
-use crate::db::{DbController, DbJunctionRelaySchedule, DbJunctionTag, DbSchedule, DbTag};
+use crate::db::{DbController, DbJunctionTag, DbTag};
 use crate::errors::DatabaseError;
-use crate::types::Weekday;
-use crate::utils;
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct DbRelay {
diff --git a/src/models/relay.rs b/src/models/relay.rs
index bc2b48b..b67c4d3 100644
--- a/src/models/relay.rs
+++ b/src/models/relay.rs
@@ -1,6 +1,6 @@
 use std::time::Instant;
 
-use chrono::NaiveTime;
+use chrono::{NaiveTime, Weekday};
 use futures::executor::block_on;
 use serde_derive::{Deserialize, Serialize};
 use sqlx::pool::PoolConnection;
@@ -9,7 +9,7 @@ use sqlx::Sqlite;
 use crate::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
 use crate::errors::DatabaseError;
 use crate::models::FromDbModel;
-use crate::types::{EmgauwaUid, RelayState, Weekday};
+use crate::types::{EmgauwaUid, RelayState};
 use crate::utils;
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -29,7 +29,7 @@ pub struct Relay {
 	pub pulsing: Option<Instant>,
 	#[serde(
 		skip,
-		default = "utils::get_weekday",
+		default = "utils::default_weekday",
 	)]
 	pub override_schedule_weekday: Weekday,
 }
@@ -68,7 +68,7 @@ impl FromDbModel for Relay {
 			is_on,
 			tags,
 			pulsing: None,
-			override_schedule_weekday: Weekday::default(),
+			override_schedule_weekday: utils::default_weekday(),
 		})
 	}
 }
diff --git a/src/types/emgauwa_now.rs b/src/types/emgauwa_now.rs
index 40f4907..315be8e 100644
--- a/src/types/emgauwa_now.rs
+++ b/src/types/emgauwa_now.rs
@@ -1,26 +1,27 @@
 use std::time::Instant;
 
-use chrono::{Local, NaiveTime, Timelike};
+use chrono::{Local, NaiveTime, Timelike, Weekday};
 
-use crate::types::Weekday;
 use crate::utils;
 
 pub struct EmgauwaNow {
 	pub time: NaiveTime,
 	pub instant: Instant,
 	pub weekday: Weekday,
+	pub midnight: NaiveTime,
 }
 
 impl EmgauwaNow {
-	pub fn now() -> EmgauwaNow {
+	pub fn now(midnight: &NaiveTime) -> EmgauwaNow {
 		EmgauwaNow {
 			time: Local::now().time(),
 			instant: Instant::now(),
-			weekday: utils::get_weekday(),
+			weekday: utils::get_weekday(midnight),
+			midnight: *midnight,
 		}
 	}
 
-	pub fn time_in_s(&self) -> u32 {
+	pub fn num_seconds_from_midnight(&self) -> u32 {
 		self.time.num_seconds_from_midnight()
 	}
 }
diff --git a/src/types/mod.rs b/src/types/mod.rs
index 44d116c..8325110 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -16,8 +16,6 @@ use crate::db::DbSchedule;
 use crate::errors::EmgauwaError;
 use crate::models::{Controller, Relay};
 
-pub type Weekday = i64;
-
 #[derive(Debug, Serialize, Deserialize, Message)]
 #[rtype(result = "Result<(), EmgauwaError>")]
 pub enum ControllerWsAction {
diff --git a/src/utils.rs b/src/utils.rs
index 3b20d9a..67e705b 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -2,13 +2,13 @@ use std::ffi::CString;
 use std::io::{Error, ErrorKind};
 use std::str::FromStr;
 
-use chrono::Datelike;
+use chrono::{Datelike, NaiveTime, Weekday};
 use log::LevelFilter;
 use simple_logger::SimpleLogger;
 
 use crate::errors::EmgauwaError;
 use crate::settings::Permissions;
-use crate::types::{RelayStates, Weekday};
+use crate::types::RelayStates;
 
 pub fn init_logging(level: &str) -> Result<(), EmgauwaError> {
 	let log_level: LevelFilter = LevelFilter::from_str(level)
@@ -92,12 +92,18 @@ fn drop_privileges_user(user: &str) -> Result<(), Error> {
 	Ok(())
 }
 
-pub fn get_weekday() -> Weekday {
-	(chrono::offset::Local::now()
-		.date_naive()
-		.weekday()
-		.number_from_monday()
-		- 1) as Weekday
+pub fn get_weekday(midnight: &NaiveTime) -> Weekday {
+	let dt = chrono::offset::Local::now().naive_local();
+	let weekday = dt.weekday();
+	if dt.time().lt(midnight) {
+		weekday.pred()
+	} else {
+		weekday
+	}
+}
+
+pub fn default_weekday() -> Weekday {
+	Weekday::Mon
 }
 
 pub fn printable_relay_states(relay_states: &RelayStates) -> String {