#![doc = include_str!("../README.md")]
mod apps;
mod color;
mod errors;
mod http;
mod indicators;
mod liveview;
mod mood;
mod mqtt;
mod power;
mod settings;
mod sound;
mod status;
pub use apps::*;
pub use color::Color;
pub use errors::Awtrix3Error;
pub use http::Awtrix3HttpClient;
pub use indicators::{Indicator, IndicatorId};
pub use mood::Mood;
pub use mqtt::{Awtrix3MqttClient, Awtrix3MqttClientOptions};
pub use settings::{Settings, TransitionEffect};
pub use status::Stats;
use power::{Power, Sleep};
use sound::Sound;
use status::{StatsEffects, StatsLoop, StatsTransitions};
use std::str;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Topic {
Stats,
StatsEffects,
StatsTransitions,
StatsLoop,
StatsCurrentApp,
Power,
Sleep,
Sound,
Rtttl,
MoodLight,
ButtonRight,
ButtonLeft,
ButtonSelect,
}
impl Topic {
pub fn decode(topic: &str, prefix: &str) -> Result<Self, Awtrix3Error> {
if !topic.starts_with(prefix) {
return Err(Awtrix3Error::InvalidPrefix(topic.to_string()));
}
match topic
.split("/")
.skip(1)
.collect::<Vec<&str>>()
.join("/")
.as_str()
{
"stats" => Ok(Topic::Stats),
"stats/effects" => Ok(Topic::StatsEffects),
"stats/transitions" => Ok(Topic::StatsTransitions),
"stats/loop" => Ok(Topic::StatsLoop),
"stats/currentApp" => Ok(Topic::StatsCurrentApp),
"stats/buttonRight" => Ok(Topic::ButtonRight),
"stats/buttonLeft" => Ok(Topic::ButtonLeft),
"stats/buttonSelect" => Ok(Topic::ButtonSelect),
_ => Err(Awtrix3Error::InvalidTopic(topic.to_string())),
}
}
pub async fn http_get(&self, client: &Awtrix3HttpClient) -> Result<Message, Awtrix3Error> {
match self {
Topic::Stats => client
.get_request::<Stats>("/api/stats")
.await
.map(Message::Stats),
Topic::StatsEffects => client
.get_request::<StatsEffects>("/api/effects")
.await
.map(Message::StatsEffects),
Topic::StatsTransitions => client
.get_request::<StatsTransitions>("/api/transitions")
.await
.map(Message::StatsTransitions),
Topic::StatsLoop => client
.get_request::<StatsLoop>("/api/loop")
.await
.map(Message::StatsLoop),
_ => Err(Awtrix3Error::InvalidTopic("Not implemented".to_string())),
}
}
pub fn payload(&self, payload: &[u8]) -> Result<Message, Awtrix3Error> {
match self {
Topic::Stats => Ok(Message::Stats(serde_json::from_slice(payload)?)),
Topic::StatsEffects => Ok(Message::StatsEffects(serde_json::from_slice(payload)?)),
Topic::StatsTransitions => {
Ok(Message::StatsTransitions(serde_json::from_slice(payload)?))
}
Topic::StatsLoop => Ok(Message::StatsLoop(serde_json::from_slice(payload)?)),
Topic::StatsCurrentApp => {
let val: String = str::from_utf8(payload).map(|v| v.to_string())?;
Ok(Message::CurrentApp(val))
}
Topic::ButtonRight => {
let val: u8 = serde_json::from_slice(payload)?;
Ok(Message::ButtonRight(val == 1))
}
Topic::ButtonLeft => {
let val: u8 = serde_json::from_slice(payload)?;
Ok(Message::ButtonLeft(val == 1))
}
Topic::ButtonSelect => {
let val: u8 = serde_json::from_slice(payload)?;
Ok(Message::ButtonSelect(val == 1))
}
_ => Err(Awtrix3Error::InvalidTopic("Not implemented".to_string())),
}
}
}
#[derive(Debug)]
pub enum Message {
Stats(Stats),
StatsEffects(StatsEffects),
StatsTransitions(StatsTransitions),
StatsLoop(StatsLoop),
CurrentApp(String),
ButtonRight(bool),
ButtonLeft(bool),
ButtonSelect(bool),
PowerOn,
PowerOff,
Sleep(u32),
Sound(String),
Rtttl(String),
MoodLightOn(mood::Mood),
MoodLightOff,
IndicatorOn(IndicatorId, Indicator),
IndicatorOff(IndicatorId),
SetApp(String, App),
SetApps(String, Vec<App>),
DeleteApp(String),
SetNotification(Notification),
DismissNotification,
NextApp,
PreviousApp,
SwitchApp(String),
Settings(Settings),
Reboot,
Update,
Erase,
ResetSettings,
}
impl Message {
pub async fn http_send(&self, client: &Awtrix3HttpClient) -> Result<(), Awtrix3Error> {
match self {
Message::PowerOn => client
.post_request("/api/power", Power { power: true })
.await
.map(|_| ()),
Message::PowerOff => client
.post_request("/api/power", Power { power: false })
.await
.map(|_| ()),
Message::Sleep(duration) => client
.post_request("/api/sleep", Sleep { sleep: *duration })
.await
.map(|_| ()),
Message::Sound(sound) => client
.post_request(
"/api/sound",
Sound {
sound: sound.clone(),
},
)
.await
.map(|_| ()),
Message::Rtttl(rtttl) => client.post_request("/api/rtttl", rtttl).await.map(|_| ()),
Message::MoodLightOn(mood) => client
.post_request("/api/moodlight", mood)
.await
.map(|_| ()),
Message::MoodLightOff => client
.post_empty_request("/api/moodlight")
.await
.map(|_| ()),
Message::IndicatorOn(id, indicator) => client
.post_request(&format!("/api/indicator{}", id), indicator)
.await
.map(|_| ()),
Message::IndicatorOff(id) => client
.post_empty_request(&format!("/api/indicator{}", id))
.await
.map(|_| ()),
Message::SetApp(app_id, app) => client
.post_request(&format!("/api/custom?name={}", app_id), app)
.await
.map(|_| ()),
Message::SetApps(app_id, apps) => client
.post_request(&format!("/api/custom?name={}", app_id), apps)
.await
.map(|_| ()),
Message::DeleteApp(app_id) => client
.post_empty_request(&format!("/api/custom?name={}", app_id))
.await
.map(|_| ()),
Message::SetNotification(notif) => {
client.post_request("/api/notify", notif).await.map(|_| ())
}
Message::DismissNotification => client
.post_empty_request("/api/notify/dismiss")
.await
.map(|_| ()),
Message::NextApp => client.post_empty_request("/api/nextapp").await.map(|_| ()),
Message::PreviousApp => client.post_empty_request("/api/prevapp").await.map(|_| ()),
Message::SwitchApp(app_id) => client
.post_request(
"/api/switch",
AppSwitch {
name: app_id.clone(),
},
)
.await
.map(|_| ()),
Message::Settings(settings) => client
.post_request("/api/settings", settings)
.await
.map(|_| ()),
Message::Reboot => client.post_empty_request("/api/reboot").await.map(|_| ()),
Message::Update => client.post_empty_request("/api/update").await.map(|_| ()),
Message::Erase => client.post_empty_request("/api/erase").await.map(|_| ()),
Message::ResetSettings => client
.post_empty_request("/api/resetSettings")
.await
.map(|_| ()),
_ => Err(Awtrix3Error::InvalidSendMessage),
}
}
pub async fn mqtt_send(&self, client: &Awtrix3MqttClient) -> Result<(), Awtrix3Error> {
match self {
Message::PowerOn => client.publish("/power", Power { power: true }).await?,
Message::PowerOff => client.publish("/power", Power { power: false }).await?,
Message::Sleep(duration) => {
client.publish("/sleep", Sleep { sleep: *duration }).await?
}
Message::Sound(sound) => {
client
.publish(
"/sound",
Sound {
sound: sound.clone(),
},
)
.await?
}
Message::Rtttl(rtttl) => client.publish("/rtttl", rtttl).await?,
Message::MoodLightOn(mood) => client.publish("/moodlight", mood).await?,
Message::MoodLightOff => client.publish("/moodlight", "").await?,
Message::IndicatorOn(id, indicator) => {
client
.publish(&format!("/indicator{}", id), indicator)
.await?
}
Message::IndicatorOff(id) => client.publish(&format!("/indicator{}", id), "").await?,
Message::SetApp(app_id, app) => {
client.publish(&format!("/custom/{}", app_id), app).await?
}
Message::SetApps(app_id, apps) => {
client.publish(&format!("/custom/{}", app_id), apps).await?
}
Message::DeleteApp(app_id) => {
client.publish(&format!("/custom/{}", app_id), "").await?
}
Message::SetNotification(notif) => client.publish("/notify", notif).await?,
Message::DismissNotification => client.publish("/notify/dismiss", "").await?,
Message::NextApp => client.publish("/nextapp", "").await?,
Message::PreviousApp => client.publish("/prevapp", "").await?,
Message::SwitchApp(app_id) => {
client
.publish(
"/switch",
AppSwitch {
name: app_id.clone(),
},
)
.await?
}
Message::Settings(settings) => client.publish("/settings", settings).await?,
Message::Reboot => client.publish("/reboot", "").await?,
Message::Update => client.publish("/update", "").await?,
Message::Erase => client.publish("/erase", "").await?,
Message::ResetSettings => client.publish("/resetSettings", "").await?,
_ => return Err(Awtrix3Error::InvalidSendMessage),
}
Ok(())
}
}