Skip to content

Commit 5724dec

Browse files
authored
Adding send-gif support in divoom-cli. (#12)
1 parent 69fde5e commit 5724dec

File tree

9 files changed

+96
-39
lines changed

9 files changed

+96
-39
lines changed

divoom/src/animation/animation_builder.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::animation::animation_builder_error::*;
21
use crate::animation::animation_frame_builder::DivoomAnimationFrameBuilder;
32
use crate::dto::*;
43
use std::collections::BTreeMap;
@@ -15,12 +14,12 @@ pub struct DivoomAnimationBuilder {
1514

1615
// Ctor and basic functions
1716
impl DivoomAnimationBuilder {
18-
pub fn new(
19-
canvas_size: u32,
20-
speed: Duration,
21-
) -> DivoomAnimationBuilderResult<DivoomAnimationBuilder> {
17+
pub fn new(canvas_size: u32, speed: Duration) -> DivoomAPIResult<DivoomAnimationBuilder> {
2218
if canvas_size != 16 && canvas_size != 32 && canvas_size != 64 {
23-
return Err(DivoomAnimationBuilderError::UnsupportedCanvasSize);
19+
return Err(DivoomAPIError::ParameterError(format!(
20+
"Invalid canvas size: {}. Only 16, 32 and 64 are supported.",
21+
canvas_size
22+
)));
2423
}
2524

2625
Ok(DivoomAnimationBuilder {
@@ -123,7 +122,7 @@ impl DivoomAnimationBuilder {
123122
#[cfg(test)]
124123
mod tests {
125124
use crate::animation::*;
126-
use crate::test_utils;
125+
use crate::{test_utils, DivoomAPIError};
127126
use std::time::Duration;
128127

129128
#[test]
@@ -139,7 +138,7 @@ mod tests {
139138
match result {
140139
Ok(_) => panic!("Canvas size is incorrect and we shall not create builder here."),
141140
Err(e) => match e {
142-
DivoomAnimationBuilderError::UnsupportedCanvasSize => (),
141+
DivoomAPIError::ParameterError(_) => (),
143142
_ => panic!("Incorrect error code!"),
144143
},
145144
}

divoom/src/animation/animation_builder_error.rs

-24
This file was deleted.

divoom/src/animation/animation_resource_loader.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::animation::animation_builder_error::DivoomAnimationBuilderResult;
1+
use crate::{DivoomAPIError, DivoomAPIResult};
22
use std::fs::File;
33
use tiny_skia::Pixmap;
44

55
pub struct DivoomAnimationResourceLoader {}
66

77
impl DivoomAnimationResourceLoader {
8-
pub fn gif(file_path: &str) -> DivoomAnimationBuilderResult<Vec<Pixmap>> {
8+
pub fn gif(file_path: &str) -> DivoomAPIResult<Vec<Pixmap>> {
99
let mut frames = vec![];
1010
let input = File::open(file_path)?;
1111

@@ -24,6 +24,12 @@ impl DivoomAnimationResourceLoader {
2424
}
2525
}
2626

27+
impl From<gif::DecodingError> for DivoomAPIError {
28+
fn from(err: gif::DecodingError) -> Self {
29+
DivoomAPIError::ResourceDecodeError(err.to_string())
30+
}
31+
}
32+
2733
#[cfg(test)]
2834
mod tests {
2935
use super::*;

divoom/src/animation/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
mod animation_builder;
2-
mod animation_builder_error;
32
mod animation_frame_builder;
43
mod animation_resource_loader;
54

65
pub use animation_builder::*;
7-
pub use animation_builder_error::*;
86
pub use animation_frame_builder::*;
97
pub use animation_resource_loader::*;

divoom/src/clients/pixoo/pixoo_client.rs

+24
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ use crate::divoom_contracts::pixoo::system::*;
88
use crate::divoom_contracts::pixoo::tool::*;
99
use crate::dto::*;
1010
use std::rc::Rc;
11+
use std::time::Duration;
12+
13+
#[cfg(feature = "animation-builder")]
14+
use crate::animation::*;
1115

1216
/// Pixoo device client
1317
///
@@ -300,6 +304,26 @@ impl PixooClient {
300304
()
301305
);
302306

307+
/// Send GIF to the device to play as an animation.
308+
///
309+
/// This API is different from `play_gif_file`, which is provided by divoom device directly. This API will try to leverage the animation API,
310+
/// create a new animation, load the gif files and draw all the frames into the animation, and send the to device to play.
311+
///
312+
/// The API `play_gif_file` doesn't seems to be very stable when the package is published, hence `send_gif_as_animation` is more preferred
313+
/// as of now.
314+
#[cfg(feature = "animation-builder")]
315+
pub async fn send_gif_as_animation(
316+
&self,
317+
canvas_size: u32,
318+
speed: Duration,
319+
file_path: &str,
320+
) -> DivoomAPIResult<()> {
321+
let animation_builder = DivoomAnimationBuilder::new(canvas_size, speed)?;
322+
let gif = DivoomAnimationResourceLoader::gif(file_path)?;
323+
let animation = animation_builder.draw_frames(&gif, 0).build();
324+
self.send_image_animation(animation).await
325+
}
326+
303327
#[doc = include_str!("../../divoom_contracts/pixoo/animation/api_send_image_animation_frame.md")]
304328
pub async fn send_image_animation(
305329
&self,

divoom/src/dto/divoom_api_error.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::io;
12
use thiserror::Error;
23

34
/// This represents the error that returned from Divoom online service or Divoom devices.
@@ -54,6 +55,15 @@ pub enum DivoomAPIError {
5455
#[error("Invalid parameter.")]
5556
ParameterError(String),
5657

58+
#[error("Failed to load resource")]
59+
ResourceLoadError {
60+
#[from]
61+
source: io::Error,
62+
},
63+
64+
#[error("Failed to decode resource")]
65+
ResourceDecodeError(String),
66+
5767
#[error("Failed to send request")]
5868
RequestError {
5969
#[from]

divoom_cli/README.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,22 @@ brightness: 67
7171
# Set channel to clock with id 100
7272
> divoom-cli 192.168.0.164 channel set-clock 100
7373

74+
# Play a gif from Internet by calling the API provided by Divoom Device.
75+
# Please note that: this API can be unstable and only accepts GIF with 16x16, 32x32 and 64x64 image size.
76+
> divoom-cli 192.168.0.123 animation gif play --url https://www.gifandgif.eu/animated_gif/Planets/Animated%20Gif%20Planets%20(16).GIF
77+
78+
# To help playing GIF in a more stable way, we can use the image animation API to craft an animation and draw the GIF
79+
# frames into it and send to device to play, e.g.:
80+
> divoom-cli 192.168.0.123 animation image send-gif "logo-16-rotate-4-frames.gif" 16 -s 100
81+
7482
# Create a text animation
75-
> divoom-cli 192.168.0.123 animation text set 1 "The gray fox jumped over the lazy dog"
83+
# Please note that: this API only works after we use "animation image send-gif" API to draw anything. This API call will be ignored,
84+
# when the device is showing other things, like clock or channel.
85+
> divoom-cli 192.168.0.123 animation text set 1 "Hello world!"
86+
> divoom-cli 192.168.0.123 animation text set 2 "The gray fox jumped over the lazy dog" -y 20
7687

77-
# Play a gif from Internet
78-
> divoom-cli 192.168.0.123 animation gif play --url https://www.gifandgif.eu/animated_gif/Planets/Animated%20Gif%20Planets%20(16).GIF
88+
# Modify existing text animation. E.g. changing "Hello world!" above to "Hello Divoom!"
89+
> divoom-cli 192.168.0.123 animation text set 1 "Hello Divoom!"
7990

8091
# Send a raw request
8192
#

divoom_cli/src/main.rs

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod opt;
33
use crate::opt::*;
44
use divoom::*;
55
use serde::Serialize;
6+
use std::time::Duration;
67
use structopt::StructOpt;
78

89
#[tokio::main]
@@ -278,6 +279,16 @@ async fn handle_image_animation_api(
278279
}
279280

280281
DivoomCliImageAnimationCommand::ResetId => pixoo.reset_next_animation_id().await,
282+
283+
DivoomCliImageAnimationCommand::SendGif {
284+
file_path,
285+
size,
286+
speed_in_ms,
287+
} => {
288+
pixoo
289+
.send_gif_as_animation(size, Duration::from_millis(speed_in_ms), &file_path)
290+
.await
291+
}
281292
}
282293
}
283294

divoom_cli/src/opt.rs

+22
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,28 @@ pub enum DivoomCliImageAnimationCommand {
305305

306306
#[structopt(about = "Reset next animation id")]
307307
ResetId,
308+
309+
#[structopt(
310+
about = "Send gif as animation. This is different from \"gif play\" command, which is provided directly by Divoom device. This command will create a regular animation and load the gif file and draw the frames into it in order to play it."
311+
)]
312+
SendGif {
313+
#[structopt(help = "Gif file path")]
314+
file_path: String,
315+
316+
#[structopt(
317+
default_value = "64",
318+
help = "Animation size in pixels. Only 16 and 32 and 64 are allowed."
319+
)]
320+
size: u32,
321+
322+
#[structopt(
323+
short,
324+
long = "speed",
325+
default_value = "100",
326+
help = "Animation play speed in milliseconds"
327+
)]
328+
speed_in_ms: u64,
329+
},
308330
}
309331

310332
#[derive(StructOpt, Debug)]

0 commit comments

Comments
 (0)