Working app

This commit is contained in:
Austen Adler 2024-04-21 21:44:09 -04:00
parent dd978179df
commit 058a076414
16 changed files with 123 additions and 28 deletions

1
.cargo/config.toml Symbolic link
View File

@ -0,0 +1 @@
../config.toml

View File

@ -195,6 +195,7 @@ impl Parameters {
} }
pub trait Pattern: std::fmt::Debug + Send + Sync { pub trait Pattern: std::fmt::Debug + Send + Sync {
fn get_name(&self) -> &'static str;
fn init(&mut self, num_lights: u16) -> PatternResult<()>; fn init(&mut self, num_lights: u16) -> PatternResult<()>;
fn step(&mut self) -> PatternResult<bool>; fn step(&mut self) -> PatternResult<bool>;
fn get_strip(&self) -> ColorIterator; fn get_strip(&self) -> ColorIterator;

View File

@ -50,6 +50,9 @@ impl CarRainbow {
} }
} }
impl Pattern for CarRainbow { impl Pattern for CarRainbow {
fn get_name(&self) -> &'static str {
"CarRainbow"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
self.lights_buf.rotate_left(1); self.lights_buf.rotate_left(1);
// TODO: Not sure if we should go forward or backwards // TODO: Not sure if we should go forward or backwards

View File

@ -71,6 +71,9 @@ impl Collide {
} }
} }
impl Pattern for Collide { impl Pattern for Collide {
fn get_name(&self) -> &'static str {
"Collide"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
// TODO: Better range storage // TODO: Better range storage
// Set the left and right color // Set the left and right color

View File

@ -74,6 +74,9 @@ impl CustomVisualizer {
} }
impl Pattern for CustomVisualizer { impl Pattern for CustomVisualizer {
fn get_name(&self) -> &'static str {
"CustomVisualizer"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
if let Some(c) = &self.cava { if let Some(c) = &self.cava {
let reading = c let reading = c

View File

@ -47,6 +47,9 @@ impl Fade {
} }
} }
impl Pattern for Fade { impl Pattern for Fade {
fn get_name(&self) -> &'static str {
"Fade"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
if self.direction { if self.direction {
if self.step == 254 { if self.step == 254 {

View File

@ -59,6 +59,9 @@ impl Flashing {
} }
impl Pattern for Flashing { impl Pattern for Flashing {
fn get_name(&self) -> &'static str {
"Flashing"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
self.step = self.step.wrapping_add(1).rem_euclid(self.tick_rate); self.step = self.step.wrapping_add(1).rem_euclid(self.tick_rate);
if self.step != 0 { if self.step != 0 {

View File

@ -48,6 +48,9 @@ impl MovingPixel {
} }
impl Pattern for MovingPixel { impl Pattern for MovingPixel {
fn get_name(&self) -> &'static str {
"MovingPixel"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
let len = self.num_lights; let len = self.num_lights;
self.lights_buf.swap( self.lights_buf.swap(

View File

@ -63,6 +63,9 @@ impl MovingRainbow {
} }
} }
impl Pattern for MovingRainbow { impl Pattern for MovingRainbow {
fn get_name(&self) -> &'static str {
"MovingRainbow"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
if self.forward { if self.forward {
self.lights_buf.rotate_left(1); self.lights_buf.rotate_left(1);

View File

@ -75,6 +75,9 @@ impl Orb {
} }
} }
impl Pattern for Orb { impl Pattern for Orb {
fn get_name(&self) -> &'static str {
"Orb"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
if !self.bounces { if !self.bounces {
// If we don't bounce, then just wrap and we're done // If we don't bounce, then just wrap and we're done

View File

@ -64,6 +64,9 @@ impl Slide {
} }
impl Pattern for Slide { impl Pattern for Slide {
fn get_name(&self) -> &'static str {
"Slide"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
self.step = self.step.wrapping_add(1); self.step = self.step.wrapping_add(1);
if self.step.rem_euclid(self.speed as usize) != 0 { if self.step.rem_euclid(self.speed as usize) != 0 {

View File

@ -43,6 +43,9 @@ impl Solid {
} }
} }
impl Pattern for Solid { impl Pattern for Solid {
fn get_name(&self) -> &'static str {
"Solid"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
let ret = !self.has_run; let ret = !self.has_run;
self.has_run = true; self.has_run = true;

View File

@ -42,6 +42,9 @@ impl Visualizer {
} }
impl Pattern for Visualizer { impl Pattern for Visualizer {
fn get_name(&self) -> &'static str {
"Visualizer"
}
fn step(&mut self) -> PatternResult<bool> { fn step(&mut self) -> PatternResult<bool> {
if let Some(c) = &self.cava { if let Some(c) = &self.cava {
let reading = c let reading = c

View File

@ -7,6 +7,7 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
pub enum Message { pub enum Message {
ClearLights, ClearLights,
TurnOn(Option<String>),
ChangePattern(Box<dyn Pattern + Send + Sync>), ChangePattern(Box<dyn Pattern + Send + Sync>),
SetNumLights(u16), SetNumLights(u16),
SetTickTime(u64), SetTickTime(u64),
@ -17,22 +18,22 @@ pub enum Message {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct State { pub struct State {
pub on: bool, pub on: bool,
pub pattern: Option<String>, pub pattern: String,
// brightnes: u8, // brightnes: u8,
pub color: Rgb, pub color: Rgb,
// Off, // Off,
// Pattern // Pattern
} }
impl Default for State { // impl Default for State {
fn default() -> Self { // fn default() -> Self {
Self { // Self {
on: false, // on: false,
pattern: None, // pattern: None,
color: color::BLACK, // color: color::BLACK,
} // }
} // }
} // }
// impl Debug for Message { // impl Debug for Message {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View File

@ -3,7 +3,7 @@ use anyhow::{Context as _, Result};
use common::{ use common::{
color::{self, Gradient, Rgb}, color::{self, Gradient, Rgb},
error::ProgramError, error::ProgramError,
pattern::{MovingRainbow, Solid, SolidParams}, pattern::{MovingRainbow, Parameters, Solid, SolidParams},
MqttConfig, MqttConfig,
}; };
use homeassistant_mqtt_discovery::{ use homeassistant_mqtt_discovery::{
@ -150,7 +150,7 @@ impl Mqtt {
} }
} }
InternalMessage::OutboundStatePacket(p) => { InternalMessage::OutboundStatePacket(p) => {
let state_msg = self.gen_state_message(&p); let state_msg = self.gen_state_message(p);
info!("Sending state message: {:?}", state_msg); info!("Sending state message: {:?}", state_msg);
@ -163,7 +163,7 @@ impl Mqtt {
false, false,
serde_json::to_vec(&state_msg).unwrap(), serde_json::to_vec(&state_msg).unwrap(),
) )
.context("Sending initial autodiscovery") .context("Sending state change")
{ {
error!("{e:?}"); error!("{e:?}");
} }
@ -263,7 +263,12 @@ impl Mqtt {
..Common::default() ..Common::default()
}, },
name: Some(FRIENDLY_NAME.to_string()), name: Some(FRIENDLY_NAME.to_string()),
effect_list: Some(vec!["Rainbow".into()]), effect_list: Some(
Parameters::get_names()
.iter()
.map(|s| s.to_string())
.collect(),
),
brightness: Some(false), brightness: Some(false),
effect: Some(true), effect: Some(true),
supported_color_modes: Some(vec![light::ColorMode::Rgb]), supported_color_modes: Some(vec![light::ColorMode::Rgb]),
@ -272,7 +277,7 @@ impl Mqtt {
} }
} }
fn gen_state_message(&self, state: &strip::State) -> light::JsonIncoming { fn gen_state_message(&self, state: strip::State) -> light::JsonIncoming {
// "<discovery_prefix>/device_trigger/[<node_id>/]<object_id>/config", // "<discovery_prefix>/device_trigger/[<node_id>/]<object_id>/config",
// homeassistant/device_trigger/0x90fd9ffffedf1266/action_arrow_left_click/config // homeassistant/device_trigger/0x90fd9ffffedf1266/action_arrow_left_click/config
light::JsonIncoming { light::JsonIncoming {
@ -285,7 +290,7 @@ impl Mqtt {
b: Some(state.color.2 as usize), b: Some(state.color.2 as usize),
..IncomingColor::default() ..IncomingColor::default()
}), }),
effect: state.pattern.clone(), effect: Some(state.pattern),
state: Some( state: Some(
(if state.on { (if state.on {
light::STATUS_DEFAULT_LIGHT_ON light::STATUS_DEFAULT_LIGHT_ON
@ -311,11 +316,11 @@ fn build_strip_tx_msg(command: &JsonIncoming) -> Option<strip::Message> {
return Some(Message::ClearLights); return Some(Message::ClearLights);
} }
if let Some(effect) = &command.effect // if let Some(effect) = &command.effect
&& effect == "Rainbow" // && effect == "Rainbow"
{ // {
return Some(Message::ChangePattern(Box::new(MovingRainbow::default()))); // return Some(Message::ChangePattern(Box::new(MovingRainbow::default())));
} // }
if command.effect.is_none() if command.effect.is_none()
&& let Some(color) = &command.color && let Some(color) = &command.color
@ -336,6 +341,14 @@ fn build_strip_tx_msg(command: &JsonIncoming) -> Option<strip::Message> {
})))); }))));
} }
if let Some(state) = &command.state
&& state == light::STATUS_DEFAULT_LIGHT_ON
{
return Some(Message::TurnOn(
command.effect.as_ref().map(|s| s.to_string()),
));
}
error!("Not able to parse input as a command: {command:?}"); error!("Not able to parse input as a command: {command:?}");
None None
} }

View File

@ -21,6 +21,7 @@ pub struct LedStrip {
pub adapter: Box<dyn Ws28xxAdapter>, pub adapter: Box<dyn Ws28xxAdapter>,
pub config: Config, pub config: Config,
pub pattern: Box<dyn Pattern + Send + Sync>, pub pattern: Box<dyn Pattern + Send + Sync>,
pub state: common::strip::State,
} }
impl LedStrip { impl LedStrip {
@ -49,10 +50,20 @@ impl LedStrip {
})) }))
}); });
let state = common::strip::State {
on: true,
pattern: pattern.get_name().to_string(),
// brightnes: u8,
color: color::WHITE,
// Off,
// Pattern
};
let mut ret = Self { let mut ret = Self {
adapter, adapter,
config, config,
pattern, pattern,
state,
}; };
ret.set_num_lights(num_lights); ret.set_num_lights(num_lights);
@ -138,6 +149,11 @@ impl LedStrip {
} }
} }
// pub fn set_state(&self) {
// // let mut state = common::strip::State::default();
// }
pub fn strip_loop( pub fn strip_loop(
&mut self, &mut self,
message_tx: &Sender<common::Message>, message_tx: &Sender<common::Message>,
@ -146,8 +162,7 @@ impl LedStrip {
strip_terminated: Sender<()>, strip_terminated: Sender<()>,
) -> Result<(), ProgramError> { ) -> Result<(), ProgramError> {
let mut exit = false; let mut exit = false;
let mut state = common::strip::State::default(); let _ = state_tx.send(self.state.clone());
let _ = state_tx.send(state.clone());
loop { loop {
let target_time = Instant::now().add(Duration::from_millis(self.config.tick_time_ms)); let target_time = Instant::now().add(Duration::from_millis(self.config.tick_time_ms));
@ -160,8 +175,9 @@ impl LedStrip {
})); }));
if pat.init(self.pattern_num_lights()).is_ok() { if pat.init(self.pattern_num_lights()).is_ok() {
self.pattern = pat; self.pattern = pat;
state.on = false; self.state.pattern = self.pattern.get_name().to_string();
let _ = state_tx.send(state.clone()); self.state.on = false;
let _ = state_tx.send(self.state.clone());
info!("Cleared lights"); info!("Cleared lights");
} else { } else {
let _result = message_tx.send(common::Message::String(format!( let _result = message_tx.send(common::Message::String(format!(
@ -177,9 +193,9 @@ impl LedStrip {
self.pattern = pat; self.pattern = pat;
info!("Changed pattern"); info!("Changed pattern");
state.on = true; self.state.on = true;
state.pattern = Some(format!("Rainbow")); self.state.pattern = self.pattern.get_name().to_string();
let _ = state_tx.send(state.clone()); let _ = state_tx.send(self.state.clone());
} }
Err(e) => { Err(e) => {
let _result = message_tx.send(common::Message::String(format!( let _result = message_tx.send(common::Message::String(format!(
@ -187,6 +203,36 @@ impl LedStrip {
))); )));
} }
}, },
Message::TurnOn(pattern_name) => {
// It's already on
if self.state.on {
continue;
}
if let Err(e) = self.pattern.cleanup() {
error!("Error cleaning up old pattern: {e:?}");
}
// Parameters::from_str(pattern_name)
let mut pat = pattern_name
.and_then(|p| pattern::Parameters::from_str(&p).ok())
.map(|p| p.to_pattern())
.unwrap_or_else(|| {
self.state.color = color::WHITE;
Box::new(pattern::Solid::new(&pattern::SolidParams {
color: color::WHITE,
}))
});
if pat.init(self.pattern_num_lights()).is_ok() {
self.pattern = pat;
self.state.pattern = self.pattern.get_name().to_string();
self.state.on = true;
let _ = state_tx.send(self.state.clone());
info!("Turned on");
}
}
Message::SetNumLights(num_lights) => { Message::SetNumLights(num_lights) => {
self.set_num_lights(num_lights); self.set_num_lights(num_lights);
} }