Skip to content

Commit

Permalink
feat: scene state overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
FruitieX committed Feb 24, 2025
1 parent 0d275fa commit a01505e
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 14 deletions.
47 changes: 46 additions & 1 deletion src/core/event.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::collections::BTreeMap;

use color_eyre::Result;

use crate::types::{
action::Action,
device::{Device, DeviceKey},
dim::DimDescriptor,
event::*,
integration::CustomActionDescriptor,
Expand Down Expand Up @@ -96,6 +99,22 @@ pub async fn handle_event(state: &mut AppState, event: &Event) -> Result<()> {
device,
skip_external_update,
} => {
let scene_config = device
.get_scene_id()
.and_then(|id| state.scenes.find_scene(&id));

let should_store_scene_override = scene_config
.and_then(|scene| {
scene
.overrides
.map(|o| o.contains_key(&device.get_device_key()))
})
.unwrap_or_default();

if should_store_scene_override {
state.scenes.store_scene_override(device, true).await?;
}

let device = device.set_scene(device.get_scene_id().as_ref(), &state.scenes);

state
Expand Down Expand Up @@ -200,7 +219,33 @@ pub async fn handle_event(state: &mut AppState, event: &Event) -> Result<()> {
state.rules.force_trigger_routine(routine_id)?;
}
Event::Action(Action::SetDeviceState(device)) => {
state.devices.set_state(device, false, false);
state.event_tx.send(Event::SetInternalState {
device: device.clone(),
skip_external_update: None,
});
}
Event::Action(Action::ToggleDeviceOverride {
device_keys,
override_state,
}) => {
let affected_devices: BTreeMap<&DeviceKey, &Device> = state
.devices
.get_state()
.0
.iter()
.filter(|(k, _)| device_keys.iter().any(|dk| &dk == k))
.collect();

for device in affected_devices.values() {
state
.scenes
.store_scene_override(device, *override_state)
.await?;
}
state
.scenes
.force_invalidate(&state.devices, &state.groups, state.expr.get_context());
state.send_state_ws(None).await;
}
Event::Action(Action::EvalExpr(expr)) => {
let eval_context = state.expr.get_context();
Expand Down
77 changes: 65 additions & 12 deletions src/core/scenes.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use crate::types::{
device::{
ControllableState, Device, DeviceData, DeviceKey, DeviceRef, DevicesState, SensorDevice,
},
group::GroupId,
scene::{
ActivateSceneDescriptor, FlattenedSceneConfig, FlattenedScenesConfig, SceneConfig,
SceneDeviceConfig, SceneDeviceStates, SceneDevicesConfig, SceneDevicesConfigs, SceneId,
ScenesConfig,
use crate::{
db::actions::db_store_scene,
types::{
device::{
ControllableState, Device, DeviceData, DeviceKey, DeviceRef, DevicesState, SensorDevice,
},
group::GroupId,
scene::{
ActivateSceneDescriptor, FlattenedSceneConfig, FlattenedScenesConfig, SceneConfig,
SceneDeviceConfig, SceneDeviceStates, SceneDevicesConfig, SceneDevicesConfigs, SceneId,
ScenesConfig,
},
},
};
use eyre::Result;
use itertools::Itertools;
use ordered_float::OrderedFloat;

Expand Down Expand Up @@ -244,10 +248,45 @@ impl Scenes {
self.db_scenes = db_scenes;
}

pub async fn store_scene_override(
&mut self,
device: &Device,
store_override: bool,
) -> Result<()> {
if let Some(scene_id) = device.get_scene_id().as_ref() {
let scene = self.find_scene(scene_id);
if let Some(scene) = scene {
let mut scene = scene.clone();
let mut overrides = scene.overrides.unwrap_or_default();

if store_override {
if let Some(state) = device.get_controllable_state() {
let scene_device_config =
SceneDeviceConfig::DeviceState(state.clone().into());
overrides.insert(device.get_device_key(), scene_device_config);
}
} else {
overrides.remove(&device.get_device_key());
}

if overrides.is_empty() {
scene.overrides = None;
} else {
scene.overrides = Some(overrides);
}

db_store_scene(scene_id, &scene).await?;
self.refresh_db_scenes().await;
}
}

Ok(())
}

pub fn get_scenes(&self) -> ScenesConfig {
let mut db_scenes = self.db_scenes.clone();
db_scenes.extend(self.config.clone());
db_scenes
let mut scenes = self.config.clone();
scenes.extend(self.db_scenes.clone());
scenes
}

pub fn get_scene_ids(&self) -> Vec<SceneId> {
Expand Down Expand Up @@ -368,6 +407,13 @@ impl Scenes {
scene_devices_config.insert(device_key, device_config);
}

// Insert devices from scene overrides
if let Some(overrides) = &scene.overrides {
for (device_key, device_config) in overrides {
scene_devices_config.insert(device_key.clone(), device_config.clone());
}
}

Some(scene_devices_config)
}

Expand Down Expand Up @@ -397,9 +443,16 @@ impl Scenes {
})
.collect();

let active_overrides = scene_config
.overrides
.as_ref()
.map(|overrides| overrides.keys().cloned().collect())
.unwrap_or_default();

Some(FlattenedSceneConfig {
name: scene_config.name.clone(),
devices: SceneDeviceStates(devices),
active_overrides,
hidden: scene_config.hidden,
})
}
Expand Down
8 changes: 7 additions & 1 deletion src/types/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use ts_rs::TS;

use super::{
device::Device,
device::{Device, DeviceKey},
dim::DimDescriptor,
integration::CustomActionDescriptor,
rule::ForceTriggerRoutineDescriptor,
Expand Down Expand Up @@ -32,6 +32,12 @@ pub enum Action {
/// Sets device state to given state.
SetDeviceState(Device),

/// Enables / disables device scene state overrides.
ToggleDeviceOverride {
device_keys: Vec<DeviceKey>,
override_state: bool,
},

/// Special category of actions that are only used by UI.
Ui(UiActionDescriptor),

Expand Down
3 changes: 3 additions & 0 deletions src/types/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ pub struct SceneConfig {
pub groups: Option<SceneGroupsConfig>,
pub hidden: Option<bool>,

pub overrides: Option<SceneDevicesConfig>,

/// Evaluates given expression to compute scene config.
#[ts(skip)]
#[serde(skip_serializing)]
Expand All @@ -139,6 +141,7 @@ pub struct SceneDeviceStates(pub BTreeMap<DeviceKey, ControllableState>);
pub struct FlattenedSceneConfig {
pub name: String,
pub devices: SceneDeviceStates,
pub active_overrides: Vec<DeviceKey>,
pub hidden: Option<bool>,
}

Expand Down

0 comments on commit a01505e

Please sign in to comment.