-
-
Notifications
You must be signed in to change notification settings - Fork 16
Wake up automation w/ sunrise effect #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
3fcdc17
dc87fd8
ab023a5
c99342a
0ee02bf
eddd947
fee447a
da67172
4b548c4
8926811
318fab2
9d1344b
86087f0
ace0fdb
33263af
0c7079a
342385b
7de4da5
156d8ff
be5dd0c
db04497
a6621c4
48dbbea
23aa411
cda0c1e
5132724
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| use std::ops::AddAssign; | ||
|
|
||
| use serde::{Deserialize, Deserializer, Serialize}; | ||
| use serde_json::Value; | ||
| use uuid::{uuid, Uuid}; | ||
|
|
||
| use super::DollarRef; | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| pub struct BehaviorScript { | ||
| pub configuration_schema: DollarRef, | ||
| pub description: String, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub max_number_instances: Option<u32>, | ||
| pub metadata: BehaviorScriptMetadata, | ||
| pub state_schema: DollarRef, | ||
| pub supported_features: Vec<String>, | ||
| pub trigger_schema: DollarRef, | ||
| pub version: String, | ||
| } | ||
|
|
||
| impl BehaviorScript { | ||
| pub const WAKE_UP_ID: Uuid = uuid!("ff8957e3-2eb9-4699-a0c8-ad2cb3ede704"); | ||
|
|
||
| #[must_use] | ||
| pub fn wake_up() -> Self { | ||
| Self { | ||
| configuration_schema: DollarRef { | ||
| dref: Some("basic_wake_up_config.json#".to_string()), | ||
| }, | ||
| description: | ||
| "Get your body in the mood to wake up by fading on the lights in the morning." | ||
| .to_string(), | ||
| max_number_instances: None, | ||
| metadata: BehaviorScriptMetadata { | ||
| name: "Basic wake up routine".to_string(), | ||
| category: "automation".to_string(), | ||
| }, | ||
| state_schema: DollarRef { dref: None }, | ||
| supported_features: vec!["style_sunrise".to_string(), "intensity".to_string()], | ||
| trigger_schema: DollarRef { | ||
| dref: Some("trigger.json#".to_string()), | ||
| }, | ||
| version: "0.0.1".to_string(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| pub struct BehaviorScriptMetadata { | ||
| pub name: String, | ||
| pub category: String, | ||
| } | ||
|
|
||
| fn deserialize_optional_field<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error> | ||
| where | ||
| D: Deserializer<'de>, | ||
| { | ||
| Ok(Some(Value::deserialize(deserializer)?)) | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] | ||
| pub struct BehaviorInstance { | ||
| #[serde(default)] | ||
| pub dependees: Vec<Value>, | ||
| pub enabled: bool, | ||
| pub last_error: Option<String>, | ||
| pub metadata: BehaviorInstanceMetadata, | ||
| // Wake up: ff8957e3-2eb9-4699-a0c8-ad2cb3ede704 | ||
| pub script_id: Uuid, | ||
| pub status: Option<String>, | ||
| #[serde( | ||
| default, | ||
| deserialize_with = "deserialize_optional_field", | ||
| skip_serializing_if = "Option::is_none" | ||
| )] | ||
| pub state: Option<Value>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub migrated_from: Option<Value>, | ||
| pub configuration: BehaviorInstanceConfiguration, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] | ||
| #[serde(untagged)] | ||
| pub enum BehaviorInstanceConfiguration { | ||
| Wakeup(WakeupConfiguration), | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||
| pub struct WakeupConfiguration { | ||
| pub end_brightness: f64, | ||
| pub fade_in_duration: configuration::Duration, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub turn_lights_off_after: Option<configuration::Duration>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub style: Option<WakeupStyle>, | ||
| pub when: configuration::When, | ||
| #[serde(rename = "where")] | ||
| pub where_field: Vec<configuration::Where>, | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] | ||
| #[serde(rename_all = "lowercase")] | ||
| pub enum WakeupStyle { | ||
| Sunrise, | ||
| Basic, | ||
| } | ||
|
|
||
| pub mod configuration { | ||
| use std::time::Duration as StdDuration; | ||
|
|
||
| use chrono::Weekday; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| use crate::api::ResourceLink; | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct Duration { | ||
| pub seconds: u32, | ||
| } | ||
|
|
||
| impl Duration { | ||
| pub fn to_std(&self) -> StdDuration { | ||
| StdDuration::from_secs(self.seconds.into()) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct When { | ||
| pub recurrence_days: Option<Vec<Weekday>>, | ||
| pub time_point: TimePoint, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| #[serde(tag = "type", rename_all = "snake_case")] | ||
| pub enum TimePoint { | ||
| Time { time: Time }, | ||
| } | ||
|
|
||
| impl TimePoint { | ||
| pub const fn time(&self) -> &Time { | ||
| match self { | ||
| Self::Time { time } => time, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct Time { | ||
| pub hour: u32, | ||
| pub minute: u32, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct Where { | ||
| pub group: ResourceLink, | ||
| pub items: Option<Vec<ResourceLink>>, | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] | ||
| pub struct BehaviorInstanceMetadata { | ||
| pub name: String, | ||
| } | ||
|
|
||
| #[derive(Debug, Default, Serialize, Deserialize, Clone)] | ||
| pub struct BehaviorInstanceUpdate { | ||
| pub configuration: Option<BehaviorInstanceConfiguration>, | ||
| pub enabled: Option<bool>, | ||
| pub metadata: Option<BehaviorInstanceMetadata>, | ||
| } | ||
|
|
||
| impl BehaviorInstanceUpdate { | ||
| #[must_use] | ||
| pub fn new() -> Self { | ||
| Self::default() | ||
| } | ||
|
|
||
| #[must_use] | ||
| pub fn with_metadata(self, metadata: BehaviorInstanceMetadata) -> Self { | ||
| Self { | ||
| metadata: Some(metadata), | ||
| ..self | ||
| } | ||
| } | ||
|
|
||
| #[must_use] | ||
| pub fn with_enabled(self, enabled: bool) -> Self { | ||
| Self { | ||
| enabled: Some(enabled), | ||
| ..self | ||
| } | ||
| } | ||
|
|
||
| #[must_use] | ||
| pub fn with_configuration(self, configuration: BehaviorInstanceConfiguration) -> Self { | ||
| Self { | ||
| configuration: Some(configuration), | ||
| ..self | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl AddAssign<BehaviorInstanceUpdate> for BehaviorInstance { | ||
| fn add_assign(&mut self, upd: BehaviorInstanceUpdate) { | ||
| if let Some(md) = upd.metadata { | ||
| self.metadata = md; | ||
| } | ||
|
|
||
| if let Some(enabled) = upd.enabled { | ||
| self.enabled = enabled; | ||
| } | ||
|
|
||
| if let Some(configuration) = upd.configuration { | ||
| self.configuration = configuration; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,9 @@ pub struct GroupedLightUpdate { | |
| pub color: Option<ColorUpdate>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub color_temperature: Option<ColorTemperatureUpdate>, | ||
|
|
||
| #[serde(skip)] | ||
| pub transition: Option<f64>, | ||
|
Comment on lines
+60
to
+61
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait, serde skip? So this isn't in the protocol? Are we using this for internal message passing in Bifrost? If so, we have to rework this :( We can't inject Bifrost-specific fields into the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, but I think the main problem is that
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, that's fair, hehe :) I actually wanted to talk to you about that. I think it would be a good idea to split this PR into more than one - at least two. Once we've shined up the rough edges, I think we'll get a pretty clear idea of what goes into improving the hue crate, and then "everything else". So at least there's those two natural "chunks" to work with. And it should be quite easy to split it up later. I think it makes sense to work on the rough edges first.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I completely You're absolutely right - we do need some kind of side channel, and it's certainly not the easiest change to implement. On the other hand, I'm firmly against adding bifrost-specific code to the hue crate. If we open that pandora's box, we'll never close it 😅 So how about this - if you add some clear, eye-catching comments in those 2 places we use Then we can make a refactoring run later, to change the update channel to include a sideband. Deal?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deal 🤝😈 |
||
| } | ||
|
|
||
| impl GroupedLightUpdate { | ||
|
|
@@ -100,4 +103,12 @@ impl GroupedLightUpdate { | |
| ..self | ||
| } | ||
| } | ||
|
|
||
| #[must_use] | ||
| pub fn with_transition(self, transition: Option<impl Into<f64>>) -> Self { | ||
| Self { | ||
| transition: transition.map(Into::into), | ||
| ..self | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice - follows the style completely 👍