Compare commits
2 Commits
6d5340e9ca
...
4419442101
| Author | SHA1 | Date | |
|---|---|---|---|
| 4419442101 | |||
| 31e0664aa6 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -358,7 +358,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mqttAutomation"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossbeam",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mqttAutomation"
|
||||
version = "1.3.1"
|
||||
version = "1.3.2"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -9,16 +9,17 @@ 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"
|
||||
# auto:
|
||||
# - trigger:
|
||||
# !Time
|
||||
# date:
|
||||
# weekday: "0,1,2,3,4"
|
||||
# time:
|
||||
# hour: "7"
|
||||
# minute: "0"
|
||||
# seconds: "0"
|
||||
# actions:
|
||||
# - !Publish
|
||||
# type: publish
|
||||
# topic: "/cool/devices/lamp-01/set"
|
||||
# pauload: "ON"
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
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);
|
||||
},,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
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>
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
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),
|
||||
}
|
||||
}
|
||||
@ -1,153 +0,0 @@
|
||||
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/automation/auto/mod.rs
Normal file
66
src/automation/auto/mod.rs
Normal file
@ -0,0 +1,66 @@
|
||||
mod triggers;
|
||||
|
||||
pub mod automation {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
pub use crate::automation::auto::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>
|
||||
}
|
||||
|
||||
pub struct Generator {
|
||||
mqtt_triggers: HashMap<String, triggers::Mqtt>,
|
||||
time_triggers: HashMap<String, triggers::Datetime>,
|
||||
}
|
||||
impl Generator {
|
||||
|
||||
pub fn new() -> Generator {
|
||||
Generator {
|
||||
mqtt_triggers: HashMap::new(),
|
||||
time_triggers: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_automations(&mut self, automations: Vec<Automation>) {
|
||||
for auto in automations {
|
||||
self.read_automation_single(auto);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_automation_single(&mut self, auto: Automation) {
|
||||
// let auto_cloned = auto.clone();
|
||||
match auto.trigger {
|
||||
triggers::Trigger::Mqtt(mqtt) => {
|
||||
self.mqtt_triggers.insert(mqtt.topic.clone(), mqtt.clone());
|
||||
},
|
||||
triggers::Trigger::Time(_datetime) => {
|
||||
// self.time_triggers.insert(mqtt.topic, auto_cloned);
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,15 +1,39 @@
|
||||
mod datetime;
|
||||
|
||||
use
|
||||
|
||||
pub trait TriggerBase {
|
||||
fn key()
|
||||
}
|
||||
pub mod triggers {
|
||||
}
|
||||
use serde::Deserialize;
|
||||
|
||||
pub trait TriggerBase {
|
||||
fn get_next_trigger(&self) -> String;
|
||||
}
|
||||
|
||||
pub impl clock {
|
||||
#[derive(Deserialize, Clone)]
|
||||
enum StringOrU16 {
|
||||
String(String),
|
||||
U16(u16)
|
||||
}
|
||||
|
||||
// ===== mqtt
|
||||
|
||||
#[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.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== datetime
|
||||
|
||||
pub use crate::automation::auto::triggers::datetime::trigger_datetime::Datetime;
|
||||
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub enum Trigger {
|
||||
Mqtt(Mqtt),
|
||||
Time(Datetime),
|
||||
}
|
||||
}
|
||||
|
||||
316
src/automation/auto/triggers/datetime.rs
Normal file
316
src/automation/auto/triggers/datetime.rs
Normal file
@ -0,0 +1,316 @@
|
||||
|
||||
|
||||
pub mod trigger_datetime {
|
||||
use serde::Deserialize;
|
||||
use crate::automation::auto::triggers::triggers::TriggerBase;
|
||||
use chrono::{DateTime, Datelike, Local, NaiveDate, TimeZone, 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
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompareError {
|
||||
InvalidValue,
|
||||
DivNaN
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Date {
|
||||
year: Option<String>,
|
||||
month: Option<String>,
|
||||
date: Option<String>,
|
||||
weekday: Option<String>
|
||||
}
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Time {
|
||||
hour: Option<String>,
|
||||
minute: Option<String>,
|
||||
second: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct Datetime {
|
||||
date: Date,
|
||||
time: Time
|
||||
}
|
||||
impl Datetime {
|
||||
pub fn first_match(condition: String, start: u16, end: u16) -> Result<Option<u16>, CompareError> {
|
||||
if condition == "*" {
|
||||
return Ok(Some(start));
|
||||
}
|
||||
if condition.starts_with("*/") {
|
||||
let div = condition[2..].to_string();
|
||||
let mut options: Vec<u16> = (start..end).collect();
|
||||
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 <= end);
|
||||
let min = options.iter().min();
|
||||
match min {
|
||||
Some(min) => {
|
||||
return Ok(Some(min.clone()));
|
||||
},
|
||||
None => {
|
||||
return Ok(None);
|
||||
},
|
||||
}
|
||||
}
|
||||
match condition.parse::<u16>() {
|
||||
Ok(n) => {
|
||||
if n >= start && n <= end {
|
||||
return Ok(Some(n))
|
||||
}
|
||||
else {
|
||||
return Ok(None);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(CompareError::InvalidValue);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_thing(&self, option: String, start: u16, end: u16) -> Option<u16> {
|
||||
match self::Datetime::first_match(option, start, end) {
|
||||
Ok(r) => {
|
||||
return r
|
||||
},
|
||||
Err(e) => {
|
||||
println!("ERROR: trigger.datetime.find_next: failed to parse month: {:?}", e);
|
||||
return None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn get_days_in_month(date: &DateTime<Local>) -> u16 {
|
||||
let year = date.year();
|
||||
let month = date.month();
|
||||
let date = match NaiveDate::from_ymd_opt(year, month + 1, 1) {
|
||||
Some(d) => d,
|
||||
None => NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap()
|
||||
};
|
||||
let d = date.signed_duration_since(NaiveDate::from_ymd_opt(year, month, 1).unwrap());
|
||||
u16::try_from(d.num_days()).unwrap()
|
||||
}
|
||||
|
||||
fn find_next_trigger(&self, from: DateTime<Local>) -> Option<DateTime<Local>> {
|
||||
let mut next = from.clone();
|
||||
|
||||
// year
|
||||
match &self.date.year {
|
||||
Some(y) => {
|
||||
match self::Datetime::first_match(y.to_string(), get_u16_from_i32(next.year()).unwrap(), u16::MAX) {
|
||||
Ok(r) => {
|
||||
match r {
|
||||
Some(y) => {
|
||||
if get_u16_from_i32(next.year()).unwrap() != y {
|
||||
next = match Local::with_ymd_and_hms(&Local, i32::from(y), 1, 1, 0, 0, 0) {
|
||||
chrono::offset::LocalResult::Single(d) => d,
|
||||
chrono::offset::LocalResult::Ambiguous(d, _) => d,
|
||||
chrono::offset::LocalResult::None => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
None => return None,
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("ERROR: trigger.datetime.find_next: failed to parse year: {:?}", e);
|
||||
return None
|
||||
},
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
// month
|
||||
match &self.date.month {
|
||||
Some(opt) => {
|
||||
let now = get_u16_from_u32(next.month()).unwrap();
|
||||
match self.check_thing(opt.to_string(), now, 12) {
|
||||
Some(r) => {
|
||||
if r > now {
|
||||
next = match Local::with_ymd_and_hms(&Local, next.year(), u32::from(r), 1, 0, 0, 0) {
|
||||
chrono::offset::LocalResult::Single(d) => d,
|
||||
chrono::offset::LocalResult::Ambiguous(d, _) => d,
|
||||
chrono::offset::LocalResult::None => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
next = match Local::with_ymd_and_hms(&Local, next.year()+1, 1, 1, 0, 0, 0) {
|
||||
chrono::offset::LocalResult::Single(d) => d,
|
||||
chrono::offset::LocalResult::Ambiguous(d, _) => d,
|
||||
chrono::offset::LocalResult::None => return None
|
||||
};
|
||||
if (next - Local::now()).num_days() < 2*365 {
|
||||
return self.find_next_trigger(next);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
// date
|
||||
match &self.date.date {
|
||||
Some(opt) => {
|
||||
let now = get_u16_from_u32(next.day()).unwrap();
|
||||
match self.check_thing(opt.to_string(), now, Datetime::get_days_in_month(&next)) {
|
||||
Some(r) => {
|
||||
if r > now {
|
||||
next = match Local::with_ymd_and_hms(&Local, next.year(), next.month(), u32::from(r), 0, 0, 0) {
|
||||
chrono::offset::LocalResult::Single(d) => d,
|
||||
chrono::offset::LocalResult::Ambiguous(d, _) => d,
|
||||
chrono::offset::LocalResult::None => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let m = match next.month() {
|
||||
12 => 1,
|
||||
munth => munth
|
||||
};
|
||||
let y = match m {
|
||||
1 => next.year()+1,
|
||||
_ => next.year()
|
||||
};
|
||||
next = match Local::with_ymd_and_hms(&Local, y, m, 1, 0, 0, 0) {
|
||||
chrono::offset::LocalResult::Single(d) => d,
|
||||
chrono::offset::LocalResult::Ambiguous(d, _) => d,
|
||||
chrono::offset::LocalResult::None => return None
|
||||
};
|
||||
if (next - Local::now()).num_days() < 2*365 {
|
||||
return self.find_next_trigger(next);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
// hour
|
||||
match &self.time.hour {
|
||||
Some(opt) => {
|
||||
let now = get_u16_from_u32(next.hour()).unwrap();
|
||||
match self.check_thing(opt.to_string(), now, 23) {
|
||||
Some(r) => {
|
||||
if r > now {
|
||||
next = match Local::with_ymd_and_hms(&Local, next.year(), next.month(), next.hour(), u32::from(r), 0, 0) {
|
||||
chrono::offset::LocalResult::Single(d) => d,
|
||||
chrono::offset::LocalResult::Ambiguous(d, _) => d,
|
||||
chrono::offset::LocalResult::None => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let mut d = next.day() + 1;
|
||||
if d == u32::from(Datetime::get_days_in_month(&next)) {
|
||||
d = 1;
|
||||
}
|
||||
let m = match d {
|
||||
1 => next.month()+1,
|
||||
_ => next.month()
|
||||
};
|
||||
let y = match m {
|
||||
1 => next.year()+1,
|
||||
_ => next.year()
|
||||
};
|
||||
next = match Local::with_ymd_and_hms(&Local, y, m, d, 0, 0, 0) {
|
||||
chrono::offset::LocalResult::Single(d) => d,
|
||||
chrono::offset::LocalResult::Ambiguous(d, _) => d,
|
||||
chrono::offset::LocalResult::None => return None
|
||||
};
|
||||
if (next - Local::now()).num_days() < 2*365 {
|
||||
return self.find_next_trigger(next);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
Some(next)
|
||||
}
|
||||
}
|
||||
impl TriggerBase for Datetime {
|
||||
fn get_next_trigger(&self) -> String {
|
||||
let next_trigger: Option<DateTime<Local>> = self.find_next_trigger(Local::now());
|
||||
match next_trigger {
|
||||
Some(next) => {
|
||||
let str = next.format("%S").to_string();
|
||||
print!("datetime string: {str}");
|
||||
return str
|
||||
},
|
||||
None => {
|
||||
print!("ERROR: trigger.datetime.get_next: No next trigger found");
|
||||
return "None".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn datetime_first_match() {
|
||||
assert_eq!(Datetime::first_match("*".to_string(), 123, 128).unwrap(), Some(123));
|
||||
assert_eq!(Datetime::first_match("*/4".to_string(), 123, 128).unwrap(), Some(124));
|
||||
assert_eq!(Datetime::first_match("*/10".to_string(), 123, 128).unwrap(), None);
|
||||
assert_eq!(Datetime::first_match("125".to_string(), 123, 128).unwrap(), Some(125));
|
||||
assert_eq!(Datetime::first_match("122,126".to_string(), 123, 128).unwrap(), Some(126));
|
||||
assert_eq!(Datetime::first_match("122,129".to_string(), 123, 128).unwrap(), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
mod json;
|
||||
//AUTO mod auto;
|
||||
|
||||
//AUTO use std::collections::HashMap;
|
||||
use std::{thread, time::Duration};
|
||||
use serde::Deserialize;
|
||||
|
||||
@ -9,7 +8,7 @@ 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};
|
||||
//AUTO use crate::automation::auto::automation::{Automation as AutoConfig, Generator};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SettingsConf {
|
||||
@ -18,7 +17,7 @@ pub struct SettingsConf {
|
||||
alarm_hour: u8,
|
||||
alarm_minute: u8,
|
||||
|
||||
// auto: AutoConfig
|
||||
//AUTO auto: Vec<AutoConfig>
|
||||
}
|
||||
|
||||
struct PingStats {
|
||||
@ -132,7 +131,7 @@ impl Automation {
|
||||
if topic.starts_with("alarm/set") {
|
||||
|
||||
let mut time = message.payload.split(':');
|
||||
if time.clone().count() != 2 {
|
||||
if time.clone().count() != 2 && time.clone().count() != 3 {
|
||||
println!("ERROR: config_message_in: alarm/set has invalid payload. incorect number of slices ({})", message.payload)
|
||||
}
|
||||
else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user