awtrix3 0.0.2

Awtrix3 types and API (mqtt/http), from https://blueforcer.github.io/awtrix3/#/api
Documentation
#![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(())
    }
}