feat: 通过增量、绝对量亮度控制。#5.
This commit is contained in:
@@ -75,7 +75,8 @@ impl CoreManager {
|
||||
colors.push(color);
|
||||
}
|
||||
hue = (hue + 1.0) % 360.0;
|
||||
match rpc::manager::Manager::global()
|
||||
match rpc::Manager::global()
|
||||
.await
|
||||
.publish_led_colors(&colors)
|
||||
.await
|
||||
{
|
||||
@@ -187,7 +188,8 @@ impl CoreManager {
|
||||
}
|
||||
// info!("{:?}", colors);
|
||||
global_sub_pixels = HashMap::new();
|
||||
match rpc::manager::Manager::global()
|
||||
match rpc::Manager::global()
|
||||
.await
|
||||
.publish_led_sub_pixels(colors)
|
||||
.await
|
||||
{
|
||||
|
13
src-tauri/src/display/brightness.rs
Normal file
13
src-tauri/src/display/brightness.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
||||
pub enum Brightness {
|
||||
Relative(u16),
|
||||
Absolute(u16),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
||||
pub struct DisplayBrightness {
|
||||
pub brightness: Brightness,
|
||||
pub display_index: usize,
|
||||
}
|
78
src-tauri/src/display/manager.rs
Normal file
78
src-tauri/src/display/manager.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use ddc_hi::Display;
|
||||
use paris::{info, error};
|
||||
use tokio::sync::{broadcast, OnceCell};
|
||||
|
||||
use crate::{display::Brightness, rpc};
|
||||
|
||||
use super::DisplayBrightness;
|
||||
use ddc_hi::Ddc;
|
||||
|
||||
pub struct Manager {}
|
||||
|
||||
impl Manager {
|
||||
pub async fn global() -> &'static Self {
|
||||
static DISPLAY_MANAGER: OnceCell<Manager> = OnceCell::const_new();
|
||||
|
||||
DISPLAY_MANAGER.get_or_init(|| Self::create()).await
|
||||
}
|
||||
|
||||
pub async fn create() -> Self {
|
||||
let rpc = rpc::Manager::global().await;
|
||||
|
||||
let rx = rpc.client().subscribe_change_display_brightness_rx();
|
||||
tokio::spawn(Self::subscribe_display_brightness(rx));
|
||||
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub async fn subscribe_display_brightness(mut rx: broadcast::Receiver<DisplayBrightness>) {
|
||||
while let Ok(display_brightness) = rx.recv().await {
|
||||
if let Err(err) = Self::set_display_brightness(display_brightness) {
|
||||
error!("set_display_brightness failed. {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_display_brightness(display_brightness: DisplayBrightness) -> anyhow::Result<()> {
|
||||
match Display::enumerate().get_mut(display_brightness.display_index) {
|
||||
Some(display) => match display.handle.get_vcp_feature(0x10) {
|
||||
Ok(curr_brightness) => {
|
||||
let curr = curr_brightness.value();
|
||||
info!("curr_brightness: {:?}", curr);
|
||||
let mut target = match display_brightness.brightness {
|
||||
Brightness::Relative(v) => v + curr,
|
||||
Brightness::Absolute(v) => v,
|
||||
};
|
||||
if target.gt(&curr_brightness.maximum()) {
|
||||
target = curr_brightness.maximum();
|
||||
} else if target.lt(&0) {
|
||||
target = 0;
|
||||
}
|
||||
display
|
||||
.handle
|
||||
.set_vcp_feature(0x10, target)
|
||||
.map_err(|err| anyhow::anyhow!("can not set brightness. {:?}", err))?;
|
||||
}
|
||||
Err(err) => {
|
||||
info!(
|
||||
"can not get display#{} brightness. {:?}",
|
||||
display_brightness.display_index, err
|
||||
);
|
||||
if let Brightness::Absolute(v) = display_brightness.brightness {
|
||||
display
|
||||
.handle
|
||||
.set_vcp_feature(0x10, v)
|
||||
.map_err(|err| anyhow::anyhow!("can not set brightness. {:?}", err))?;
|
||||
};
|
||||
}
|
||||
},
|
||||
None => {
|
||||
anyhow::bail!(
|
||||
"display#{} was not found.",
|
||||
display_brightness.display_index
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
8
src-tauri/src/display/mod.rs
Normal file
8
src-tauri/src/display/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
mod brightness;
|
||||
mod manager;
|
||||
|
||||
pub use brightness::*;
|
||||
pub use manager::*;
|
||||
|
||||
|
||||
|
@@ -5,24 +5,28 @@
|
||||
#![feature(bool_to_option)]
|
||||
|
||||
mod core;
|
||||
mod display;
|
||||
mod picker;
|
||||
mod rpc;
|
||||
|
||||
use crate::core::AmbientLightMode;
|
||||
use crate::core::CoreManager;
|
||||
use once_cell::sync::OnceCell;
|
||||
use paris::*;
|
||||
use picker::config::DisplayConfig;
|
||||
use picker::manager::Picker;
|
||||
use picker::screenshot::ScreenshotDto;
|
||||
use tauri::async_runtime::Mutex;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
static GET_SCREENSHOT_LOCK: OnceCell<Mutex<bool>> = OnceCell::new();
|
||||
|
||||
#[tauri::command]
|
||||
async fn take_snapshot() -> Vec<ScreenshotDto> {
|
||||
info!("Hi?");
|
||||
let _lock = GET_SCREENSHOT_LOCK.get_or_init(|| Mutex::new(false)).lock().await;
|
||||
let _lock = GET_SCREENSHOT_LOCK
|
||||
.get_or_init(|| Mutex::new(false))
|
||||
.lock()
|
||||
.await;
|
||||
info!("Hi!");
|
||||
let manager = Picker::global().await;
|
||||
|
||||
@@ -88,8 +92,7 @@ async fn play_mode(target_mode: AmbientLightMode) {
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
rpc::manager::Manager::global();
|
||||
async fn main() {display::Manager::global().await;
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
take_snapshot,
|
||||
|
@@ -1,23 +1,35 @@
|
||||
use crate::picker::led_color::LedColor;
|
||||
use paris::error;
|
||||
use tokio::sync::{broadcast, OnceCell};
|
||||
|
||||
use super::mqtt::MqttConnection;
|
||||
use once_cell::sync::OnceCell;
|
||||
use crate::{display, picker::led_color::LedColor};
|
||||
|
||||
use super::mqtt::MqttRpc;
|
||||
|
||||
pub struct Manager {
|
||||
mqtt: MqttConnection,
|
||||
client: MqttRpc,
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn global() -> &'static Self {
|
||||
static RPC_MANAGER: OnceCell<Manager> = OnceCell::new();
|
||||
pub async fn global() -> &'static Self {
|
||||
static RPC_MANAGER: OnceCell<Manager> = OnceCell::const_new();
|
||||
|
||||
RPC_MANAGER.get_or_init(|| Manager::new())
|
||||
RPC_MANAGER.get_or_init(|| Manager::new()).await
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut mqtt = MqttConnection::new();
|
||||
mqtt.initialize();
|
||||
Self { mqtt }
|
||||
pub async fn new() -> Self {
|
||||
let mut mqtt = MqttRpc::new();
|
||||
let initialized = match mqtt.initialize().await {
|
||||
Ok(_) => true,
|
||||
Err(err) => {
|
||||
error!("initialize for mqtt was failed. {:?}", err);
|
||||
false
|
||||
}
|
||||
};
|
||||
Self {
|
||||
client: mqtt,
|
||||
initialized,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn publish_led_colors(&self, colors: &Vec<LedColor>) -> anyhow::Result<()> {
|
||||
@@ -27,29 +39,14 @@ impl Manager {
|
||||
.flatten()
|
||||
.collect::<Vec<u8>>();
|
||||
|
||||
self.mqtt
|
||||
.client
|
||||
.publish(
|
||||
"display-ambient-light/desktop/colors",
|
||||
rumqttc::QoS::AtLeastOnce,
|
||||
false,
|
||||
payload,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| anyhow::anyhow!("mqtt publish failed. {}", error))
|
||||
self.publish_led_sub_pixels(payload).await
|
||||
}
|
||||
|
||||
pub async fn publish_led_sub_pixels(&self, payload: Vec<u8>) -> anyhow::Result<()> {
|
||||
self.client.publish_led_sub_pixels(payload).await
|
||||
}
|
||||
|
||||
self.mqtt
|
||||
.client
|
||||
.publish(
|
||||
"display-ambient-light/desktop/colors",
|
||||
rumqttc::QoS::AtLeastOnce,
|
||||
false,
|
||||
payload,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| anyhow::anyhow!("mqtt publish failed. {}", error))
|
||||
pub fn client(&self) -> &MqttRpc {
|
||||
&self.client
|
||||
}
|
||||
}
|
||||
|
@@ -1,2 +1,4 @@
|
||||
pub mod manager;
|
||||
pub mod mqtt;
|
||||
mod manager;
|
||||
pub mod mqtt;
|
||||
|
||||
pub use manager::*;
|
@@ -1,41 +1,106 @@
|
||||
use rumqttc::{AsyncClient, MqttOptions, QoS};
|
||||
use crate::display;
|
||||
use image::EncodableLayout;
|
||||
use paris::warn;
|
||||
use rumqttc::{AsyncClient, Event, Incoming, MqttOptions, QoS};
|
||||
use serde::Deserialize;
|
||||
use std::time::Duration;
|
||||
use time::{format_description, OffsetDateTime};
|
||||
use tokio::task;
|
||||
use tracing::warn;
|
||||
use tokio::{sync::broadcast, task};
|
||||
|
||||
pub struct MqttConnection {
|
||||
pub client: AsyncClient,
|
||||
const DISPLAY_TOPIC: &'static str = "display-ambient-light/display";
|
||||
const DISPLAY_BRIGHTNESS_TOPIC: &'static str = "display-ambient-light/display/brightness";
|
||||
|
||||
pub struct MqttRpc {
|
||||
client: AsyncClient,
|
||||
change_display_brightness_tx: broadcast::Sender<display::DisplayBrightness>,
|
||||
}
|
||||
|
||||
impl MqttConnection {
|
||||
impl MqttRpc {
|
||||
pub fn new() -> Self {
|
||||
let mut options = MqttOptions::new("rumqtt-async", "192.168.31.11", 1883);
|
||||
options.set_keep_alive(Duration::from_secs(5));
|
||||
|
||||
let (client, mut eventloop) = AsyncClient::new(options, 10);
|
||||
|
||||
let (change_display_brightness_tx, _) =
|
||||
broadcast::channel::<display::DisplayBrightness>(16);
|
||||
|
||||
let change_display_brightness_tx2 = change_display_brightness_tx.clone();
|
||||
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
match eventloop.poll().await {
|
||||
Ok(_) => {}
|
||||
Ok(notification) => {
|
||||
let handled = || -> anyhow::Result<()> {
|
||||
println!("MQTT notification = {:?}", notification);
|
||||
if let Event::Incoming(notification) = notification {
|
||||
if let Incoming::Publish(notification) = notification {
|
||||
match notification.topic.as_str() {
|
||||
DISPLAY_BRIGHTNESS_TOPIC => {
|
||||
let payload_text = String::from_utf8(
|
||||
notification.payload.as_bytes().to_owned(),
|
||||
)
|
||||
.map_err(|err| {
|
||||
anyhow::anyhow!("can not parse json. {:?}", err)
|
||||
})?;
|
||||
let display_brightness: display::DisplayBrightness =
|
||||
serde_json::from_str(payload_text.as_str())
|
||||
.map_err(|err| {
|
||||
anyhow::anyhow!(
|
||||
"can not deserialize display brightness. {:?}",
|
||||
err
|
||||
)
|
||||
})?;
|
||||
change_display_brightness_tx2
|
||||
.send(display_brightness)
|
||||
.map_err(|err| {
|
||||
anyhow::anyhow!(
|
||||
"can not broadcast display brightness. {:?}",
|
||||
err
|
||||
)
|
||||
})?;
|
||||
}
|
||||
&_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
if let Err(err) = handled() {
|
||||
warn!("handle notification was failed. Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
println!("MQTT Error Event = {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Self { client }
|
||||
|
||||
Self {
|
||||
client,
|
||||
change_display_brightness_tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self) {
|
||||
self.subscribe_board();
|
||||
pub async fn initialize(&mut self) -> anyhow::Result<()> {
|
||||
self.subscribe_board().await?;
|
||||
self.subscribe_display().await?;
|
||||
self.broadcast_desktop_online();
|
||||
anyhow::Ok(())
|
||||
}
|
||||
|
||||
async fn subscribe_board(&self) {
|
||||
async fn subscribe_board(&self) -> anyhow::Result<()> {
|
||||
self.client
|
||||
.subscribe("display-ambient-light/board/#", QoS::AtMostOnce)
|
||||
.await;
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!("subscribe board failed. {:?}", err))
|
||||
}
|
||||
async fn subscribe_display(&self) -> anyhow::Result<()> {
|
||||
self.client
|
||||
.subscribe(format!("{}/#", DISPLAY_TOPIC), QoS::AtMostOnce)
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!("subscribe board failed. {:?}", err))
|
||||
}
|
||||
|
||||
fn broadcast_desktop_online(&mut self) {
|
||||
@@ -69,4 +134,20 @@ impl MqttConnection {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn publish_led_sub_pixels(&self, payload: Vec<u8>) -> anyhow::Result<()> {
|
||||
self.client
|
||||
.publish(
|
||||
"display-ambient-light/desktop/colors",
|
||||
rumqttc::QoS::AtLeastOnce,
|
||||
false,
|
||||
payload,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| anyhow::anyhow!("mqtt publish failed. {}", error))
|
||||
}
|
||||
|
||||
pub fn subscribe_change_display_brightness_rx(&self) -> broadcast::Receiver<display::DisplayBrightness> {
|
||||
self.change_display_brightness_tx.subscribe()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user