use log::error; use std::{string::String, vec::Vec}; pub mod premaid { use crate::eldecode::EltakoFrame; pub enum Positions { TopRight = 0x70, TopLeft = 0x30, BotLeft = 0x10, BotRight = 0x50, Nothing = 0x00, } pub fn scan_start() -> EltakoFrame { let mut temp = EltakoFrame::default(); temp.rorg = 0xf0; temp.data = 0x01028708; temp.source = 0x04065200; temp.status = 0x00; temp } pub fn scan_members(index: u8) -> EltakoFrame { let mut temp = EltakoFrame::default(); temp.rorg = 0xf0; temp.data = 0x00000000; temp.source = 0x00000000; temp.status = index; temp } pub fn status(index: u8) -> EltakoFrame { let mut temp = EltakoFrame::default(); temp.rorg = 0xfe; temp.data = 0x00000000; temp.source = 0x00000000; temp.status = index; temp } pub fn acknowledge(index: u8) -> EltakoFrame { let mut temp = EltakoFrame::default(); temp.rorg = 0xfc; temp.data = 0x00000000; temp.source = 0x00000000; temp.status = index; temp } pub fn button(source_address: u32, status: bool, position: Positions) -> EltakoFrame { let mut temp = EltakoFrame::default(); temp.rorg = 0x05; temp.data = 0x00000000 | (position as u32) << 24; temp.source = source_address; temp.status = if status { 0x30 } else { 0x20 }; temp } } #[derive(Debug, Clone, Copy, PartialEq)] pub struct EltakoFrame { pub hseq: u8, pub length: u8, pub rorg: u8, pub data: u32, pub source: u32, pub status: u8, } impl EltakoFrame { fn collect_to_u32(data: &[u8]) -> u32 { (data[0] as u32) << 24 | (data[1] as u32) << 16 | (data[2] as u32) << 8 | (data[3] as u32) } fn crc_from_vec(frame: &[u8]) -> u8 { let mut crc: u8 = 0; for i in 2..(frame.len() - 1) { (crc, _) = crc.overflowing_add(frame[i]); } return crc; } fn crc_from_frame(self) -> u8 { return EltakoFrame::crc_from_vec(&(self.to_vec(false).clone())); } pub fn from_vec(frame: &[u8]) -> Result { if frame.len() == 0xd { return Err(()); } if frame[0] != 0xa5 || frame[1] != 0x5a { error!("Message has an invalid preamble! {} {}", frame[0], frame[1]); return Err(()); } let crc = EltakoFrame::crc_from_vec(frame); if frame[13] != crc { error!( "Message crc check failed! Calculated: {:x} != Frame: {:x}", crc, frame[13] ); return Err(()); } Ok(EltakoFrame { hseq: frame[2] >> 5, length: frame[2] & 0x1f, rorg: frame[3], data: EltakoFrame::collect_to_u32(&frame[4..8]), source: EltakoFrame::collect_to_u32(&frame[8..12]), status: frame[12], }) } pub fn to_vec(self, with_crc: bool) -> Vec { let mut frame = Vec::new(); frame.push(0xa5); frame.push(0x5a); frame.push(self.length); frame.push(self.rorg); for i in 0..4 { frame.push(self.data.to_be_bytes()[i]); } for i in 0..4 { frame.push(self.source.to_be_bytes()[i]); } frame.push(self.status); if with_crc { frame.push(EltakoFrame::crc_from_frame(self)); } // Fill the rest of the frame with 0s to avoid crc failing while (self.length + 2) as usize >= frame.len() { frame.push(0x00); } return frame; } pub fn explain(&self) -> String { let msg_rorg; let mut msg_data = String::new(); let mut msg_status = ""; // Message type apparently? match self.rorg { 0x05 => { // Message type msg_rorg = "Button"; // Button location msg_data = match self.data.to_be_bytes()[0] { 0x70 => "Top Right", 0x30 => "Top Left", 0x10 => "Bot Left", 0x50 => "Bot Right", _ => "", } .to_string(); // Button state msg_status = match self.status { 0x30 => "On", 0x20 => "Off", _ => "", }; } 0x07 => { // Message type msg_rorg = "Dimmer"; let data = self.data.to_be_bytes(); msg_data += &format!("On={} ", data[0] & 0x1); msg_data += &format!("Lock={} ", data[0] & 0x4); msg_data += &format!("Speed={} ", data[1]); msg_data += &format!("Val={} ", data[2]); msg_data += match data[3] { 0x02 => "Dimming", _ => "", }; } 0xf0 => { msg_rorg = "Scan"; } 0xfe => { msg_rorg = "Status"; } 0xfc => { msg_rorg = "Acknowledge"; } _ => msg_rorg = "", } format!( "hseq: {:02x} | rorg:{:<12} | source_addr:0x{:08x} | status:{:<3} | data:{:<20} -> {:<2x?}", self.hseq, msg_rorg, self.source, msg_status, msg_data, self.to_vec(true) ) } } impl Default for EltakoFrame { fn default() -> Self { EltakoFrame { hseq: 0x0, length: 0xb, rorg: 0x00, data: 0x00000000, source: 0x00000000, status: 0x00, } } }