first consept for configurable automation

This commit is contained in:
Laila van Reenen 2025-05-06 23:37:44 +02:00
parent 642e02949f
commit 6d5340e9ca
Signed by: LailaTheElf
GPG Key ID: 8A3EF0226518C12D
9 changed files with 513 additions and 2 deletions

208
Cargo.lock generated
View File

@ -17,6 +17,21 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "autocfg"
version = "1.4.0"
@ -44,6 +59,12 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "bumpalo"
version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "bytes"
version = "1.9.0"
@ -65,6 +86,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -208,6 +243,30 @@ version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "indexmap"
version = "2.7.1"
@ -224,6 +283,16 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "json"
version = "0.12.4"
@ -291,6 +360,7 @@ dependencies = [
name = "mqttAutomation"
version = "1.3.1"
dependencies = [
"chrono",
"crossbeam",
"json",
"mqtt-client",
@ -299,6 +369,15 @@ dependencies = [
"serde_yaml",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.36.7"
@ -308,6 +387,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "openssl-probe"
version = "0.1.5"
@ -436,6 +521,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]]
name = "ryu"
version = "1.0.18"
@ -646,6 +737,123 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-result"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.52.0"

View File

@ -10,3 +10,4 @@ rumqttc = "0.24.0"
serde = { version = "1.0.217", features = ["derive"] }
serde_yaml = "0.9.34"
mqtt-client = { tag = "v4.0.0", git = "https://gitea.finnvanreenen.nl/LailaTheElf/mqttClient.git" }
chrono = "0.4.41"

View File

@ -8,3 +8,17 @@ automation:
base_topic: "/kees/automation/"
alarm_hour: 7
alarm_minute: 0
# automation:
# - trigger:
# - type: time
# date:
# weekday: [0,1,2,3,4]
# time:
# hour: 7
# minute: 0
# seconds: 0
# action:
# - type: publish
# topic: "/cool/devices/lamp-01/set"
# pauload: "ON"

48
src/automation/auto.rs Normal file
View File

@ -0,0 +1,48 @@
mod config;
pub mod auto_generator {
use std::collections::HashMap;
pub use crate::automation::auto::config::automation_config as config;
struct TimeTrigger {
next: u64,
time: config::triggers::Datetime
}
pub struct Generator {
mqtt_triggers: HashMap<String, config::Automation>,
time_triggers: HashMap<String, TimeTrigger>,
}
impl Generator {
pub fn new() -> Generator {
Generator {
mqtt_triggers: HashMap::new(),
time_triggers: HashMap::new()
}
}
pub fn read_automations(&mut self, automations: Vec<config::Automation>) {
for auto in automations {
self.read_automation_single(auto);
}
}
fn read_automation_single(&mut self, auto: config::Automation) {
let auto_cloned = auto.clone();
match auto.trigger {
config::triggers::Trigger::Mqtt(mqtt) => {
self.mqtt_triggers.insert(mqtt.topic, auto_cloned);
},
config::triggers::Trigger::Time(datetime) => {
self.time_triggers.insert(mqtt.topic, auto_cloned);
},,
}
}
}
}

View File

@ -0,0 +1,30 @@
mod triggers;
pub mod automation_config {
use serde::Deserialize;
pub use crate::automation::auto::config::triggers::triggers;
mod actions {
use serde::Deserialize;
#[derive(Deserialize, Clone)]
struct Publish {
topic: String,
payload: Option<String>,
retain: Option<bool>
}
#[derive(Deserialize, Clone)]
pub enum Action {
Publish(Publish),
}
}
#[derive(Deserialize, Clone)]
pub struct Automation {
pub trigger: triggers::Trigger,
pub actions: Vec<actions::Action>
}
}

View File

@ -0,0 +1,33 @@
mod datetime;
pub mod triggers {
use serde::Deserialize;
pub trait TriggerBase {
fn get_next_trigger(&self) -> &String;
}
#[derive(Deserialize, Clone)]
enum StringOrU16 {
String(String),
U16(u16)
}
#[derive(Deserialize, Clone)]
pub struct Mqtt {
pub topic: String,
pub payload: Option<String>
}
impl TriggerBase for Mqtt {
fn get_next_trigger(&self) -> &String {
&self.topic
}
}
#[derive(Deserialize, Clone)]
pub enum Trigger {
Mqtt(Mqtt),
Time(Datetime),
}
}

View File

@ -0,0 +1,153 @@
pub mod trigger_datetime {
use std::collections::btree_map::Range;
use serde::Deserialize;
use crate::automation::auto::config::triggers::triggers::TriggerBase;
use chrono::{DateTime, Datelike, Local, Timelike};
fn get_u16_from_u32(value: u32) -> Option<u16> {
match u16::try_from(value) {
Ok(n) => Some(n),
Err(_) => None
}
}
fn get_u16_from_i32(value: i32) -> Option<u16> {
match u16::try_from(value) {
Ok(n) => Some(n),
Err(_) => None
}
}
pub enum CompareError {
InvalidValue
DivNaN
}
#[derive(Deserialize, Clone)]
enum StringOrU16 {
String(String),
U16(u16)
}
#[derive(Deserialize, Clone)]
struct Date {
year: Option<StringOrU16>,
month: Option<StringOrU16>,
date: Option<StringOrU16>,
weekday: Option<StringOrU16>
}
#[derive(Deserialize, Clone)]
struct Time {
hour: Option<StringOrU16>,
minute: Option<StringOrU16>,
second: Option<StringOrU16>
}
#[derive(Deserialize, Clone)]
pub struct Datetime {
date: Date,
time: Time
}
impl Datetime {
pub fn first_match(condition: String, start: u16, max: u16) -> Result<Option<u16>, CompareError> {
if condition == "*" {
return Ok(Some(start));
}
if condition.starts_with("*/") {
let divs = condition.split('/');
let mut options: Vec<u16> = (start..max).collect();
for div in divs {
match div.parse::<u16>() {
Ok(n) => {
options.retain(|x| x % n == 0);
},
Err(_) => {
return Err(CompareError::DivNaN);
},
}
}
let min = options.iter().min();
match min {
Some(min) => {
return Ok(Some(min.clone()));
},
None => {
return Ok(None);
},
}
}
if condition.split(',').count() > 0 {
let parts = condition.split(',');
let mut options: Vec<u16> = [].to_vec();
for part in parts {
match part.parse::<u16>() {
Ok(n) => {
options.push(n);
},
Err(_) => {
return Err(CompareError::DivNaN);
},
}
}
options.retain(|x| *x >= start && *x <= max);
let min = options.iter().min();
match min {
Some(min) => {
return Ok(Some(min.clone()));
},
None => {
return Ok(None);
},
}
}
Err(CompareError::InvalidValue)
}
fn find_next_trigger(&self, from: DateTime<Local>) -> DateTime<Local> {
let year: u16 = u16::MAX;
let month: u16 = u16::MAX;
let date: u16 = u16::MAX;
let weekday: u16 = u16::MAX;
let hour: u16 = u16::MAX;
let minute: u16 = u16::MAX;
let second: u16 = u16::MAX;
let a_next_time: bool = true;
// year
match self.date.year {
Some(y) => {
match y {
StringOrU16::String(y) => {
match self::Datetime::first_match(y, get_u16_from_i32(from.year()).unwrap(), u16::MAX) {
Ok(r) => {
match r {
Some(y) => year = y,
None => todo!(),
}
},
Err(_) => todo!(),
}
},
StringOrU16::U16(y) => {
if y == get_u16_from_u32(from.year()) {
year = y;
}
},
}
},
None => todo!(),
}
todo!()
}
}
impl TriggerBase for Datetime {
fn get_next_trigger(&self) -> &String {
let next_trigger = self.find_next_trigger(Local::now())
}
}
}

View File

@ -0,0 +1,15 @@
use
pub trait TriggerBase {
fn key()
}
pub mod triggers {
}
pub impl clock {
}

View File

@ -1,5 +1,7 @@
mod json;
//AUTO mod auto;
//AUTO use std::collections::HashMap;
use std::{thread, time::Duration};
use serde::Deserialize;
@ -7,13 +9,16 @@ use mqtt_client::{MqttMessage, MqttEvent, Sender, Receiver, QoS};
use mqtt_client::mqtt_client;
use crate::automation::json::json_parser;
//AUTO use crate::automation::auto::auto_generator::{config::Automation as AutoConfig, Generator};
#[derive(Deserialize)]
pub struct SettingsConf {
base_topic: String,
alarm_hour: u8,
alarm_minute: u8
alarm_minute: u8,
// auto: AutoConfig
}
struct PingStats {
@ -53,7 +58,9 @@ pub struct Automation {
ping_elfdesktop: PingStats,
config: SettingsConf
config: SettingsConf,
//AUTO generator: Generator
}
@ -340,6 +347,8 @@ impl mqtt_client::MqttTool<SettingsConf> for Automation {
ping_elfdesktop: { PingStats { total: 0, fails: 0, avg: 0.0 } },
config: config,
//AUTO generator: Generator::new()
}
}