mirror of
https://github.com/fossasia/badgemagic-rs
synced 2025-07-26 21:53:57 +00:00
Merge 210dc5fdf6
into 5d745ab8fd
This commit is contained in:
commit
468f07a725
2 changed files with 98 additions and 10 deletions
|
@ -6,7 +6,7 @@ use std::{fs, path::PathBuf};
|
|||
use anyhow::{Context, Result};
|
||||
use badgemagic::{
|
||||
ble::Device as BleDevice,
|
||||
protocol::{Mode, PayloadBuffer, Speed, Style},
|
||||
protocol::{Brightness, Mode, PayloadBuffer, Speed, Style},
|
||||
usb_hid::Device as UsbDevice,
|
||||
};
|
||||
use base64::Engine;
|
||||
|
@ -63,6 +63,9 @@ enum TransportProtocol {
|
|||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Config {
|
||||
#[serde(default)]
|
||||
brightness: Brightness,
|
||||
|
||||
#[serde(rename = "message")]
|
||||
messages: Vec<Message>,
|
||||
}
|
||||
|
@ -148,6 +151,7 @@ fn gnerate_payload(args: &mut Args) -> Result<PayloadBuffer> {
|
|||
};
|
||||
|
||||
let mut payload = PayloadBuffer::new();
|
||||
payload.set_brightness(config.brightness);
|
||||
|
||||
for message in config.messages {
|
||||
let mut style = Style::default();
|
||||
|
|
102
src/protocol.rs
102
src/protocol.rs
|
@ -1,7 +1,5 @@
|
|||
//! Protocol used to update the badge
|
||||
|
||||
use std::num::TryFromIntError;
|
||||
|
||||
#[cfg(feature = "embedded-graphics")]
|
||||
use embedded_graphics::{
|
||||
draw_target::DrawTarget,
|
||||
|
@ -11,6 +9,7 @@ use embedded_graphics::{
|
|||
primitives::Rectangle,
|
||||
Drawable,
|
||||
};
|
||||
use std::{convert::Infallible, num::TryFromIntError};
|
||||
use time::OffsetDateTime;
|
||||
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U16};
|
||||
|
||||
|
@ -54,7 +53,7 @@ impl Style {
|
|||
self
|
||||
}
|
||||
|
||||
/// Show a dotted border arround the display.
|
||||
/// Show a dotted border around the display.
|
||||
/// ```
|
||||
/// use badgemagic::protocol::Style;
|
||||
/// # (
|
||||
|
@ -161,7 +160,7 @@ impl TryFrom<u8> for Speed {
|
|||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
|
||||
pub enum Mode {
|
||||
/// Scroll thorugh the message from left to right
|
||||
/// Scroll through the message from left to right
|
||||
#[default]
|
||||
Left,
|
||||
|
||||
|
@ -193,14 +192,72 @@ pub enum Mode {
|
|||
Laser,
|
||||
}
|
||||
|
||||
/// Display Brightness
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "f32", into = "u8"))]
|
||||
pub enum Brightness {
|
||||
#[default]
|
||||
Full = 0x00,
|
||||
ThreeQuarters = 0x10,
|
||||
Half = 0x20,
|
||||
OneQuarter = 0x30,
|
||||
}
|
||||
|
||||
impl From<Brightness> for u8 {
|
||||
fn from(value: Brightness) -> Self {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Brightness {
|
||||
type Error = TryFromIntError;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0x00 => Self::Full,
|
||||
0x10 => Self::ThreeQuarters,
|
||||
0x20 => Self::Half,
|
||||
0x30 => Self::OneQuarter,
|
||||
_ => return Err(u8::try_from(-1).unwrap_err()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Brightness> for f32 {
|
||||
fn from(value: Brightness) -> Self {
|
||||
match value {
|
||||
Brightness::Full => 1.0,
|
||||
Brightness::ThreeQuarters => 0.75,
|
||||
Brightness::Half => 0.5,
|
||||
Brightness::OneQuarter => 0.25,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<f32> for Brightness {
|
||||
type Error = Infallible;
|
||||
fn try_from(value: f32) -> Result<Self, Self::Error> {
|
||||
if value < 0.375 {
|
||||
Ok(Self::OneQuarter)
|
||||
} else if value < 0.625 {
|
||||
Ok(Self::Half)
|
||||
} else if value < 0.875 {
|
||||
Ok(Self::ThreeQuarters)
|
||||
} else {
|
||||
Ok(Self::Full)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MSG_PADDING_ALIGN: usize = 64;
|
||||
|
||||
const MAGIC: [u8; 6] = *b"wang\0\0";
|
||||
const MAGIC: [u8; 5] = *b"wang\0";
|
||||
|
||||
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
|
||||
#[repr(C)]
|
||||
struct Header {
|
||||
magic: [u8; 6],
|
||||
magic: [u8; 5],
|
||||
brightness: u8,
|
||||
blink: u8,
|
||||
border: u8,
|
||||
speed_and_mode: [u8; 8],
|
||||
|
@ -241,7 +298,7 @@ impl Timestamp {
|
|||
|
||||
/// Buffer to create a payload
|
||||
///
|
||||
/// A payload consits of up to 8 messages
|
||||
/// A payload consists of up to 8 messages
|
||||
/// ```
|
||||
/// # #[cfg(feature = "embedded-graphics")]
|
||||
/// # fn main() {
|
||||
|
@ -283,6 +340,7 @@ impl PayloadBuffer {
|
|||
num_messages: 0,
|
||||
data: Header {
|
||||
magic: MAGIC,
|
||||
brightness: Brightness::Full.into(),
|
||||
blink: 0,
|
||||
border: 0,
|
||||
speed_and_mode: [0; 8],
|
||||
|
@ -300,6 +358,10 @@ impl PayloadBuffer {
|
|||
Header::mut_from_prefix(&mut self.data).unwrap().0
|
||||
}
|
||||
|
||||
pub fn set_brightness(&mut self, brightness: Brightness) {
|
||||
self.header_mut().brightness = brightness.into();
|
||||
}
|
||||
|
||||
/// Return the current number of messages
|
||||
pub fn num_messages(&mut self) -> usize {
|
||||
self.num_messages as usize
|
||||
|
@ -368,7 +430,7 @@ impl PayloadBuffer {
|
|||
&self.data
|
||||
}
|
||||
|
||||
/// Convert the payload buffe into bytes (with padding)
|
||||
/// Convert the payload buffer into bytes (with padding)
|
||||
#[allow(clippy::missing_panics_doc)] // should never panic
|
||||
#[must_use]
|
||||
pub fn into_padded_bytes(self) -> impl AsRef<[u8]> {
|
||||
|
@ -486,7 +548,7 @@ impl DrawTarget for MessageBuffer<'_> {
|
|||
mod test {
|
||||
use std::ops::Range;
|
||||
|
||||
use super::Speed;
|
||||
use super::{Brightness, Speed};
|
||||
|
||||
#[test]
|
||||
fn speed_to_u8_and_back() {
|
||||
|
@ -499,4 +561,26 @@ mod test {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u8_to_brightness_and_back() {
|
||||
const VALID_BRIGHTNESS_VALUES: [u8; 4] = [0x00, 0x10, 0x20, 0x30];
|
||||
for i in u8::MIN..u8::MAX {
|
||||
if let Ok(brightness) = Brightness::try_from(i) {
|
||||
assert_eq!(u8::from(brightness), i);
|
||||
} else {
|
||||
assert!(!VALID_BRIGHTNESS_VALUES.contains(&i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_to_brightness_and_back() {
|
||||
const VALID_BRIGHTNESS_VALUES: [f32; 4] = [0.25, 0.5, 0.75, 1.0];
|
||||
for i in i8::MIN..i8::MAX {
|
||||
let i = f32::from(i) / 4f32;
|
||||
let Ok(brightness) = Brightness::try_from(i);
|
||||
assert!(VALID_BRIGHTNESS_VALUES.contains(&(f32::from(brightness))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue