Compare commits
4 Commits
fde59d05ab
...
239ae331cd
| Author | SHA1 | Date | |
|---|---|---|---|
| 239ae331cd | |||
| a1e27181a8 | |||
| a96dce2771 | |||
| e42397e072 |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2101,6 +2101,7 @@ dependencies = [
|
|||||||
"deranged",
|
"deranged",
|
||||||
"driver-kasa",
|
"driver-kasa",
|
||||||
"emitter-and-signal",
|
"emitter-and-signal",
|
||||||
|
"futures",
|
||||||
"home-assistant",
|
"home-assistant",
|
||||||
"im",
|
"im",
|
||||||
"persisted",
|
"persisted",
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
use palette::{encoding::Srgb, Hsv, IntoColor};
|
use palette::{encoding::Srgb, Hsv, IntoColor};
|
||||||
use protocol::light::{GetState, Kelvin, SetState, TurnToColor, TurnToTemperature};
|
use protocol::light::{GetState, SetState, TurnToColor, TurnToTemperature};
|
||||||
use snafu::{ResultExt, Snafu};
|
use snafu::{ResultExt, Snafu};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::{HandleError, LB130USHandle},
|
connection::{HandleError, LB130USHandle},
|
||||||
messages::{
|
messages::{
|
||||||
Angle, Hsb, LightState, Off, On, Percentage, SetLightHsv, SetLightLastOn, SetLightOff,
|
Angle, Hsb, LightState, Off, On, Percentage, SetLightHsv, SetLightKelvin, SetLightLastOn,
|
||||||
SetLightStateArgs, SetLightTo,
|
SetLightOff, SetLightStateArgs, SetLightTo,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,11 +64,33 @@ impl SetState for LB130USHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TurnToTemperature for LB130USHandle {
|
#[derive(Debug, Snafu)]
|
||||||
type Error = Infallible; // TODO
|
#[snafu(module)]
|
||||||
|
pub enum TurnToTemperatureError {
|
||||||
|
HandleError { source: HandleError },
|
||||||
|
}
|
||||||
|
|
||||||
async fn turn_to_temperature(&mut self, temperature: Kelvin) -> Result<(), Self::Error> {
|
impl TurnToTemperature for LB130USHandle {
|
||||||
todo!()
|
type Error = TurnToTemperatureError;
|
||||||
|
|
||||||
|
async fn turn_to_temperature(
|
||||||
|
&mut self,
|
||||||
|
temperature: protocol::light::Kelvin,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
// TODO: re-evaluate saturating
|
||||||
|
let color_temp = crate::messages::Kelvin::new_saturating(temperature.get());
|
||||||
|
|
||||||
|
self.set_light_state(SetLightStateArgs {
|
||||||
|
to: SetLightTo::Kelvin(SetLightKelvin {
|
||||||
|
on_off: On,
|
||||||
|
color_temp,
|
||||||
|
}),
|
||||||
|
transition: None,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.context(turn_to_temperature_error::HandleSnafu)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -388,13 +388,19 @@ pub struct SetLightHsv {
|
|||||||
pub hsb: Hsb,
|
pub hsb: Hsb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct SetLightKelvin {
|
||||||
|
pub on_off: On,
|
||||||
|
pub color_temp: Kelvin,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum SetLightTo {
|
pub enum SetLightTo {
|
||||||
Off(SetLightOff),
|
Off(SetLightOff),
|
||||||
LastOn(SetLightLastOn),
|
LastOn(SetLightLastOn),
|
||||||
Hsv(SetLightHsv),
|
Hsv(SetLightHsv),
|
||||||
// TODO: kelvin
|
Kelvin(SetLightKelvin),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, derive_more::From)]
|
#[derive(Debug, Clone, derive_more::From)]
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ clap = { version = "4", features = ["derive", "env"] }
|
|||||||
deranged = { workspace = true, features = ["serde"] }
|
deranged = { workspace = true, features = ["serde"] }
|
||||||
driver-kasa = { path = "../driver/kasa" }
|
driver-kasa = { path = "../driver/kasa" }
|
||||||
emitter-and-signal = { path = "../emitter-and-signal" }
|
emitter-and-signal = { path = "../emitter-and-signal" }
|
||||||
|
futures = "0.3"
|
||||||
home-assistant = { path = "../home-assistant", features = ["tracing"] }
|
home-assistant = { path = "../home-assistant", features = ["tracing"] }
|
||||||
im = { version = "15.1.0", features = ["rayon"] }
|
im = { version = "15.1.0", features = ["rayon"] }
|
||||||
persisted = { path = "../persisted" }
|
persisted = { path = "../persisted" }
|
||||||
|
|||||||
@@ -2,15 +2,19 @@ use std::{num::NonZeroUsize, path::PathBuf, str::FromStr, time::Duration};
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use driver_kasa::connection::LB130USHandle;
|
use driver_kasa::connection::LB130USHandle;
|
||||||
|
use futures::stream::{FuturesUnordered, StreamExt};
|
||||||
use home_assistant::{
|
use home_assistant::{
|
||||||
event::context::context::Context, home_assistant::HomeAssistant, light::HomeAssistantLight,
|
event::context::context::Context, home_assistant::HomeAssistant, light::HomeAssistantLight,
|
||||||
notify::service::mobile_app::StandardNotification, object_id::ObjectId,
|
notify::service::mobile_app::StandardNotification, object_id::ObjectId,
|
||||||
};
|
};
|
||||||
use persisted::{persisted, Config, Keyspace, Partition, PartitionCreateOptions};
|
use persisted::{persisted, Config, Keyspace, Partition, PartitionCreateOptions};
|
||||||
use protocol::light::{IsOff, IsOn};
|
use protocol::light::{IsOff, IsOn, Kelvin, TurnToTemperature};
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use shadow_rs::shadow;
|
use shadow_rs::shadow;
|
||||||
use tokio::{task::spawn_blocking, time::interval};
|
use tokio::{
|
||||||
|
task::{spawn_blocking, JoinSet},
|
||||||
|
time::{interval, MissedTickBehavior},
|
||||||
|
};
|
||||||
use tracing::{level_filters::LevelFilter, Level};
|
use tracing::{level_filters::LevelFilter, Level};
|
||||||
use tracing_appender::rolling::{self, RollingFileAppender};
|
use tracing_appender::rolling::{self, RollingFileAppender};
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
@@ -97,148 +101,40 @@ async fn real_main(
|
|||||||
let built_at = build_info::BUILD_TIME;
|
let built_at = build_info::BUILD_TIME;
|
||||||
tracing::info!(built_at);
|
tracing::info!(built_at);
|
||||||
|
|
||||||
// let lamp = HomeAssistantLight {
|
let mut bathroom_mirror_lights = [
|
||||||
// home_assistant,
|
LB130USHandle::new(
|
||||||
// object_id: ObjectId::from_str("jacob_s_lamp_side").unwrap(),
|
([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 ip = [10, 0, 3, 71];
|
let mut interval = interval(Duration::from_secs(15));
|
||||||
// let port = 9999;
|
interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
|
||||||
|
|
||||||
// let some_light = LB130USHandle::new(
|
let mut temperature = Kelvin::MIN;
|
||||||
// (ip, port).into(),
|
|
||||||
// Duration::from_secs(10),
|
|
||||||
// (64).try_into().unwrap(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
let mut int = interval(Duration::from_secs(170));
|
|
||||||
let mut value = 0;
|
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_secs(15)).await;
|
|
||||||
|
|
||||||
let services = Python::attach(|py| home_assistant.services(py)).unwrap();
|
|
||||||
loop {
|
|
||||||
int.tick().await;
|
|
||||||
|
|
||||||
tracing::debug!(?value);
|
|
||||||
// let service_result: Result<Py<PyAny>, _> = services
|
|
||||||
// .call_service(
|
|
||||||
// StandardNotification::builder()
|
|
||||||
// .object_id(ObjectId("galaxy_s21_ultra_1".parse().unwrap()))
|
|
||||||
// .message(format!("The counter is now {value:?}").parse().unwrap())
|
|
||||||
// .title("New value of the counter".into())
|
|
||||||
// .build(),
|
|
||||||
// Option::<Context<()>>::None,
|
|
||||||
// Option::<()>::None,
|
|
||||||
// false,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// tracing::debug!(?service_result);
|
|
||||||
|
|
||||||
value += 1;
|
|
||||||
|
|
||||||
// tracing::info!("about to call get_sysinfo");
|
|
||||||
// let sysinfo_res = some_light.get_sysinfo().await;
|
|
||||||
// tracing::info!(?sysinfo_res, "got sys info");
|
|
||||||
|
|
||||||
// let is_on = some_light.is_on().await;
|
|
||||||
// tracing::info!(?is_on);
|
|
||||||
// let is_off = some_light.is_off().await;
|
|
||||||
// tracing::info!(?is_off);
|
|
||||||
|
|
||||||
// let is_on = lamp.is_on().await;
|
|
||||||
// tracing::info!(?is_on);
|
|
||||||
// let is_off = lamp.is_off().await;
|
|
||||||
// tracing::info!(?is_off);
|
|
||||||
|
|
||||||
// let something = lamp.turn_on().await;
|
|
||||||
// tracing::info!(?something);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(persistence_directory) = persistence_directory {
|
|
||||||
let config = Config::new(persistence_directory);
|
|
||||||
let keyspace = Keyspace::open(config).unwrap(); // TODO: just debugging and experiencing it
|
|
||||||
let partition_name = "trying_this_out_partition";
|
|
||||||
let create_options = PartitionCreateOptions::default();
|
|
||||||
let partition =
|
|
||||||
spawn_blocking(move || keyspace.open_partition(partition_name, create_options))
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
// TODO: just debugging and experiencing it
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let identifier = "0a7wmg09awgmagw97nawg7awg90a8wgn982".into();
|
|
||||||
let buffer = NonZeroUsize::new(128).unwrap();
|
|
||||||
let (setter, signal, task) = persisted(partition, identifier, buffer).await;
|
|
||||||
|
|
||||||
let consumer = tokio::spawn({
|
|
||||||
let mut subscription = signal.subscribe().unwrap();
|
|
||||||
|
|
||||||
let services = Python::attach(|py| home_assistant.services(py)).unwrap();
|
|
||||||
async move {
|
|
||||||
let initial = subscription.get();
|
|
||||||
tracing::debug!(?initial);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tracing::info!("waiting for changed");
|
let instant = interval.tick().await;
|
||||||
subscription.changed().await.unwrap();
|
// temperature = temperature.wrapping_add(5173);
|
||||||
|
tracing::info!(?temperature);
|
||||||
|
|
||||||
let value = subscription.get();
|
let tasks = bathroom_mirror_lights
|
||||||
tracing::debug!(?value);
|
.iter_mut()
|
||||||
|
.map(|bathroom_mirror_light| bathroom_mirror_light.turn_to_temperature(temperature));
|
||||||
|
let mut tasks = FuturesUnordered::from_iter(tasks);
|
||||||
|
|
||||||
// TODO: WIP: DEBUGGING
|
while let Some(result) = tasks.next().await {
|
||||||
let service_result: Result<Py<PyAny>, _> = services
|
match result {
|
||||||
.call_service(
|
Ok(()) => {}
|
||||||
StandardNotification::builder()
|
Err(error_turning_to_temperature) => tracing::error!(?error_turning_to_temperature),
|
||||||
.object_id(ObjectId("galaxy_s21_ultra_1".parse().unwrap()))
|
|
||||||
.message(format!("The counter is now {value:?}").parse().unwrap())
|
|
||||||
.title("New value of the counter".into())
|
|
||||||
.build(),
|
|
||||||
Option::<Context<()>>::None,
|
|
||||||
Option::<()>::None,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
tracing::error!(?service_result);
|
|
||||||
let service_result_introspection = Python::attach(|py| {
|
|
||||||
service_result
|
|
||||||
.map(|ret| ret.bind(py).get_type().name().unwrap().to_string())
|
|
||||||
});
|
|
||||||
tracing::error!(?service_result_introspection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let producer = tokio::spawn({
|
|
||||||
let mut subscription = signal.subscribe().unwrap();
|
|
||||||
async move {
|
|
||||||
let mut int = interval(Duration::from_secs(190));
|
|
||||||
|
|
||||||
loop {
|
|
||||||
int.tick().await;
|
|
||||||
|
|
||||||
let value = subscription.get();
|
|
||||||
tracing::debug!(?value);
|
|
||||||
|
|
||||||
// let mut counter = value.unwrap_or(14u16);
|
|
||||||
// tracing::info!(?counter);
|
|
||||||
|
|
||||||
if let Ok(counter) = value {
|
|
||||||
let counter: u16 = counter + 1;
|
|
||||||
setter.set(counter).await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
producer.await.unwrap();
|
|
||||||
consumer.await.unwrap();
|
|
||||||
task.await.unwrap();
|
|
||||||
|
|
||||||
unreachable!();
|
|
||||||
} else {
|
|
||||||
panic!("please set PERSISTENCE_DIRECTORY while debugging and trying persisted out")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ use std::str::FromStr;
|
|||||||
use pyo3::IntoPyObject;
|
use pyo3::IntoPyObject;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
domain::Domain, entity_id::EntityId, object_id::ObjectId, service::{IntoServiceCall, service_domain::ServiceDomain, service_id::ServiceId}
|
domain::Domain,
|
||||||
|
entity_id::EntityId,
|
||||||
|
object_id::ObjectId,
|
||||||
|
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ use std::str::FromStr;
|
|||||||
use pyo3::IntoPyObject;
|
use pyo3::IntoPyObject;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
domain::Domain, entity_id::EntityId, object_id::ObjectId, service::{IntoServiceCall, service_domain::ServiceDomain, service_id::ServiceId}
|
domain::Domain,
|
||||||
|
entity_id::EntityId,
|
||||||
|
object_id::ObjectId,
|
||||||
|
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
Reference in New Issue
Block a user