use rusb::{DeviceHandle, GlobalContext};
use std::time::Duration;
#[cfg(feature = "serde_support")]
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct Elc {
handle: DeviceHandle<GlobalContext>,
has_kernel_driver: bool,
}
impl Elc {
#![allow(clippy::new_without_default)]
pub fn new() -> Elc {
let handle = rusb::open_device_with_vid_pid(0x187c, 0x0550).unwrap();
let has_kernel_driver = handle.kernel_driver_active(0).unwrap_or(false);
if has_kernel_driver {
handle
.detach_kernel_driver(0)
.expect("Can't detach the kernel driver");
}
handle
.claim_interface(0)
.expect("Can't claim the interface");
Elc {
handle,
has_kernel_driver,
}
}
fn send_command(&mut self, data: &impl Command) -> Result<(), rusb::Error> {
let request_type = rusb::request_type(
rusb::Direction::Out,
rusb::RequestType::Class,
rusb::Recipient::Interface,
);
let header = [0x03].to_vec();
let mut payload: Vec<u8> = header.into_iter().chain(data.build()).collect();
payload.resize(33, 0x00);
let result = self.handle.write_control(
request_type,
rusb::constants::LIBUSB_REQUEST_SET_CONFIGURATION,
0x202,
0,
&payload,
Duration::from_secs(1),
);
if let Ok(s) = result
&& s == 33
{
Ok(())
} else if let Err(e) = result {
Err(e)
} else {
Err(rusb::Error::Other)
}
}
fn receive_data(&mut self) -> Vec<u8> {
let mut buf = [0; 33].to_vec();
let request_type = rusb::request_type(
rusb::Direction::In,
rusb::RequestType::Class,
rusb::Recipient::Interface,
);
self.handle
.read_control(
request_type,
rusb::constants::LIBUSB_REQUEST_CLEAR_FEATURE,
0x101,
0,
&mut buf,
Duration::from_secs(1),
)
.unwrap();
buf
}
pub fn execute(&mut self, command: &impl Command) -> Result<Response, rusb::Error> {
self.send_command(command)?;
let data = self.receive_data();
Ok(Response::from_data(command.response_type(), data))
}
}
impl Drop for Elc {
fn drop(&mut self) {
if self.has_kernel_driver {
self.handle
.release_interface(0)
.expect("Can't release the interface");
self.handle
.attach_kernel_driver(0)
.expect("Can't attach the kernel driver")
}
}
}
pub trait Command {
fn build(&self) -> Vec<u8>;
fn response_type(&self) -> ResponseType {
ResponseType::Raw
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum Commands {
Query(QuerySub),
Animation(AnimationSub, u16),
StartSeries(bool, Vec<Zone>),
AddActions(Vec<Action>),
Dim(u8, Vec<Zone>),
Color(Rgb, Vec<Zone>),
EraseFlash,
}
impl Command for Commands {
fn build(&self) -> Vec<u8> {
match self {
Commands::Query(sub) => {
let opcode = [0x20].to_vec();
let sub = sub.build();
opcode.into_iter().chain(sub).collect()
}
Commands::Animation(sub, animation_id) => {
let opcode = if *animation_id >= 0x5b && *animation_id <= 0x60 {
[0x21].to_vec()
} else {
[0x22].to_vec()
};
let sub = sub.build();
let animation_id = animation_id.to_be_bytes().to_vec();
opcode.into_iter().chain(sub).chain(animation_id).collect()
}
Commands::StartSeries(loop_flag, zones) => {
let mut opcode = [0x23].to_vec().to_vec();
let mut buf = vec![];
buf.append(&mut opcode);
let loop_flag = if *loop_flag { 1 } else { 0 };
buf.push(loop_flag);
let mut zones_len = (zones.len() as u16).to_be_bytes().to_vec();
buf.append(&mut zones_len);
for zone in zones {
buf.push(*zone);
}
buf
}
Commands::AddActions(actions) => {
if actions.len() > 3 {
panic!("Can't add more than 3 actions at a time.");
}
let mut opcode = [0x24].to_vec();
let mut buf = vec![];
buf.append(&mut opcode);
for action in actions {
buf.append(&mut action.build());
}
buf
}
Commands::Dim(level, zones) => {
let mut opcode = [0x26].to_vec();
let mut buf = vec![];
buf.append(&mut opcode);
buf.push(*level);
let mut zones_len = (zones.len() as u16).to_be_bytes().to_vec();
buf.append(&mut zones_len);
for zone in zones {
buf.push(*zone);
}
buf
}
Commands::Color(color, zones) => {
let mut opcode = [0x27].to_vec();
let mut buf = vec![];
buf.append(&mut opcode);
buf.append(&mut color.build());
let mut zones_len = (zones.len() as u16).to_be_bytes().to_vec();
buf.append(&mut zones_len);
for zone in zones {
buf.push(*zone);
}
buf
}
Commands::EraseFlash => {
vec![0xFF]
}
}
}
fn response_type(&self) -> ResponseType {
if let Self::Query(sub) = self {
match sub {
QuerySub::Version => ResponseType::Version,
QuerySub::Status => ResponseType::Status,
QuerySub::Platform => ResponseType::Platform,
QuerySub::AnimationCount => ResponseType::AnimationCount,
}
} else {
ResponseType::Raw
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum QuerySub {
Version,
Status,
Platform,
AnimationCount,
}
impl Command for QuerySub {
fn build(&self) -> Vec<u8> {
match self {
QuerySub::Version => [0x00].to_vec(),
QuerySub::Status => [0x01].to_vec(),
QuerySub::Platform => [0x02].to_vec(),
QuerySub::AnimationCount => [0x03].to_vec(),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum AnimationSub {
StartNew,
FinishSave,
FinishPlay,
Remove,
Play,
SetDefault,
SetStartup,
}
impl Command for AnimationSub {
fn build(&self) -> Vec<u8> {
match self {
AnimationSub::StartNew => 0x01u16.to_be_bytes().to_vec(),
AnimationSub::FinishSave => 0x02u16.to_be_bytes().to_vec(),
AnimationSub::FinishPlay => 0x03u16.to_be_bytes().to_vec(),
AnimationSub::Remove => 0x04u16.to_be_bytes().to_vec(),
AnimationSub::Play => 0x05u16.to_be_bytes().to_vec(),
AnimationSub::SetDefault => 0x06u16.to_be_bytes().to_vec(),
AnimationSub::SetStartup => 0x07u16.to_be_bytes().to_vec(),
}
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum Effects {
Color,
Pulse,
Morph,
}
impl Command for Effects {
fn build(&self) -> Vec<u8> {
match self {
Effects::Color => [0x00].to_vec(),
Effects::Pulse => [0x01].to_vec(),
Effects::Morph => [0x02].to_vec(),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct Action {
effect: Effects,
color: Rgb,
duration: Duration,
tempo: Duration,
}
impl Action {
pub fn new(effect: Effects, color: Rgb, duration: Duration, tempo: Duration) -> Self {
Action {
effect,
color,
duration,
tempo,
}
}
}
impl Command for Action {
fn build(&self) -> Vec<u8> {
let mut effect = self.effect.build();
let mut color = self.color.build();
let duration = self.duration.as_millis() as u16;
let mut duration = duration.to_be_bytes().to_vec();
let tempo = self.tempo.as_millis() as u16;
let mut tempo = tempo.to_be_bytes().to_vec();
let mut buf = vec![];
buf.append(&mut effect);
buf.append(&mut duration);
buf.append(&mut tempo);
buf.append(&mut color);
buf
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct Rgb {
red: u8,
green: u8,
blue: u8,
}
impl Rgb {
pub fn new(red: u8, green: u8, blue: u8) -> Rgb {
Rgb { red, green, blue }
}
}
impl Command for Rgb {
fn build(&self) -> Vec<u8> {
vec![self.red, self.green, self.blue]
}
}
pub type Zone = u8;
#[derive(Debug)]
pub enum ResponseType {
Raw,
Version,
Status,
Platform,
AnimationCount,
}
#[derive(Debug)]
pub enum Response {
Raw(Vec<u8>),
Version(VersionResponse),
Status(StatusResponse),
Platform(PlatformResponse),
AnimationCount(AnimationCountResponse),
}
impl Response {
fn from_data(response_type: ResponseType, data: Vec<u8>) -> Response {
match response_type {
ResponseType::Raw => Response::Raw(data),
ResponseType::Version => Response::Version(VersionResponse::from(data)),
ResponseType::Status => Response::Status(StatusResponse::from(data)),
ResponseType::Platform => Response::Platform(PlatformResponse::from(data)),
ResponseType::AnimationCount => {
Response::AnimationCount(AnimationCountResponse::from(data))
}
}
}
}
#[derive(Debug)]
pub struct VersionResponse {
pub major: u8,
pub minor: u8,
pub revision: u8,
}
impl From<Vec<u8>> for VersionResponse {
fn from(value: Vec<u8>) -> Self {
let value: Vec<u8> = value.into_iter().skip(3).collect();
VersionResponse {
major: value[0],
minor: value[1],
revision: value[2],
}
}
}
#[derive(Debug)]
pub enum StatusResponse {
OK,
Err,
Unknown(u8),
}
impl From<Vec<u8>> for StatusResponse {
fn from(value: Vec<u8>) -> Self {
let value: Vec<u8> = value.into_iter().skip(3).collect();
let value = value[0];
match value {
0x00 => StatusResponse::OK,
0x01 => StatusResponse::Err,
v => StatusResponse::Unknown(v),
}
}
}
#[derive(Debug)]
pub struct PlatformResponse {
pub platform_id: u16,
pub zone_count: u8,
}
impl From<Vec<u8>> for PlatformResponse {
fn from(value: Vec<u8>) -> Self {
let value: Vec<u8> = value.into_iter().skip(3).collect();
let platform_id = [value[0], value[1]];
let platform_id = u16::from_be_bytes(platform_id);
let zone_count = value[2];
PlatformResponse {
platform_id,
zone_count,
}
}
}
#[derive(Debug)]
pub struct AnimationCountResponse {
pub animation_count: u16,
pub last_animation_id: u16,
}
impl From<Vec<u8>> for AnimationCountResponse {
fn from(value: Vec<u8>) -> Self {
let value: Vec<u8> = value.into_iter().skip(3).collect();
let animation_count = [value[0], value[1]];
let animation_count = u16::from_be_bytes(animation_count);
let last_animation_id = [value[0], value[1]];
let last_animation_id = u16::from_be_bytes(last_animation_id);
AnimationCountResponse {
animation_count,
last_animation_id,
}
}
}
#[derive(Debug)]
pub enum AnimationId {
Bootup,
Keyboard,
Ephimeral,
}
impl AnimationId {
pub const fn build_animation(&self) -> u16 {
match self {
AnimationId::Bootup => 0x0008,
AnimationId::Keyboard => 0x0061,
AnimationId::Ephimeral => 0xFFFF,
}
}
}
#[derive(Debug)]
pub enum PowerAnimationId {
AcSleep,
AcCharged,
AcCharging,
BatterySleep,
BatteryOn,
BatteryCritical,
}
impl PowerAnimationId {
pub const fn build_animation(&self) -> u16 {
match self {
PowerAnimationId::AcSleep => 0x005B,
PowerAnimationId::AcCharged => 0x005C,
PowerAnimationId::AcCharging => 0x005D,
PowerAnimationId::BatterySleep => 0x005E,
PowerAnimationId::BatteryOn => 0x005F,
PowerAnimationId::BatteryCritical => 0x0060,
}
}
}
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
#[cfg(feature = "serde_support")]
pub struct Config {
pub commands: Vec<Commands>,
}
#[cfg(feature = "serde_support")]
impl Config {
pub fn new(commands: Vec<Commands>) -> Self {
Self { commands }
}
}