paralell
This commit is contained in:
+7
-2
@@ -8,14 +8,19 @@ anyhow = "1.0"
|
||||
config = "0.15.21"
|
||||
ctrlc = "3.5.2"
|
||||
futures = "0.3"
|
||||
grim-rs = "0.1.6"
|
||||
image = "0.25.10"
|
||||
image-compare = "0.5.0"
|
||||
libc = "0.2.183"
|
||||
okmain = "0.2.0"
|
||||
reqwest = { version = "0.13.2", features = ["json"] }
|
||||
rgb = "0.8"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
tokio = { version = "1.50.0", features = ["full"] }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
grim-rs = "0.1.6"
|
||||
libc = "0.2.183"
|
||||
tray-item = { version = "0.10.0", features = ["ksni"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
tray-item = { version = "0.10.0" }
|
||||
|
||||
@@ -34,8 +34,9 @@ ha_url = "http://192.168.1.100:8123"
|
||||
# Your Long-Lived Access Token (Required)
|
||||
ha_token = "ey..."
|
||||
|
||||
# Target Frames Per Second to process (default: 1)
|
||||
target_fps = 1
|
||||
# Target Frames Per Second for light updates (default: 15)
|
||||
# This is the MAXIMUM rate - lights only update when color actually changes
|
||||
target_fps = 15
|
||||
|
||||
# Color smoothing factor from 0.0 to 1.0 (default: 0.0)
|
||||
# Higher values mean slower, smoother color transitions.
|
||||
@@ -81,7 +82,9 @@ nix develop -c cargo run --release
|
||||
|
||||
|
||||
|
||||
# TODO: Parallel Event Loop Restructuring
|
||||
# TODO: Parallel Event Loop Restructuring (COMPLETED)
|
||||
|
||||
This architecture has been implemented. See `src/eventloop.rs` for details.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
|
||||
+26
-38
@@ -1,43 +1,31 @@
|
||||
# # Configuration for Ambilight Home Assistant Integration
|
||||
#
|
||||
# # The base URL of your Home Assistant instance.
|
||||
# # Example: "http://192.168.1.100:8123" or "https://ha.yourdomain.com"
|
||||
# ha_url = "http://192.168.1.238:8123"
|
||||
#
|
||||
# # A Long-Lived Access Token from Home Assistant.
|
||||
# # You can generate this in Home Assistant by going to your user profile.
|
||||
# ha_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI0YjNjMmYxNGE3NWM0YmJjYmNhYzA0YWFmYTlhOTVkNCIsImlhdCI6MTc3MzUyOTcwNCwiZXhwIjoyMDg4ODg5NzA0fQ.I923kml1zWqYYZk-0JSunexbo5NKcgehhwmG-T8jBcg"
|
||||
#
|
||||
# # Target updates per second (FPS)
|
||||
# target_fps = 3
|
||||
#
|
||||
# # Color smoothing factor (0.0 to 1.0)
|
||||
# # 0.0 = no smoothing (instant updates)
|
||||
# # 1.0 = completely static (no updates)
|
||||
# smoothing = 0.1
|
||||
#
|
||||
# # Specific lights to control. If empty, all supported color lights are used.
|
||||
# # Example: ["light.living_room", "light.bedroom"]
|
||||
# lights = []
|
||||
#
|
||||
# # Restore lights to their original state/color when the program exits.
|
||||
# restore_on_exit = true
|
||||
#
|
||||
# # Time (in seconds) it takes to fade to the new color. 0.0 means instant.
|
||||
# transition = 0.1
|
||||
#
|
||||
# # Minimum percentage of difference required between frames to trigger an update (e.g. 1.0 = 1%)
|
||||
# # Helps with flickering on static images, from the non deterministic color algorithm.
|
||||
# min_diff_percent = 5.0
|
||||
|
||||
|
||||
|
||||
# Configuration for Ambilight Home Assistant Integration
|
||||
|
||||
# The base URL of your Home Assistant instance.
|
||||
# Example: "http://192.168.1.100:8123" or "https://ha.yourdomain.com"
|
||||
ha_url = "https://homeassistant.pvv.ntnu.no:8123"
|
||||
ha_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3ZmIxMzQ4NzE5ZmU0MmMyYTA5M2JiNWU2NjQ2OWJmYSIsImlhdCI6MTc3MzY2MzQwNCwiZXhwIjoyMDg5MDIzNDA0fQ.JKn8hPvK7aPS-SuXKKEREk7vva-PGXehLUH7ijoSbjQ"
|
||||
target_fps = 15.0
|
||||
|
||||
# A Long-Lived Access Token from Home Assistant.
|
||||
# You can generate this in Home Assistant by going to your user profile.
|
||||
ha_token = ""
|
||||
|
||||
# Screenshot capture rate (FPS). How often to capture and analyze the screen.
|
||||
screenshot_fps = 15
|
||||
|
||||
# Maximum light update rate (FPS). Lights only update when color actually changes.
|
||||
# This acts as a rate limiter to prevent spamming Home Assistant.
|
||||
target_fps = 3
|
||||
|
||||
# Color smoothing factor (0.0 to 1.0)
|
||||
# Higher values = slower, smoother transitions (0.0 = instant, 1.0 = no movement)
|
||||
smoothing = 0.6
|
||||
lights = [ "light.fargelys" ]
|
||||
|
||||
# Specific lights to control. If empty, all supported color lights are used.
|
||||
# Example: ["light.living_room", "light.bedroom"]
|
||||
lights = ["light.fargelys"]
|
||||
|
||||
# Restore lights to their original state/color when the program exits.
|
||||
restore_on_exit = true
|
||||
transition = 0.0
|
||||
|
||||
# Minimum percentage of difference required between frames to trigger an update.
|
||||
# Helps prevent flickering on static screens due to non-deterministic color algorithm.
|
||||
min_diff_percent = 10.0
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
rustfmt
|
||||
clippy
|
||||
rustPlatform.bindgenHook
|
||||
pkgs.pkgsCross.mingwW64.windows.mingw_w64_headers
|
||||
dbus
|
||||
gtk3
|
||||
glib
|
||||
|
||||
+41
-15
@@ -20,23 +20,25 @@ pub fn run_loop(
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
let screenshot_fps = settings.screenshot_fps.max(1);
|
||||
let target_fps = settings.target_fps.max(1);
|
||||
let screenshot_interval = Duration::from_millis((1000 / screenshot_fps) as u64);
|
||||
let target_interval = Duration::from_millis((1000 / target_fps) as u64);
|
||||
let smoothing = settings.smoothing.clamp(0.0, 1.0);
|
||||
let min_diff_percent = settings.min_diff_percent;
|
||||
let max_fps = settings.target_fps.max(1);
|
||||
let min_update_interval = Duration::from_millis((1000 / max_fps) as u64);
|
||||
|
||||
println!(
|
||||
"Starting Ambilight loop (screenshot: {} FPS, light update: {} FPS)...",
|
||||
screenshot_fps, target_fps
|
||||
"Starting Ambilight loop (screenshot: {} FPS, max light updates: {} FPS)...",
|
||||
screenshot_fps, max_fps
|
||||
);
|
||||
|
||||
let exit_flag = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let is_running_screenshot = is_running.clone();
|
||||
let state_screenshot = state.clone();
|
||||
let settings_screenshot = settings.clone();
|
||||
let exit_flag_screenshot = exit_flag.clone();
|
||||
|
||||
let screenshot_handle = thread::spawn(move || {
|
||||
loop {
|
||||
while !exit_flag_screenshot.load(Ordering::Relaxed) {
|
||||
let start = Instant::now();
|
||||
|
||||
if is_running_screenshot.load(Ordering::Relaxed) {
|
||||
@@ -77,14 +79,23 @@ pub fn run_loop(
|
||||
let ha_client_smoothing = ha_client.clone();
|
||||
let target_lights_smoothing = target_lights.clone();
|
||||
let is_running_smoothing = is_running.clone();
|
||||
let exit_flag_smoothing = exit_flag.clone();
|
||||
let min_update_interval_clone = min_update_interval;
|
||||
|
||||
rt.block_on(async {
|
||||
let mut last_sent_color: Option<RGB<u8>> = None;
|
||||
let color_threshold: f32 = 5.0;
|
||||
let mut last_calc_time = Instant::now();
|
||||
let calc_interval = Duration::from_millis(50);
|
||||
let mut last_update_time = Instant::now() - min_update_interval_clone;
|
||||
|
||||
loop {
|
||||
let start = Instant::now();
|
||||
|
||||
if exit_flag_smoothing.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
|
||||
if is_running_smoothing.load(Ordering::Relaxed) {
|
||||
if start.duration_since(last_calc_time) >= calc_interval {
|
||||
let active_screenshot = {
|
||||
@@ -123,26 +134,41 @@ pub fn run_loop(
|
||||
(guard.r.round() as u8, guard.g.round() as u8, guard.b.round() as u8)
|
||||
};
|
||||
|
||||
if let Err(e) = ha_client_smoothing
|
||||
.set_lights_color_parallel(&target_lights_smoothing, r, g, b)
|
||||
.await
|
||||
{
|
||||
eprintln!("Failed to update lights: {}", e);
|
||||
let should_update = match last_sent_color {
|
||||
None => true,
|
||||
Some(last) => {
|
||||
let dr = (r as f32 - last.r as f32).abs();
|
||||
let dg = (g as f32 - last.g as f32).abs();
|
||||
let db = (b as f32 - last.b as f32).abs();
|
||||
dr > color_threshold || dg > color_threshold || db > color_threshold
|
||||
}
|
||||
};
|
||||
|
||||
if should_update && start.duration_since(last_update_time) >= min_update_interval_clone {
|
||||
if let Err(e) = ha_client_smoothing
|
||||
.set_lights_color_parallel(&target_lights_smoothing, r, g, b)
|
||||
.await
|
||||
{
|
||||
eprintln!("Failed to update lights: {}", e);
|
||||
} else {
|
||||
last_sent_color = Some(RGB::new(r, g, b));
|
||||
last_update_time = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
let sleep_duration = target_interval.saturating_sub(elapsed);
|
||||
|
||||
let poll_interval = Duration::from_millis(10);
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(sleep_duration) => {}
|
||||
_ = tokio::time::sleep(poll_interval) => {}
|
||||
_ = exit_rx.recv() => {
|
||||
println!("\nExit signal received, stopping loop...");
|
||||
exit_flag_smoothing.store(true, Ordering::Relaxed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exit_flag.store(true, Ordering::Relaxed);
|
||||
let _ = screenshot_handle.join();
|
||||
}
|
||||
|
||||
+4
-1
@@ -6,7 +6,10 @@ mod state;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tray_item::{IconSource, TrayItem};
|
||||
|
||||
#[cfg(unix)]
|
||||
use tray_item::IconSource;
|
||||
use tray_item::TrayItem;
|
||||
|
||||
fn main() {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
+2
-1
@@ -211,7 +211,8 @@ mod ffi {
|
||||
mod ffi {
|
||||
#![allow(non_snake_case, dead_code)]
|
||||
use super::{Pixel, ScreenResult, Screenshot};
|
||||
use libc::{c_int, c_long, c_uint, c_void};
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::{c_int, c_long, c_uint};
|
||||
use std::mem::size_of;
|
||||
|
||||
type PVOID = *mut c_void;
|
||||
|
||||
+2
-2
@@ -37,11 +37,11 @@ fn default_restore_on_exit() -> bool {
|
||||
}
|
||||
|
||||
fn default_target_fps() -> u32 {
|
||||
1
|
||||
3
|
||||
}
|
||||
|
||||
fn default_screenshot_fps() -> u32 {
|
||||
3
|
||||
15
|
||||
}
|
||||
|
||||
fn default_smoothing() -> f32 {
|
||||
|
||||
Reference in New Issue
Block a user