diff --git a/config/battery.lua b/config/battery.lua index 7eb82f2..19546bc 100644 --- a/config/battery.lua +++ b/config/battery.lua @@ -38,4 +38,8 @@ function module.notify_low_battery() }) end +module.schedule = { + ["0 0 21 */1 * *"] = module.notify_low_battery, +} + return module diff --git a/config/config.lua b/config/config.lua index cb5cbdc..289a573 100644 --- a/config/config.lua +++ b/config/config.lua @@ -29,14 +29,4 @@ return { require("config.rooms"), require("config.windows"), }, - -- TODO: Make this also part of the modules - schedule = { - ["0 0 19 * * *"] = function() - require("config.rooms.bedroom").set_airfilter_on(true) - end, - ["0 0 20 * * *"] = function() - require("config.rooms.bedroom").set_airfilter_on(false) - end, - ["0 0 21 */1 * *"] = require("config.battery").notify_low_battery, - }, } diff --git a/config/rooms/bedroom.lua b/config/rooms/bedroom.lua index ee8a561..50502f5 100644 --- a/config/rooms/bedroom.lua +++ b/config/rooms/bedroom.lua @@ -56,19 +56,22 @@ function module.setup(mqtt_client) windows.add(window) return { - lights, - lights_relax, - air_filter, - switch, - window, + devices = { + lights, + lights_relax, + air_filter, + switch, + window, + }, + schedule = { + ["0 0 19 * * *"] = function() + air_filter:set_on(true) + end, + ["0 0 20 * * *"] = function() + air_filter:set_on(false) + end, + }, } end ---- @param on boolean -function module.set_airfilter_on(on) - if air_filter then - air_filter:set_on(on) - end -end - return module diff --git a/definitions/config.lua b/definitions/config.lua index 32ed6aa..1cef4fd 100644 --- a/definitions/config.lua +++ b/definitions/config.lua @@ -11,12 +11,12 @@ local FulfillmentConfig ---@field fulfillment FulfillmentConfig ---@field modules (Module)[] ---@field mqtt MqttConfig ----@field schedule (table)? local Config ---@class Module ---@field setup (fun(mqtt_client: AsyncClient): Module | DeviceInterface[] | nil)? ---@field devices (DeviceInterface)[]? +---@field schedule table? ---@field [number] (Module)[]? local Module diff --git a/src/bin/automation.rs b/src/bin/automation.rs index 8b759c3..8dea38e 100644 --- a/src/bin/automation.rs +++ b/src/bin/automation.rs @@ -6,7 +6,6 @@ use std::process; use ::config::{Environment, File}; use automation::config::{Config, Setup}; -use automation::schedule::start_scheduler; use automation::secret::EnvironmentSecretFile; use automation::version::VERSION; use automation::web::{ApiError, User}; @@ -145,7 +144,7 @@ async fn app() -> anyhow::Result<()> { device_manager.add(device).await; } - start_scheduler(config.schedule).await?; + resolved.scheduler.start().await?; // Create google home fulfillment route let fulfillment = Router::new().route("/google_home", post(fulfillment)); diff --git a/src/config.rs b/src/config.rs index 5f2331a..fd71452 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,6 +9,8 @@ use lua_typed::Typed; use mlua::FromLua; use serde::Deserialize; +use crate::schedule::Scheduler; + #[derive(Debug, Deserialize)] pub struct Setup { #[serde(default = "default_entrypoint")] @@ -57,6 +59,7 @@ impl FromLua for SetupFunction { pub struct Module { pub setup: Option, pub devices: Vec>, + pub schedule: HashMap>, pub modules: Vec, } @@ -74,10 +77,12 @@ impl Typed for Module { Some(format!( r#"---@field setup {} ---@field devices {}? +---@field schedule {}? ---@field [number] {}? "#, Option::::type_name(), Vec::>::type_name(), + HashMap::>::type_name(), Vec::::type_name(), )) } @@ -105,8 +110,9 @@ impl FromLua for Module { }; let setup = table.get("setup")?; - let devices = table.get("devices").unwrap_or_default(); + let schedule = table.get("schedule").unwrap_or_default(); + let mut modules = Vec::new(); for module in table.sequence_values::() { @@ -116,6 +122,7 @@ impl FromLua for Module { Ok(Module { setup, devices, + schedule, modules, }) } @@ -142,10 +149,10 @@ impl Modules { lua: &mlua::Lua, client: &WrappedAsyncClient, ) -> mlua::Result { - let mut modules: VecDeque<_> = self.0.into(); - let mut devices = Vec::new(); + let mut scheduler = Scheduler::default(); + let mut modules: VecDeque<_> = self.0.into(); loop { let Some(module) = modules.pop_front() else { break; @@ -174,15 +181,19 @@ impl Modules { } devices.extend(module.devices); + for (cron, f) in module.schedule { + scheduler.add_job(cron, f); + } } - Ok(Resolved { devices }) + Ok(Resolved { devices, scheduler }) } } #[derive(Debug, Default)] pub struct Resolved { pub devices: Vec>, + pub scheduler: Scheduler, } #[derive(Debug, LuaDeviceConfig, Typed)] @@ -192,9 +203,6 @@ pub struct Config { pub modules: Modules, #[device_config(from_lua)] pub mqtt: MqttConfig, - #[device_config(from_lua, default)] - #[typed(default)] - pub schedule: HashMap>, } impl From for SocketAddr { diff --git a/src/schedule.rs b/src/schedule.rs index 543c0fe..3615fed 100644 --- a/src/schedule.rs +++ b/src/schedule.rs @@ -1,29 +1,37 @@ -use std::collections::HashMap; use std::pin::Pin; use automation_lib::action_callback::ActionCallback; use tokio_cron_scheduler::{Job, JobScheduler, JobSchedulerError}; -pub async fn start_scheduler( - schedule: HashMap>, -) -> Result<(), JobSchedulerError> { - let scheduler = JobScheduler::new().await?; +#[derive(Debug, Default)] +pub struct Scheduler { + jobs: Vec<(String, ActionCallback<()>)>, +} - for (s, f) in schedule { - let job = { - move |_uuid, _lock| -> Pin + Send>> { - let f = f.clone(); - - Box::pin(async move { - f.call(()).await; - }) - } - }; - - let job = Job::new_async(s, job)?; - - scheduler.add(job).await?; +impl Scheduler { + pub fn add_job(&mut self, cron: String, f: ActionCallback<()>) { + self.jobs.push((cron, f)); } - scheduler.start().await + pub async fn start(self) -> Result<(), JobSchedulerError> { + let scheduler = JobScheduler::new().await?; + + for (s, f) in self.jobs { + let job = { + move |_uuid, _lock| -> Pin + Send>> { + let f = f.clone(); + + Box::pin(async move { + f.call(()).await; + }) + } + }; + + let job = Job::new_async(s, job)?; + + scheduler.add(job).await?; + } + + scheduler.start().await + } }