use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
pub const PROTOCOL_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VersionedRequest {
pub version: String,
pub request: Request,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Request {
ArtList,
ArtShowInfo {
content_id: Option<String>,
},
ArtSelect {
content_id: String,
},
ArtUpload {
file_path: PathBuf,
select: bool,
replace_selected: bool,
canvas_color: Option<String>,
},
ArtDelete {
content_ids: Vec<String>,
},
ArtThumbnail {
content_id: String,
output_path: PathBuf,
},
ArtFavorite {
content_id: String,
on: bool,
},
MatteList,
MatteSet {
content_id: String,
matte_id: String,
portrait: Option<String>,
},
FilterList,
FilterSet {
content_id: String,
filter_id: String,
},
ModeOn,
ModeOff,
ModeStatus,
SlideshowOn {
duration_secs: Option<u64>,
shuffle: bool,
category: Option<String>,
},
SlideshowOff,
SlideshowStatus,
SlideshowConfigure {
duration_secs: Option<u64>,
shuffle: Option<bool>,
category: Option<String>,
},
BrightnessGet,
BrightnessSet {
value: u8,
},
ColorTempGet,
ColorTempSet {
value: i8,
},
RotationGet,
AutoBrightnessOn,
AutoBrightnessOff,
MotionTimer {
minutes: String,
},
MotionSensitivity {
level: String,
},
RemoteButton {
button: String,
},
AuthPair,
AuthVerify,
Shutdown,
Status,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ServerMessage {
Heartbeat,
Response(Response),
ShuttingDown,
VersionMismatch {
server_version: String,
client_version: String,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Response {
Ok { data: serde_json::Value },
Error { message: String },
TvDisconnected { message: String },
}
pub async fn write_message<W: AsyncWriteExt + Unpin, T: Serialize>(
writer: &mut W,
msg: &T,
) -> std::io::Result<()> {
let json = serde_json::to_vec(msg)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
let len = json.len() as u32;
writer.write_all(&len.to_be_bytes()).await?;
writer.write_all(&json).await?;
writer.flush().await?;
Ok(())
}
pub async fn read_message<R: AsyncReadExt + Unpin, T: for<'de> Deserialize<'de>>(
reader: &mut R,
) -> std::io::Result<T> {
let mut len_buf = [0u8; 4];
reader.read_exact(&mut len_buf).await?;
let len = u32::from_be_bytes(len_buf) as usize;
if len > 64 * 1024 * 1024 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("message too large: {len} bytes"),
));
}
let mut buf = vec![0u8; len];
reader.read_exact(&mut buf).await?;
serde_json::from_slice(&buf)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}