diff --git a/Cargo.lock b/Cargo.lock index a71f1da..0153116 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,12 +36,6 @@ dependencies = [ "serde", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -108,6 +102,7 @@ dependencies = [ "serde_json", "thiserror 2.0.16", "tokio", + "tokio-cron-scheduler", "tracing", "tracing-subscriber", ] @@ -154,7 +149,6 @@ dependencies = [ "futures", "google_home", "hostname", - "indexmap", "inventory", "lua_typed", "mlua", @@ -163,9 +157,7 @@ dependencies = [ "serde_json", "thiserror 2.0.16", "tokio", - "tokio-cron-scheduler", "tracing", - "uuid", ] [[package]] @@ -324,16 +316,25 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf", ] [[package]] @@ -376,11 +377,48 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "croner" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c344b0690c1ad1c7176fe18eb173e0c927008fdaaa256e40dfd43ddd149c0843" +checksum = "4c007081651a19b42931f86f7d4f74ee1c2a7d0cd2c6636a81695b5ffd4e9990" dependencies = [ "chrono", + "derive_builder", + "strum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.106", ] [[package]] @@ -424,6 +462,37 @@ dependencies = [ "thiserror 2.0.16", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.106", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -468,12 +537,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "erased-serde" version = "0.4.6" @@ -703,10 +766,10 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.15.5" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hex" @@ -722,7 +785,7 @@ checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" dependencies = [ "cfg-if", "libc", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -836,9 +899,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -944,6 +1007,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.1.0" @@ -965,17 +1034,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "indexmap" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" -dependencies = [ - "equivalent", - "hashbrown", - "serde", -] - [[package]] name = "inventory" version = "0.3.21" @@ -1104,16 +1162,17 @@ dependencies = [ [[package]] name = "lua_typed" version = "0.1.0" -source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#08f5c4533a93131e8eda6702c062fb841d14d4e1" +source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#f6a684291432aae2ef7109712882e7e3ed758d08" dependencies = [ "eui48", "lua_typed_macro", + "mlua", ] [[package]] name = "lua_typed_macro" version = "0.1.0" -source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#08f5c4533a93131e8eda6702c062fb841d14d4e1" +source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#f6a684291432aae2ef7109712882e7e3ed758d08" dependencies = [ "convert_case", "itertools", @@ -1209,9 +1268,9 @@ dependencies = [ [[package]] name = "mlua" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3dd94c3c4dea0049b22296397040840a8f6b5b5229f438434ba82df402b42d" +checksum = "9be1c2bfc684b8a228fbaebf954af7a47a98ec27721986654a4cc2c40a20cc7e" dependencies = [ "bstr", "either", @@ -1349,6 +1408,24 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1901,6 +1978,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.11" @@ -1938,6 +2021,33 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "subtle" version = "2.6.1" @@ -2080,11 +2190,12 @@ dependencies = [ [[package]] name = "tokio-cron-scheduler" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71ce8f810abc9fabebccc30302a952f9e89c6cf246fafaf170fef164063141" +checksum = "bb73c4033ddcbbf81fd828293fd41a0145cde2cbc30dd782227c5081a523214d" dependencies = [ "chrono", + "chrono-tz", "croner", "num-derive", "num-traits", @@ -2479,22 +2590,22 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.2.1", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -2503,9 +2614,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -2519,21 +2630,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-result" -version = "0.3.4" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fa7f3e8..159d31b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ futures = "0.3.31" google_home = { path = "./google_home/google_home" } google_home_macro = { path = "./google_home/google_home_macro" } hostname = "0.4.1" -indexmap = { version = "2.11.0", features = ["serde"] } inventory = "0.3.21" itertools = "0.14.0" json_value_merge = "2.0.1" @@ -59,10 +58,9 @@ serde_repr = "0.1.20" syn = { version = "2.0.106" } thiserror = "2.0.16" tokio = { version = "1", features = ["rt-multi-thread"] } -tokio-cron-scheduler = "0.14.0" +tokio-cron-scheduler = "0.15.0" tracing = "0.1.41" tracing-subscriber = "0.3.20" -uuid = "1.18.1" wakey = "0.3.0" [dependencies] @@ -87,6 +85,7 @@ serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } +tokio-cron-scheduler = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/automation_lib/Cargo.toml b/automation_lib/Cargo.toml index 15b28aa..04dea30 100644 --- a/automation_lib/Cargo.toml +++ b/automation_lib/Cargo.toml @@ -11,7 +11,6 @@ dyn-clone = { workspace = true } futures = { workspace = true } google_home = { workspace = true } hostname = { workspace = true } -indexmap = { workspace = true } inventory = { workspace = true } lua_typed = { workspace = true } mlua = { workspace = true } @@ -20,6 +19,4 @@ serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } -tokio-cron-scheduler = { workspace = true } tracing = { workspace = true } -uuid = { workspace = true } diff --git a/automation_lib/src/device_manager.rs b/automation_lib/src/device_manager.rs index fe755f2..cdf302a 100644 --- a/automation_lib/src/device_manager.rs +++ b/automation_lib/src/device_manager.rs @@ -1,13 +1,10 @@ use std::collections::HashMap; -use std::pin::Pin; use std::sync::Arc; -use futures::Future; use futures::future::join_all; use lua_typed::Typed; use mlua::FromLua; use tokio::sync::{RwLock, RwLockReadGuard}; -use tokio_cron_scheduler::{Job, JobScheduler}; use tracing::{debug, instrument, trace}; use crate::device::Device; @@ -19,7 +16,6 @@ pub type DeviceMap = HashMap>; pub struct DeviceManager { devices: Arc>, event_channel: EventChannel, - scheduler: JobScheduler, } impl DeviceManager { @@ -29,7 +25,6 @@ impl DeviceManager { let device_manager = Self { devices: Arc::new(RwLock::new(HashMap::new())), event_channel, - scheduler: JobScheduler::new().await.unwrap(), }; tokio::spawn({ @@ -45,8 +40,6 @@ impl DeviceManager { } }); - device_manager.scheduler.start().await.unwrap(); - device_manager } @@ -105,42 +98,6 @@ impl mlua::UserData for DeviceManager { Ok(()) }); - methods.add_async_method( - "schedule", - async |lua, this, (schedule, f): (String, mlua::Function)| { - debug!("schedule = {schedule}"); - // This creates a function, that returns the actual job we want to run - let create_job = { - let lua = lua.clone(); - - move |uuid: uuid::Uuid, - _: tokio_cron_scheduler::JobScheduler| - -> Pin + Send>> { - let lua = lua.clone(); - - // Create the actual function we want to run on a schedule - let future = async move { - let f: mlua::Function = - lua.named_registry_value(uuid.to_string().as_str()).unwrap(); - f.call_async::<()>(()).await.unwrap(); - }; - - Box::pin(future) - } - }; - - let job = Job::new_async(schedule.as_str(), create_job).unwrap(); - - let uuid = this.scheduler.add(job).await.unwrap(); - - // Store the function in the registry - lua.set_named_registry_value(uuid.to_string().as_str(), f) - .unwrap(); - - Ok(()) - }, - ); - methods.add_method("event_channel", |_lua, this, ()| Ok(this.event_channel())) } } diff --git a/automation_lib/src/lib.rs b/automation_lib/src/lib.rs index 224971a..a50fabd 100644 --- a/automation_lib/src/lib.rs +++ b/automation_lib/src/lib.rs @@ -13,7 +13,6 @@ pub mod helpers; pub mod lua; pub mod messages; pub mod mqtt; -pub mod schedule; type RegisterFn = fn(lua: &mlua::Lua) -> mlua::Result; type DefinitionsFn = fn() -> String; diff --git a/automation_lib/src/schedule.rs b/automation_lib/src/schedule.rs deleted file mode 100644 index 3300629..0000000 --- a/automation_lib/src/schedule.rs +++ /dev/null @@ -1,17 +0,0 @@ -use indexmap::IndexMap; -use serde::Deserialize; - -#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone, Copy)] -#[serde(rename_all = "snake_case")] -pub enum Action { - On, - Off, -} - -pub type Schedule = IndexMap>>; - -// #[derive(Debug, Deserialize)] -// pub struct Schedule { -// pub when: String, -// pub actions: IndexMap>, -// } diff --git a/config.lua b/config.lua index b67a3d9..90493b9 100644 --- a/config.lua +++ b/config.lua @@ -57,7 +57,7 @@ local function check_battery(device, battery) low_battery[id] = nil end end -device_manager:schedule("0 0 21 */1 * *", function() +local function notify_low_battery() -- Don't send notifications if there are now devices with low battery if next(low_battery) == nil then print("No devices with low battery") @@ -76,7 +76,7 @@ device_manager:schedule("0 0 21 */1 * *", function() tags = { "battery" }, priority = "default", }) -end) +end --- @class OnPresence --- @field [integer] fun(presence: boolean) @@ -742,17 +742,19 @@ devs:add(devices.ContactSensor.new({ battery_callback = check_battery, })) -device_manager:schedule("0 0 19 * * *", function() - bedroom_air_filter:set_on(true) -end) -device_manager:schedule("0 0 20 * * *", function() - bedroom_air_filter:set_on(false) -end) - ---@type Config return { fulfillment = { openid_url = "https://login.huizinga.dev/api/oidc", }, devices = devs, + schedule = { + ["0 0 19 * * *"] = function() + bedroom_air_filter:set_on(true) + end, + ["0 0 20 * * *"] = function() + bedroom_air_filter:set_on(false) + end, + ["0 0 21 */1 * *"] = notify_low_battery, + }, } diff --git a/definitions/automation:device_manager.lua b/definitions/automation:device_manager.lua index 9fa74fc..23af2c8 100644 --- a/definitions/automation:device_manager.lua +++ b/definitions/automation:device_manager.lua @@ -5,8 +5,4 @@ local DeviceManager ---@param device DeviceInterface function DeviceManager:add(device) end ----@param cron string ----@param callback fun() -function DeviceManager:schedule(cron, callback) end - return DeviceManager diff --git a/definitions/config.lua b/definitions/config.lua index 346d45a..9892431 100644 --- a/definitions/config.lua +++ b/definitions/config.lua @@ -10,4 +10,5 @@ local FulfillmentConfig ---@class Config ---@field fulfillment FulfillmentConfig ---@field devices DeviceInterface[]? +---@field schedule table? local Config diff --git a/src/bin/automation.rs b/src/bin/automation.rs index a0cd914..644e94a 100644 --- a/src/bin/automation.rs +++ b/src/bin/automation.rs @@ -6,6 +6,7 @@ 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}; @@ -142,6 +143,8 @@ async fn app() -> anyhow::Result<()> { device_manager.add(device).await; } + start_scheduler(config.schedule).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 2875164..2360daf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -37,6 +37,9 @@ pub struct Config { #[device_config(from_lua, default)] #[typed(default)] pub devices: Vec>, + #[device_config(from_lua, default)] + #[typed(default)] + pub schedule: HashMap, } impl From for SocketAddr { diff --git a/src/lib.rs b/src/lib.rs index 49e787e..f1e947d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod config; +pub mod schedule; pub mod secret; pub mod version; pub mod web; diff --git a/src/schedule.rs b/src/schedule.rs new file mode 100644 index 0000000..62a90ad --- /dev/null +++ b/src/schedule.rs @@ -0,0 +1,28 @@ +use std::collections::HashMap; +use std::pin::Pin; + +use tokio_cron_scheduler::{Job, JobScheduler, JobSchedulerError}; + +pub async fn start_scheduler( + schedule: HashMap, +) -> Result<(), JobSchedulerError> { + let scheduler = JobScheduler::new().await?; + + for (s, f) in schedule { + let job = { + move |_uuid, _lock| -> Pin + Send>> { + let f = f.clone(); + + Box::pin(async move { + f.call_async::<()>(()).await.unwrap(); + }) + } + }; + + let job = Job::new_async(s, job)?; + + scheduler.add(job).await?; + } + + scheduler.start().await +}