212 lines
7.8 KiB
Rust
212 lines
7.8 KiB
Rust
use std::{num::NonZeroUsize, path::PathBuf, str::FromStr, time::Duration};
|
|
|
|
use clap::Parser;
|
|
use driver_kasa::connection::LB130USHandle;
|
|
use futures::stream::{FuturesUnordered, StreamExt};
|
|
use home_assistant::{
|
|
event::context::context::Context, home_assistant::HomeAssistant, light::HomeAssistantLight,
|
|
notify::service::mobile_app::StandardNotification, object_id::ObjectId,
|
|
};
|
|
use persisted::{persisted, Config, Keyspace, Partition, PartitionCreateOptions};
|
|
use protocol::light::{IsOff, IsOn, Kelvin, TurnToTemperature};
|
|
use pyo3::prelude::*;
|
|
use shadow_rs::shadow;
|
|
use tokio::{
|
|
task::{spawn_blocking, JoinSet},
|
|
time::{interval, MissedTickBehavior},
|
|
};
|
|
use tracing::{level_filters::LevelFilter, Level};
|
|
use tracing_appender::rolling::{self, RollingFileAppender};
|
|
use tracing_subscriber::{
|
|
fmt::{self, fmt, format::FmtSpan},
|
|
layer::SubscriberExt,
|
|
registry,
|
|
util::SubscriberInitExt,
|
|
Layer,
|
|
};
|
|
use tracing_to_home_assistant::TracingToHomeAssistant;
|
|
|
|
mod tracing_to_home_assistant;
|
|
|
|
shadow!(build_info);
|
|
|
|
#[derive(Debug, Parser)]
|
|
struct Args {
|
|
#[arg(env)]
|
|
persistence_directory: Option<PathBuf>,
|
|
|
|
#[arg(env)]
|
|
tracing_directory: Option<PathBuf>,
|
|
#[arg(env, default_value = "")]
|
|
tracing_file_name_prefix: String,
|
|
#[arg(env, default_value = "log")]
|
|
tracing_file_name_suffix: String,
|
|
#[arg(env, default_value_t = 64)]
|
|
tracing_max_log_files: usize,
|
|
}
|
|
|
|
async fn real_main(
|
|
Args {
|
|
persistence_directory,
|
|
tracing_directory,
|
|
tracing_file_name_prefix,
|
|
tracing_file_name_suffix,
|
|
tracing_max_log_files,
|
|
}: Args,
|
|
home_assistant: HomeAssistant,
|
|
) -> ! {
|
|
let tracing_to_directory_res = tracing_directory
|
|
.map(|tracing_directory| {
|
|
tracing_appender::rolling::Builder::new()
|
|
.filename_prefix(tracing_file_name_prefix)
|
|
.filename_suffix(tracing_file_name_suffix)
|
|
.max_log_files(tracing_max_log_files)
|
|
.build(tracing_directory)
|
|
.map(tracing_appender::non_blocking)
|
|
})
|
|
.transpose();
|
|
|
|
let (tracing_to_directory, _guard, tracing_to_directory_initialization_error) =
|
|
match tracing_to_directory_res {
|
|
Ok(tracing_to_directory) => match tracing_to_directory {
|
|
Some((tracing_to_directory, guard)) => {
|
|
(Some(tracing_to_directory), Some(guard), None)
|
|
}
|
|
None => (None, None, None),
|
|
},
|
|
Err(error) => (None, None, Some(error)),
|
|
};
|
|
|
|
registry()
|
|
.with(
|
|
fmt::layer()
|
|
.pretty()
|
|
.with_span_events(FmtSpan::ACTIVE)
|
|
.with_filter(LevelFilter::from_level(Level::TRACE)),
|
|
)
|
|
.with(TracingToHomeAssistant)
|
|
.with(tracing_to_directory.map(|writer| {
|
|
fmt::layer()
|
|
.pretty()
|
|
.with_span_events(FmtSpan::ACTIVE)
|
|
.with_writer(writer)
|
|
.with_filter(LevelFilter::from_level(Level::TRACE))
|
|
}))
|
|
.init();
|
|
|
|
if let Some(error) = tracing_to_directory_initialization_error {
|
|
tracing::error!(?error, "cannot trace to directory");
|
|
}
|
|
|
|
let built_at = build_info::BUILD_TIME;
|
|
tracing::info!(built_at);
|
|
|
|
tokio::join!(
|
|
async {
|
|
let mut bathroom_mirror_lights = [
|
|
LB130USHandle::new(
|
|
([10, 0, 3, 80], 9999).into(),
|
|
Duration::from_secs(10),
|
|
(64).try_into().unwrap(),
|
|
),
|
|
LB130USHandle::new(
|
|
([10, 0, 3, 82], 9999).into(),
|
|
Duration::from_secs(10),
|
|
(64).try_into().unwrap(),
|
|
),
|
|
];
|
|
|
|
let mut interval = interval(Duration::from_secs(15));
|
|
interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
|
|
|
|
let mut temperature = Kelvin::MIN;
|
|
|
|
loop {
|
|
let instant = interval.tick().await;
|
|
// temperature = temperature.wrapping_add(5173);
|
|
tracing::info!(?temperature);
|
|
|
|
let tasks = bathroom_mirror_lights
|
|
.iter_mut()
|
|
.map(|bathroom_mirror_light| {
|
|
bathroom_mirror_light.turn_to_temperature(temperature)
|
|
});
|
|
let mut tasks = FuturesUnordered::from_iter(tasks);
|
|
|
|
while let Some(result) = tasks.next().await {
|
|
match result {
|
|
Ok(()) => {}
|
|
Err(error_turning_to_temperature) => {
|
|
tracing::error!(?error_turning_to_temperature)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
async {
|
|
let jacob_phone_id = "galaxy_s21_ultra_1";
|
|
let jacob_phone_object_id = ObjectId::from_str(jacob_phone_id).unwrap();
|
|
|
|
let services = Python::attach(|py| home_assistant.services(py)).unwrap();
|
|
|
|
let mut interval = interval(Duration::from_secs(15));
|
|
interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
|
|
|
|
use home_assistant::notify::service::mobile_app::command;
|
|
|
|
// loop {
|
|
// let instant = interval.tick().await;
|
|
// let context: Option<Context<()>> = None;
|
|
// let target: Option<()> = None;
|
|
// let turn_off_result: Result<Py<PyAny>, _> = services.call_service(command::DoNotDisturb {
|
|
// object_id: jacob_phone_object_id.clone(),
|
|
// filter: command::DoNotDisturbFilter::Off,
|
|
// }, context, target, false).await;
|
|
// dbg!(turn_off_result);
|
|
|
|
// let instant = interval.tick().await;
|
|
// let context: Option<Context<()>> = None;
|
|
// let target: Option<()> = None;
|
|
// let alarms_only_result: Result<Py<PyAny>, _> = services.call_service(command::DoNotDisturb {
|
|
// object_id: jacob_phone_object_id.clone(),
|
|
// filter: command::DoNotDisturbFilter::AlarmsOnly,
|
|
// }, context, target, false).await;
|
|
// dbg!(alarms_only_result);
|
|
|
|
// let instant = interval.tick().await;
|
|
// let context: Option<Context<()>> = None;
|
|
// let target: Option<()> = None;
|
|
// let priority_only_result: Result<Py<PyAny>, _> = services.call_service(command::DoNotDisturb {
|
|
// object_id: jacob_phone_object_id.clone(),
|
|
// filter: command::DoNotDisturbFilter::PriorityOnly,
|
|
// }, context, target, false).await;
|
|
// dbg!(priority_only_result);
|
|
|
|
// let instant = interval.tick().await;
|
|
// let context: Option<Context<()>> = None;
|
|
// let target: Option<()> = None;
|
|
// let total_silence_result: Result<Py<PyAny>, _> = services.call_service(command::DoNotDisturb {
|
|
// object_id: jacob_phone_object_id.clone(),
|
|
// filter: command::DoNotDisturbFilter::TotalSilence,
|
|
// }, context, target, false).await;
|
|
// dbg!(total_silence_result);
|
|
// }
|
|
}
|
|
).0
|
|
}
|
|
|
|
#[pyfunction]
|
|
fn main<'py>(py: Python<'py>, home_assistant: HomeAssistant) -> PyResult<Bound<'py, PyAny>> {
|
|
let args = Args::parse();
|
|
pyo3_async_runtimes::tokio::future_into_py::<_, ()>(py, async move {
|
|
real_main(args, home_assistant).await;
|
|
})
|
|
}
|
|
|
|
/// A Python module implemented in Rust.
|
|
#[pymodule]
|
|
fn smart_home_in_rust_with_home_assistant(module: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
module.add_function(wrap_pyfunction!(main, module)?)?;
|
|
Ok(())
|
|
}
|