use super::codec::{validate_length, DptCodec, DptId};
use super::value::DptValue;
use super::{decode_dpt9, encode_dpt9};
use crate::error::{KnxError, KnxResult};
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt1Switch;
impl Dpt1Switch {
fn encode_impl(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::Bool(v) => Ok(vec![if *v { 1 } else { 0 }]),
DptValue::U8(v) => Ok(vec![if *v != 0 { 1 } else { 0 }]),
_ => Err(KnxError::dpt_encoding(
"DPT 1.001",
"Expected boolean value",
)),
}
}
fn decode_impl(&self, data: &[u8]) -> KnxResult<DptValue> {
let byte = data.first().copied().unwrap_or(0);
Ok(DptValue::Bool(byte & 0x01 != 0))
}
}
impl DptCodec for Dpt1Switch {
fn id(&self) -> DptId {
DptId::new(1, 1)
}
fn name(&self) -> &'static str {
"Switch"
}
fn size(&self) -> usize {
0 }
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
self.encode_impl(value)
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
self.decode_impl(data)
}
fn is_small(&self) -> bool {
true
}
fn default_value(&self) -> DptValue {
DptValue::Bool(false)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt1Bool;
impl DptCodec for Dpt1Bool {
fn id(&self) -> DptId {
DptId::new(1, 2)
}
fn name(&self) -> &'static str {
"Bool"
}
fn size(&self) -> usize {
0
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
Dpt1Switch.encode(value)
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
Dpt1Switch.decode(data)
}
fn is_small(&self) -> bool {
true
}
fn default_value(&self) -> DptValue {
DptValue::Bool(false)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt1UpDown;
impl DptCodec for Dpt1UpDown {
fn id(&self) -> DptId {
DptId::new(1, 8)
}
fn name(&self) -> &'static str {
"Up/Down"
}
fn size(&self) -> usize {
0
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
Dpt1Switch.encode(value)
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
Dpt1Switch.decode(data)
}
fn is_small(&self) -> bool {
true
}
fn description(&self) -> &'static str {
"0=Up, 1=Down"
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt2SwitchControl;
impl DptCodec for Dpt2SwitchControl {
fn id(&self) -> DptId {
DptId::new(2, 1)
}
fn name(&self) -> &'static str {
"Switch Control"
}
fn size(&self) -> usize {
0
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::PriorityControl { control, value } => {
let byte = (if *control { 0x02 } else { 0 }) | (if *value { 0x01 } else { 0 });
Ok(vec![byte])
}
_ => Err(KnxError::dpt_encoding(
"DPT 2.001",
"Expected PriorityControl value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
let byte = data.first().copied().unwrap_or(0);
Ok(DptValue::PriorityControl {
control: byte & 0x02 != 0,
value: byte & 0x01 != 0,
})
}
fn is_small(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt3DimmingControl;
impl DptCodec for Dpt3DimmingControl {
fn id(&self) -> DptId {
DptId::new(3, 7)
}
fn name(&self) -> &'static str {
"Dimming Control"
}
fn size(&self) -> usize {
0
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::DimmingControl {
direction,
step_code,
} => {
let byte = (if *direction { 0x08 } else { 0 }) | (*step_code & 0x07);
Ok(vec![byte])
}
_ => Err(KnxError::dpt_encoding(
"DPT 3.007",
"Expected DimmingControl value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
let byte = data.first().copied().unwrap_or(0);
Ok(DptValue::DimmingControl {
direction: byte & 0x08 != 0,
step_code: byte & 0x07,
})
}
fn is_small(&self) -> bool {
true
}
fn description(&self) -> &'static str {
"Direction: 0=decrease, 1=increase; StepCode: 0=break, 1-7=steps"
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt3BlindsControl;
impl DptCodec for Dpt3BlindsControl {
fn id(&self) -> DptId {
DptId::new(3, 8)
}
fn name(&self) -> &'static str {
"Blinds Control"
}
fn size(&self) -> usize {
0
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::BlindsControl {
direction,
step_code,
} => {
let byte = (if *direction { 0x08 } else { 0 }) | (*step_code & 0x07);
Ok(vec![byte])
}
_ => Err(KnxError::dpt_encoding(
"DPT 3.008",
"Expected BlindsControl value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
let byte = data.first().copied().unwrap_or(0);
Ok(DptValue::BlindsControl {
direction: byte & 0x08 != 0,
step_code: byte & 0x07,
})
}
fn is_small(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt5Scaling;
impl DptCodec for Dpt5Scaling {
fn id(&self) -> DptId {
DptId::new(5, 1)
}
fn name(&self) -> &'static str {
"Scaling"
}
fn size(&self) -> usize {
1
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::U8(v) => Ok(vec![*v]),
DptValue::F16(v) | DptValue::F32(v) => {
let scaled = ((*v).clamp(0.0, 100.0) * 2.55) as u8;
Ok(vec![scaled])
}
_ => Err(KnxError::dpt_encoding(
"DPT 5.001",
"Expected numeric value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 1, "DPT 5.001")?;
let percent = (data[0] as f32) / 2.55;
Ok(DptValue::F16(percent))
}
fn unit(&self) -> Option<&'static str> {
Some("%")
}
fn min_value(&self) -> Option<f64> {
Some(0.0)
}
fn max_value(&self) -> Option<f64> {
Some(100.0)
}
fn default_value(&self) -> DptValue {
DptValue::F16(0.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt5Angle;
impl DptCodec for Dpt5Angle {
fn id(&self) -> DptId {
DptId::new(5, 3)
}
fn name(&self) -> &'static str {
"Angle"
}
fn size(&self) -> usize {
1
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::U8(v) => Ok(vec![*v]),
DptValue::F16(v) | DptValue::F32(v) => {
let scaled = ((*v).clamp(0.0, 360.0) * (255.0 / 360.0)) as u8;
Ok(vec![scaled])
}
_ => Err(KnxError::dpt_encoding(
"DPT 5.003",
"Expected numeric value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 1, "DPT 5.003")?;
let angle = (data[0] as f32) * (360.0 / 255.0);
Ok(DptValue::F16(angle))
}
fn unit(&self) -> Option<&'static str> {
Some("°")
}
fn min_value(&self) -> Option<f64> {
Some(0.0)
}
fn max_value(&self) -> Option<f64> {
Some(360.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt5Counter;
impl DptCodec for Dpt5Counter {
fn id(&self) -> DptId {
DptId::new(5, 10)
}
fn name(&self) -> &'static str {
"Counter Pulses"
}
fn size(&self) -> usize {
1
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::U8(v) => Ok(vec![*v]),
_ => Err(KnxError::dpt_encoding("DPT 5.010", "Expected U8 value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 1, "DPT 5.010")?;
Ok(DptValue::U8(data[0]))
}
fn min_value(&self) -> Option<f64> {
Some(0.0)
}
fn max_value(&self) -> Option<f64> {
Some(255.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt6Percent;
impl DptCodec for Dpt6Percent {
fn id(&self) -> DptId {
DptId::new(6, 1)
}
fn name(&self) -> &'static str {
"Percent (Signed)"
}
fn size(&self) -> usize {
1
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::I8(v) => Ok(vec![*v as u8]),
_ => Err(KnxError::dpt_encoding("DPT 6.001", "Expected I8 value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 1, "DPT 6.001")?;
Ok(DptValue::I8(data[0] as i8))
}
fn unit(&self) -> Option<&'static str> {
Some("%")
}
fn min_value(&self) -> Option<f64> {
Some(-128.0)
}
fn max_value(&self) -> Option<f64> {
Some(127.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt7Pulses;
impl DptCodec for Dpt7Pulses {
fn id(&self) -> DptId {
DptId::new(7, 1)
}
fn name(&self) -> &'static str {
"Pulses"
}
fn size(&self) -> usize {
2
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::U16(v) => Ok(v.to_be_bytes().to_vec()),
_ => Err(KnxError::dpt_encoding("DPT 7.001", "Expected U16 value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 2, "DPT 7.001")?;
let value = u16::from_be_bytes([data[0], data[1]]);
Ok(DptValue::U16(value))
}
fn min_value(&self) -> Option<f64> {
Some(0.0)
}
fn max_value(&self) -> Option<f64> {
Some(65535.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt8PulsesDiff;
impl DptCodec for Dpt8PulsesDiff {
fn id(&self) -> DptId {
DptId::new(8, 1)
}
fn name(&self) -> &'static str {
"Pulses Difference"
}
fn size(&self) -> usize {
2
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::I16(v) => Ok(v.to_be_bytes().to_vec()),
_ => Err(KnxError::dpt_encoding("DPT 8.001", "Expected I16 value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 2, "DPT 8.001")?;
let value = i16::from_be_bytes([data[0], data[1]]);
Ok(DptValue::I16(value))
}
fn min_value(&self) -> Option<f64> {
Some(-32768.0)
}
fn max_value(&self) -> Option<f64> {
Some(32767.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt9Temperature;
impl DptCodec for Dpt9Temperature {
fn id(&self) -> DptId {
DptId::new(9, 1)
}
fn name(&self) -> &'static str {
"Temperature"
}
fn size(&self) -> usize {
2
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
let float_val = match value {
DptValue::F16(v) | DptValue::F32(v) => *v,
DptValue::I16(v) => *v as f32,
DptValue::U16(v) => *v as f32,
_ => {
return Err(KnxError::dpt_encoding(
"DPT 9.001",
"Expected numeric value",
))
}
};
Ok(encode_dpt9(float_val).to_be_bytes().to_vec())
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 2, "DPT 9.001")?;
let raw = u16::from_be_bytes([data[0], data[1]]);
Ok(DptValue::F16(decode_dpt9(raw)))
}
fn unit(&self) -> Option<&'static str> {
Some("°C")
}
fn min_value(&self) -> Option<f64> {
Some(-273.0)
}
fn max_value(&self) -> Option<f64> {
Some(670760.0)
}
fn default_value(&self) -> DptValue {
DptValue::F16(20.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt9Lux;
impl DptCodec for Dpt9Lux {
fn id(&self) -> DptId {
DptId::new(9, 4)
}
fn name(&self) -> &'static str {
"Lux"
}
fn size(&self) -> usize {
2
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
Dpt9Temperature.encode(value)
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
Dpt9Temperature.decode(data)
}
fn unit(&self) -> Option<&'static str> {
Some("lux")
}
fn min_value(&self) -> Option<f64> {
Some(0.0)
}
fn max_value(&self) -> Option<f64> {
Some(670760.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt9Humidity;
impl DptCodec for Dpt9Humidity {
fn id(&self) -> DptId {
DptId::new(9, 7)
}
fn name(&self) -> &'static str {
"Humidity"
}
fn size(&self) -> usize {
2
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
Dpt9Temperature.encode(value)
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
Dpt9Temperature.decode(data)
}
fn unit(&self) -> Option<&'static str> {
Some("%")
}
fn min_value(&self) -> Option<f64> {
Some(0.0)
}
fn max_value(&self) -> Option<f64> {
Some(100.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt12Counter;
impl DptCodec for Dpt12Counter {
fn id(&self) -> DptId {
DptId::new(12, 1)
}
fn name(&self) -> &'static str {
"Counter Value"
}
fn size(&self) -> usize {
4
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::U32(v) => Ok(v.to_be_bytes().to_vec()),
_ => Err(KnxError::dpt_encoding("DPT 12.001", "Expected U32 value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 4, "DPT 12.001")?;
let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
Ok(DptValue::U32(value))
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt13Counter;
impl DptCodec for Dpt13Counter {
fn id(&self) -> DptId {
DptId::new(13, 1)
}
fn name(&self) -> &'static str {
"Counter Value (Signed)"
}
fn size(&self) -> usize {
4
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::I32(v) => Ok(v.to_be_bytes().to_vec()),
_ => Err(KnxError::dpt_encoding("DPT 13.001", "Expected I32 value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 4, "DPT 13.001")?;
let value = i32::from_be_bytes([data[0], data[1], data[2], data[3]]);
Ok(DptValue::I32(value))
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt14Float;
impl DptCodec for Dpt14Float {
fn id(&self) -> DptId {
DptId::main_only(14)
}
fn name(&self) -> &'static str {
"Float (32-bit)"
}
fn size(&self) -> usize {
4
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::F32(v) => Ok(v.to_be_bytes().to_vec()),
DptValue::F16(v) => Ok((*v).to_be_bytes().to_vec()),
_ => Err(KnxError::dpt_encoding("DPT 14", "Expected float value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 4, "DPT 14")?;
let value = f32::from_be_bytes([data[0], data[1], data[2], data[3]]);
Ok(DptValue::F32(value))
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt16String;
impl DptCodec for Dpt16String {
fn id(&self) -> DptId {
DptId::new(16, 1)
}
fn name(&self) -> &'static str {
"String (ASCII)"
}
fn size(&self) -> usize {
14
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::String(s) => {
let mut bytes = vec![0u8; 14];
let s_bytes = s.as_bytes();
let len = s_bytes.len().min(14);
bytes[..len].copy_from_slice(&s_bytes[..len]);
Ok(bytes)
}
_ => Err(KnxError::dpt_encoding(
"DPT 16.001",
"Expected string value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 14, "DPT 16.001")?;
let end = data.iter().position(|&b| b == 0).unwrap_or(14);
let s = String::from_utf8_lossy(&data[..end]).to_string();
Ok(DptValue::String(s))
}
fn default_value(&self) -> DptValue {
DptValue::String(String::new())
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt17Scene;
impl DptCodec for Dpt17Scene {
fn id(&self) -> DptId {
DptId::new(17, 1)
}
fn name(&self) -> &'static str {
"Scene Number"
}
fn size(&self) -> usize {
1
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::U8(v) => {
if *v > 63 {
return Err(KnxError::DptValueOutOfRange {
value: v.to_string(),
valid_range: "0-63".to_string(),
});
}
Ok(vec![*v])
}
DptValue::Scene { number, .. } => {
if *number > 63 {
return Err(KnxError::DptValueOutOfRange {
value: number.to_string(),
valid_range: "0-63".to_string(),
});
}
Ok(vec![*number])
}
_ => Err(KnxError::dpt_encoding(
"DPT 17.001",
"Expected scene number",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 1, "DPT 17.001")?;
Ok(DptValue::Scene {
number: data[0] & 0x3F,
learn: false,
})
}
fn min_value(&self) -> Option<f64> {
Some(0.0)
}
fn max_value(&self) -> Option<f64> {
Some(63.0)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt18SceneControl;
impl DptCodec for Dpt18SceneControl {
fn id(&self) -> DptId {
DptId::new(18, 1)
}
fn name(&self) -> &'static str {
"Scene Control"
}
fn size(&self) -> usize {
1
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::Scene { number, learn } => {
if *number > 63 {
return Err(KnxError::DptValueOutOfRange {
value: number.to_string(),
valid_range: "0-63".to_string(),
});
}
let byte = (if *learn { 0x80 } else { 0 }) | (*number & 0x3F);
Ok(vec![byte])
}
_ => Err(KnxError::dpt_encoding("DPT 18.001", "Expected Scene value")),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 1, "DPT 18.001")?;
Ok(DptValue::Scene {
number: data[0] & 0x3F,
learn: data[0] & 0x80 != 0,
})
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt20HvacMode;
impl DptCodec for Dpt20HvacMode {
fn id(&self) -> DptId {
DptId::new(20, 102)
}
fn name(&self) -> &'static str {
"HVAC Mode"
}
fn size(&self) -> usize {
1
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::HvacMode(mode) => Ok(vec![mode.to_raw()]),
DptValue::U8(v) => Ok(vec![*v]),
_ => Err(KnxError::dpt_encoding(
"DPT 20.102",
"Expected HvacMode value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
use super::value::HvacMode;
validate_length(data, 1, "DPT 20.102")?;
match HvacMode::from_raw(data[0]) {
Some(mode) => Ok(DptValue::HvacMode(mode)),
None => Ok(DptValue::U8(data[0])),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Dpt232Rgb;
impl DptCodec for Dpt232Rgb {
fn id(&self) -> DptId {
DptId::new(232, 600)
}
fn name(&self) -> &'static str {
"Color RGB"
}
fn size(&self) -> usize {
3
}
fn encode(&self, value: &DptValue) -> KnxResult<Vec<u8>> {
match value {
DptValue::ColorRgb { r, g, b } => Ok(vec![*r, *g, *b]),
_ => Err(KnxError::dpt_encoding(
"DPT 232.600",
"Expected ColorRgb value",
)),
}
}
fn decode(&self, data: &[u8]) -> KnxResult<DptValue> {
validate_length(data, 3, "DPT 232.600")?;
Ok(DptValue::ColorRgb {
r: data[0],
g: data[1],
b: data[2],
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dpt1_switch() {
let codec = Dpt1Switch;
let encoded = codec.encode(&DptValue::Bool(true)).unwrap();
assert_eq!(encoded, vec![1]);
let decoded = codec.decode(&[1]).unwrap();
assert_eq!(decoded, DptValue::Bool(true));
let decoded = codec.decode(&[0]).unwrap();
assert_eq!(decoded, DptValue::Bool(false));
}
#[test]
fn test_dpt5_scaling() {
let codec = Dpt5Scaling;
let encoded = codec.encode(&DptValue::F16(100.0)).unwrap();
assert_eq!(encoded, vec![255]);
let encoded = codec.encode(&DptValue::F16(50.0)).unwrap();
assert!(encoded[0] >= 127 && encoded[0] <= 128);
let decoded = codec.decode(&[255]).unwrap();
if let DptValue::F16(v) = decoded {
assert!((v - 100.0).abs() < 0.5);
} else {
panic!("Expected F16");
}
}
#[test]
fn test_dpt9_temperature() {
let codec = Dpt9Temperature;
let original = DptValue::F16(25.5);
let encoded = codec.encode(&original).unwrap();
let decoded = codec.decode(&encoded).unwrap();
if let (DptValue::F16(o), DptValue::F16(d)) = (&original, &decoded) {
assert!((o - d).abs() < 0.1, "Expected ~{}, got {}", o, d);
}
}
#[test]
fn test_dpt16_string() {
let codec = Dpt16String;
let value = DptValue::String("Hello".to_string());
let encoded = codec.encode(&value).unwrap();
assert_eq!(encoded.len(), 14);
assert_eq!(&encoded[..5], b"Hello");
let decoded = codec.decode(&encoded).unwrap();
assert_eq!(decoded, DptValue::String("Hello".to_string()));
}
#[test]
fn test_dpt232_rgb() {
let codec = Dpt232Rgb;
let value = DptValue::rgb(255, 128, 64);
let encoded = codec.encode(&value).unwrap();
assert_eq!(encoded, vec![255, 128, 64]);
let decoded = codec.decode(&[255, 128, 64]).unwrap();
assert_eq!(
decoded,
DptValue::ColorRgb {
r: 255,
g: 128,
b: 64
}
);
}
}