feat: rewrite spec and proto

This commit is contained in:
Strix 2023-11-30 21:52:49 +01:00
parent 11e58c3d1d
commit 7999ac6103
No known key found for this signature in database
GPG key ID: 5F35B3B8537287A7
6 changed files with 298 additions and 144 deletions

View file

@ -1,10 +1,69 @@
> **Version:** `1` > **Version:** `1`
> **Authored by:** `Raine <raine@ixvd.net>` > **Authored by:** `Raine <raine@ixvd.net>`
> **Status**: `planning` >
# Table of contents
- [Table of contents](#table-of-contents)
- [Prelude](#prelude)
- [Interpretation of types](#interpretation-of-types)
- [Booleans](#booleans)
- [Reserved fields](#reserved-fields)
- [Structure](#structure)
- [Data types](#data-types)
- [Dynamic data (`dynamic_data`)](#dynamic-data-dynamic_data)
- [Commands](#commands)
- [`0x0*` - Node management](#0x0---node-management)
- [`0x00` - Ping](#0x00---ping)
- [Command data](#command-data)
- [`0x01` - Register node](#0x01---register-node)
- [Command data](#command-data-1)
- [`0x02` - Remove node](#0x02---remove-node)
- [Command data](#command-data-2)
- [`0x0A` - Acknowledge packets](#0x0a---acknowledge-packets)
- [`0x0E` - Error](#0x0e---error)
- [Command data](#command-data-3)
- [Error codes](#error-codes)
- [`0x1*` - Properties control](#0x1---properties-control)
- [`0x10` - Register property](#0x10---register-property)
- [Command data](#command-data-4)
- [`0x11` - Remove property](#0x11---remove-property)
- [Command data](#command-data-5)
- [`0x12` - Get property](#0x12---get-property)
- [Command data](#command-data-6)
- [`0x13` - Set property](#0x13---set-property)
- [Command data](#command-data-7)
- [`0x14` - Reset property](#0x14---reset-property)
- [Command data](#command-data-8)
- [`0x1A` - Subscribe to property](#0x1a---subscribe-to-property)
- [`0x1B` - Unsubscribe to property](#0x1b---unsubscribe-to-property)
- [`0x1F` - All properties](#0x1f---all-properties)
- [Command data](#command-data-9)
- [`0xF*` - Raw data transmission](#0xf---raw-data-transmission)
- [`0xF0` - Setup transfer](#0xf0---setup-transfer)
- [Command data](#command-data-10)
- [`0xF1` - Data](#0xf1---data)
- [Command data](#command-data-11)
# Prelude # Prelude
Version 1 has zero security on itself. This document only describes what the protocol looks like.
The specification can be found at `./specification.md`.
## Interpretation of types
### Booleans
If in the description of a property it's marked as a boolean the exact logic that should be executed is:
`value != 0`
## Reserved fields
The "reserved" column (if present) will state what should be filled in by each side.
- "no": this should be filled in by both sides. (default)
- "request": this should only be filled in the request.
- "response": this should only be filled in the response.
# Structure # Structure
@ -22,30 +81,6 @@ Packets are sent in big endian.
| `0x14` | `<0x11-0x12:data_length>` | `data` | This is the data of the command | `0x0000` | | `0x14` | `<0x11-0x12:data_length>` | `data` | This is the data of the command | `0x0000` |
| `0x14 + <0x11-0x12:data_length>` | 4 bytes | `checksum` | This is the CRC32 checksum of the packet without the checksum. | `0x00000000` | | `0x14 + <0x11-0x12:data_length>` | 4 bytes | `checksum` | This is the CRC32 checksum of the packet without the checksum. | `0x00000000` |
# Packets in practice
## Statuses
When an error occurs the response should be a `0x0E` (error command).
This can contain error data.
To mark a success, you should just send back the expected response.
## Reserved fields
The "reserved" column (if present) will state what should be filled in by each side.
- "no": this should be filled in by both sides. (default)
- "request": this should only be filled in the request.
- "response": this should only be filled in the response.
# Error checking
If the CRC32 doesn't match up the receiver will send a `0x0E` (error) packet to the probable source.
The error code `0x01`/`net_broken_packet` should be used. The metadata may contain some textual info on what went
wrong.
The master will not track the broken packet's `packet_id`, so the slave can send it again.
# Data types # Data types
Domo has a data framework reliant on data types. Domo has a data framework reliant on data types.
@ -64,23 +99,16 @@ These define what kind of data the property holds.
| `0x2*` | | | **Color** | | `0x2*` | | | **Color** |
| `0x20` | 3 bytes | RGB | An RGB value. | | `0x20` | 3 bytes | RGB | An RGB value. |
<!--
REQ prop/get "firmware"
RES prop/get <DataType::Identifier(0xABFF21FA)> // this is the setup packet id
RES raw/setup size=10B
RES raw/data [0x0000 (0x2710)] ....
-->
## Dynamic data (`dynamic_data`) ## Dynamic data (`dynamic_data`)
Dynamic data is a data snippet with a `prop_data_type` byte and the data next to it. Dynamic data is a data snippet with a `prop_data_type` byte and the data next to it.
> **Note**: this section uses relative offset > **Note**: this section uses relative offset
| offset | size | name | description | example | | offset | size | name | description | example |
| ------ | ----------------------------- | ---------------- | ----------------------- | ------- | | ------ | --------------------------- | ---------------- | ----------------------- | ------- |
| `0x00` | 1 byte | `prop_data_type` | Describes the data type | `0x00` | | `0x00` | 1 byte | `prop_data_type` | Describes the data type | `0x00` |
| `0x01` | depending on `prop_data_type` | `data` | The data | | | `0x01` | depends on `prop_data_type` | `data` | The data | |
# Commands # Commands
@ -121,23 +149,13 @@ Remove node from network
there is no extra data required. there is no extra data required.
### `0x03` - Register property ### `0x0A` - Acknowledge packets
#### Command data Acknowledge received packets.
| offset | size | name | description | reserved | example | | offset | size | name | description | reserved | example |
| ------ | -------- | --------------- | ------------------------------------------------------------------ | -------- | ------- | | ------ | ------------- | ----------- | ---------------------------------- | ----------- | ------- |
| `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" | | `0x14` | 4 bytes * `n` | `packet_id` | list of packet_id's to acknowledge | origin node | |
| `0x34` | 1 byte | `data_type` | The type of data; see "Data types". | no | `0x01` |
| `0x35` | 1 byte | `read_only` | Whether the property is readonly. <br/>(`0x00` = no, `0x01` = yes) | no | `0x00` |
### `0x04` - Remove property
#### Command data
| offset | size | name | description | reserved | example |
| ------ | -------- | --------------- | ------------------------------------------- | -------- | ------- |
| `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" |
### `0x0E` - Error ### `0x0E` - Error
@ -145,10 +163,10 @@ Send a packet to state an error occurred.
#### Command data #### Command data
| offset | size | name | description | example | | offset | size | name | description | example |
| ------ | ----------------------------- | ------------ | ------------------------------- | ------- | | ------ | ------------- | ------------ | ------------------------------- | ------- |
| `0x14` | 1 byte | `error_code` | error code; check 'Error codes' | `0x00` | | `0x14` | 1 byte | `error_code` | error code; check 'Error codes' | `0x00` |
| `0x15` | `<0x14-0x15:metadata_length>` | `metadata` | metadata | | | `0x15` | `data_length` | `metadata` | metadata | |
#### Error codes #### Error codes
@ -167,20 +185,40 @@ Send a packet to state an error occurred.
| `0x20` | `node_invalid_property` | the property specified is invalid | no | | `0x20` | `node_invalid_property` | the property specified is invalid | no |
| `0x21` | `node_failed_request` | the request could not be completed. | consult metadata | | `0x21` | `node_failed_request` | the request could not be completed. | consult metadata |
## `0x1*` - Properties control ## `0x1*` - Properties control
### `0x10` - Get property ### `0x10` - Register property
#### Command data
| offset | size | name | description | reserved | example |
| ------ | -------- | --------------- | ---------------------------------------------- | -------- | ------- |
| `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" |
| `0x34` | 1 byte | `data_type` | The type of data; see "Data types". | no | `0x01` |
| `0x35` | 1 byte | `read_only` | Whether the property is readonly. (boolean) | no | `0x00` |
| `0x36` | 1 byte | `descriptive` | Whether the property is descriptive. (boolean) | no | `0x00` |
### `0x11` - Remove property
#### Command data
| offset | size | name | description | reserved | example |
| ------ | -------- | --------------- | ------------------------------------------- | -------- | ------- |
| `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" |
### `0x12` - Get property
Get a properties value Get a properties value
#### Command data #### Command data
| offset | size | name | description | reserved | example | | offset | size | name | description | reserved | example |
| ------ | -------- | --------------- | ------------------------------------------- | -------- | -------- | | ------ | -------- | --------------- | ------------------------------------------- | ---------------- | -------- |
| `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" | | `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" |
| `0x34` | dynamic | `dynamic_data` | Dynamic data snippet | response | `0x0100` | | `0x34` | dynamic | `dynamic_data` | Dynamic data snippet | destination node | `0x0100` |
### `0x11` - Set property ### `0x13` - Set property
#### Command data #### Command data
@ -189,7 +227,7 @@ Get a properties value
| `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" | | `0x14` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" |
| `0x34` | dynamic | `dynamic_data` | Dynamic data snippet | no | `0x0100` | | `0x34` | dynamic | `dynamic_data` | Dynamic data snippet | no | `0x0100` |
### `0x12` - Reset property ### `0x14` - Reset property
#### Command data #### Command data
@ -204,13 +242,26 @@ Get a properties value
| `0x14` | 4 bytes | `device_id` | Who's property? | no | `0x00000000` | | `0x14` | 4 bytes | `device_id` | Who's property? | no | `0x00000000` |
| `0x18` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" | | `0x18` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" |
### `0x1A` - Unsubscribe to property ### `0x1B` - Unsubscribe to property
| offset | size | name | description | reserved | example | | offset | size | name | description | reserved | example |
| ------ | -------- | --------------- | ------------------------------------------- | -------- | ------------ | | ------ | -------- | --------------- | ------------------------------------------- | -------- | ------------ |
| `0x14` | 4 bytes | `device_id` | Who's property? | no | `0x00000000` | | `0x14` | 4 bytes | `device_id` | Who's property? | no | `0x00000000` |
| `0x18` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" | | `0x18` | 32 bytes | `property_name` | The name of the property as a UTF-8 string. | no | "Power" |
### `0x1F` - All properties
Return all properties.
#### Command data
| offset | size | name | description | reserved | example |
| ------ | ------ | ------ | ------------------------- | -------- | ------- |
| `0x14` | 1 byte | `type` | | no | `0x00` |
| | | | `0x00` - all | | |
| | | | `0x01` - descriptive only | | |
| | | | `0x02` - m only | | |
## `0xF*` - Raw data transmission ## `0xF*` - Raw data transmission
This is useful for things like OTA updates. This is useful for things like OTA updates.
@ -221,10 +272,10 @@ Since this procedure is quite expensive (network wise), the sides must shake han
#### Command data #### Command data
| offset | size | name | description | reserved | example | | offset | size | name | description | reserved | example |
| ------ | --------- | ------ | -------------------- | -------- | ------- | | ------ | --------- | ------ | -------------------- | ----------- | ------- |
| `0x14` | 8 bytes | `size` | u64 size of the data | request | `0x00` | | `0x14` | 8 bytes | `size` | u64 size of the data | origin node | `0x00` |
| `0x1c` | 128 bytes | `mime` | media type of data | request | `0x00` | | `0x1c` | 128 bytes | `mime` | media type of data | origin node | `0x00` |
### `0xF1` - Data ### `0xF1` - Data
@ -235,7 +286,7 @@ This may be sent without the initial transfer setup for custom implementations.
The `sequence_number` is added for redundancy, all data sent will always reply to it's previous data segment. The `sequence_number` is added for redundancy, all data sent will always reply to it's previous data segment.
| offset | size | name | description | example | | offset | size | name | description | example |
| ------ | -------- | ----------------- | ------------------------------- | ------------ | | ------ | ------------- | ----------------- | ------------------------------- | ------------ |
| `0x14` | 4 bytes | `sequence_number` | the sequence number of the data | `0x00000000` | | `0x14` | 4 bytes | `sequence_number` | the sequence number of the data | `0x00000000` |
| `0x18` | `<size>` | `data` | the data | | | `0x18` | `data_length` | `data` | the data | |

46
doc/specification.md Normal file
View file

@ -0,0 +1,46 @@
# Protocol Design
In this document I will define how this document is designed and how it should be implemented.
# Prelude
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL",
"NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
RFC 2119.
"node" or "nodes" are referring to a device that implements the Domo protocol.
## Transactions
All interactions between 2 nodes is called a transaction; a request and response.
The initial packet sent in the transaction is called the "origin packet".
A initial packet MUST NOT have a `reply_to`.
A transaction consists of 2 nodes;
- "Origin node"; the node who sent the origin packet.
- "Destination node"; the node who is marked as the destination by the origin packet.
# Behaviour
This chapter describes the behaviour of a node.
## Statuses
When an error occurs on the destination node in result of a request, the response to the origin node MUST be a `0x0E` (error command).
This MAY contain error data.
To mark a success, you MUST send back the request packet altered with the data specified in the protocol.
## Reserved addresses
### Broadcast (`0xFFFFFFFF`)
Nodes MUST listen to this address.
Owner nodes MUST forward this address to their owners if applicable.
# Packet inspection
## Error checking
If the CRC32 doesn't match up the destination node MUST send a `0x0E` (error) packet to the origin node.
The error code `0x01`/`net_broken_packet` MUST be used. The metadata MAY contain some textual info on what went wrong.
The master MUST not track the broken packet's `packet_id`, so the slave can send it again.

View file

@ -12,13 +12,8 @@ pub enum NodeManagementCommand {
Ping, Ping,
RegisterNode { device_id: Identifier }, RegisterNode { device_id: Identifier },
RemoveNode, RemoveNode,
RegisterProperty { AcknowledgePackets {
property_name: String, packet_ids: Vec<Identifier>
data_type: u8,
read_only: bool,
},
RemoveProperty {
property_name: String,
}, },
Error { Error {
error_code: u8, error_code: u8,
@ -32,9 +27,8 @@ impl NodeManagementCommand {
NodeManagementCommand::Ping => 0x00, NodeManagementCommand::Ping => 0x00,
NodeManagementCommand::RegisterNode { .. } => 0x01, NodeManagementCommand::RegisterNode { .. } => 0x01,
NodeManagementCommand::RemoveNode => 0x02, NodeManagementCommand::RemoveNode => 0x02,
NodeManagementCommand::RegisterProperty { .. } => 0x03, NodeManagementCommand::AcknowledgePackets { .. } => 0x0A,
NodeManagementCommand::RemoveProperty { .. } => 0x04, NodeManagementCommand::Error { .. } => 0x0E,
NodeManagementCommand::Error { .. } => 0x0E
} }
} }
} }
@ -68,26 +62,15 @@ impl TryFrom<RawPacket> for NodeManagementCommand {
}, },
// remove node // remove node
0x02 => Ok(NodeManagementCommand::RemoveNode), 0x02 => Ok(NodeManagementCommand::RemoveNode),
// register property 0x0A => if raw_packet.data_length >= 4 && (raw_packet.data_length % 4 == 0) {
0x03 => if raw_packet.data_length == 34 { Ok(NodeManagementCommand::AcknowledgePackets {
Ok(NodeManagementCommand::RegisterProperty { packet_ids: raw_packet.data.chunks(4)
property_name: String::from_utf8(raw_packet.data[..32].to_vec()) .map(|c| Identifier::from(c))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?, .collect()
data_type: raw_packet.data[33], })
read_only: raw_packet.data[34] != 0,
})
} else { } else {
build_invalid_input_err("expected 34 bytes") build_invalid_input_err("expected at least 4 bytes and data_length%4=0")
}, }
// remove property
0x04 => if raw_packet.data_length == 32 {
Ok(NodeManagementCommand::RemoveProperty {
property_name: String::from_utf8(raw_packet.data.to_vec())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?
})
} else {
build_invalid_input_err("expected 32 bytes")
},
// error // error
0x0E => if raw_packet.data_length >= 1 { 0x0E => if raw_packet.data_length >= 1 {
Ok(NodeManagementCommand::Error { Ok(NodeManagementCommand::Error {

View file

@ -6,18 +6,21 @@ impl Into<Vec<u8>> for NodeManagementCommand {
match self { match self {
NodeManagementCommand::Ping => vec![], NodeManagementCommand::Ping => vec![],
NodeManagementCommand::RegisterNode { device_id } => { NodeManagementCommand::RegisterNode { device_id } => {
vec![device_id.bytes[0], device_id.bytes[1], device_id.bytes[2], device_id.bytes[3]] vec![
}, device_id.bytes[0],
device_id.bytes[1],
device_id.bytes[2],
device_id.bytes[3],
]
}
NodeManagementCommand::RemoveNode => vec![], NodeManagementCommand::RemoveNode => vec![],
NodeManagementCommand::RegisterProperty { property_name, data_type, read_only } => { NodeManagementCommand::AcknowledgePackets { packet_ids } =>
let mut vec = vec![]; packet_ids.iter().map(|i| i.bytes).flatten().collect(),
vec.extend(property_name.into_bytes());
vec.push(data_type); NodeManagementCommand::Error {
vec.push(read_only as u8); error_code,
vec metadata,
}, } => {
NodeManagementCommand::RemoveProperty { property_name } => property_name.into_bytes(),
NodeManagementCommand::Error { error_code, metadata } => {
let mut vec = vec![]; let mut vec = vec![];
vec.push(error_code); vec.push(error_code);
vec.extend(metadata); vec.extend(metadata);

View file

@ -1,15 +1,24 @@
use std::io;
use crate::data_types::DataType; use crate::data_types::DataType;
use crate::identifier::Identifier; use crate::identifier::Identifier;
use crate::packet::packet_data::PacketData;
use crate::packet::raw_packet::RawPacket; use crate::packet::raw_packet::RawPacket;
use crate::packet::{Packet, ToPacket}; use crate::packet::{Packet, ToPacket};
use crate::packet::packet_data::PacketData;
use crate::prelude::as_u32_be; use crate::prelude::as_u32_be;
use std::io;
pub mod vec; pub mod vec;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum PropertyControlCommand { pub enum PropertyControlCommand {
Register {
property_name: String,
data_type: u8,
read_only: bool,
// TODO: descriptive
},
Remove {
property_name: String,
},
Get { Get {
property_name: String, property_name: String,
data: DataType, data: DataType,
@ -34,21 +43,32 @@ pub enum PropertyControlCommand {
impl PropertyControlCommand { impl PropertyControlCommand {
pub fn command(&self) -> u8 { pub fn command(&self) -> u8 {
match self { match self {
PropertyControlCommand::Get { .. } => 0x10, PropertyControlCommand::Register { .. } => 0x10,
PropertyControlCommand::Set { .. } => 0x11, PropertyControlCommand::Remove { .. } => 0x11,
PropertyControlCommand::Reset { .. } => 0x12, PropertyControlCommand::Get { .. } => 0x12,
PropertyControlCommand::Set { .. } => 0x13,
PropertyControlCommand::Reset { .. } => 0x14,
PropertyControlCommand::Subscribe { .. } => 0x1A, PropertyControlCommand::Subscribe { .. } => 0x1A,
PropertyControlCommand::Unsubscribe { .. } => 0x1B PropertyControlCommand::Unsubscribe { .. } => 0x1B,
} }
} }
} }
impl ToPacket for PropertyControlCommand { impl ToPacket for PropertyControlCommand {
fn to_packet(self, src: Identifier, dest: Identifier, packet_id: Identifier, reply_to: Identifier) -> Packet { fn to_packet(
self,
src: Identifier,
dest: Identifier,
packet_id: Identifier,
reply_to: Identifier,
) -> Packet {
Packet { Packet {
src, dest, packet_id, reply_to, src,
dest,
packet_id,
reply_to,
command: self.command(), command: self.command(),
data: PacketData::PropertyControl(self.clone()) data: PacketData::PropertyControl(self.clone()),
} }
} }
} }
@ -56,26 +76,56 @@ impl ToPacket for PropertyControlCommand {
impl TryFrom<RawPacket> for PropertyControlCommand { impl TryFrom<RawPacket> for PropertyControlCommand {
type Error = io::Error; type Error = io::Error;
fn try_from(raw_packet: RawPacket) -> io::Result<Self> { fn try_from(raw_packet: RawPacket) -> io::Result<Self> {
let build_invalid_input_err = |r: &str| { let build_invalid_input_err = |r: &str| Err(io::Error::new(io::ErrorKind::InvalidInput, r));
Err(io::Error::new(io::ErrorKind::InvalidInput, r))
};
match raw_packet.command { match raw_packet.command {
// get property // remove node
0x10 => if raw_packet.data_length == 33 { 0x10 => {
Ok(PropertyControlCommand::Get { if raw_packet.data_length == 34 {
property_name: String::from_utf8(raw_packet.data[..0x34].to_vec()) Ok(PropertyControlCommand::Register {
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?, property_name: String::from_utf8(raw_packet.data[..32].to_vec()).map_err(
data: DataType::try_from(raw_packet.data[0x34..].to_vec())?, |_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"),
}) )?,
} else { data_type: raw_packet.data[33],
build_invalid_input_err("expected 33 bytes") read_only: raw_packet.data[34] != 0,
}, })
// set property } else {
build_invalid_input_err("expected 34 bytes")
}
}
// remove property
0x11 => { 0x11 => {
if raw_packet.data_length == 32 {
Ok(PropertyControlCommand::Remove {
property_name: String::from_utf8(raw_packet.data.to_vec()).map_err(
|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"),
)?,
})
} else {
build_invalid_input_err("expected 32 bytes")
}
}
// get property
0x12 => {
if raw_packet.data_length == 33 {
Ok(PropertyControlCommand::Get {
property_name: String::from_utf8(raw_packet.data[..0x34].to_vec())
.map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8")
})?,
data: DataType::try_from(raw_packet.data[0x34..].to_vec())?,
})
} else {
build_invalid_input_err("expected 33 bytes")
}
}
// set property
0x13 => {
if raw_packet.data_length == 33 { if raw_packet.data_length == 33 {
Ok(PropertyControlCommand::Set { Ok(PropertyControlCommand::Set {
property_name: String::from_utf8(raw_packet.data[..0x34].to_vec()) property_name: String::from_utf8(raw_packet.data[..0x34].to_vec())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?, .map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8")
})?,
data: DataType::try_from(raw_packet.data[0x34..].to_vec())?, data: DataType::try_from(raw_packet.data[0x34..].to_vec())?,
}) })
} else { } else {
@ -83,11 +133,13 @@ impl TryFrom<RawPacket> for PropertyControlCommand {
} }
} }
// reset property // reset property
0x12 => { 0x14 => {
if raw_packet.data_length == 33 { if raw_packet.data_length == 33 {
Ok(PropertyControlCommand::Reset { Ok(PropertyControlCommand::Reset {
property_name: String::from_utf8(raw_packet.data[..0x34].to_vec()) property_name: String::from_utf8(raw_packet.data[..0x34].to_vec())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?, .map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8")
})?,
}) })
} else { } else {
build_invalid_input_err("expected 33 bytes") build_invalid_input_err("expected 33 bytes")
@ -97,9 +149,13 @@ impl TryFrom<RawPacket> for PropertyControlCommand {
0x1A => { 0x1A => {
if raw_packet.data_length == 36 { if raw_packet.data_length == 36 {
Ok(PropertyControlCommand::Subscribe { Ok(PropertyControlCommand::Subscribe {
device_id: Identifier::from(as_u32_be(raw_packet.data[0x14..0x18].as_ref())), device_id: Identifier::from(as_u32_be(
raw_packet.data[0x14..0x18].as_ref(),
)),
property_name: String::from_utf8(raw_packet.data[0x18..0x4C].to_vec()) property_name: String::from_utf8(raw_packet.data[0x18..0x4C].to_vec())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?, .map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8")
})?,
}) })
} else { } else {
build_invalid_input_err("expected 36 bytes") build_invalid_input_err("expected 36 bytes")
@ -109,15 +165,22 @@ impl TryFrom<RawPacket> for PropertyControlCommand {
0x1B => { 0x1B => {
if raw_packet.data_length == 36 { if raw_packet.data_length == 36 {
Ok(PropertyControlCommand::Unsubscribe { Ok(PropertyControlCommand::Unsubscribe {
device_id: Identifier::from(as_u32_be(raw_packet.data[0x14..0x18].as_ref())), device_id: Identifier::from(as_u32_be(
raw_packet.data[0x14..0x18].as_ref(),
)),
property_name: String::from_utf8(raw_packet.data[0x18..0x4C].to_vec()) property_name: String::from_utf8(raw_packet.data[0x18..0x4C].to_vec())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?, .map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8")
})?,
}) })
} else { } else {
build_invalid_input_err("expected 36 bytes") build_invalid_input_err("expected 36 bytes")
} }
} }
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "command group is unsupported")) _ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"command group is unsupported",
)),
} }
} }
} }

View file

@ -4,6 +4,14 @@ use crate::commands::property_control::PropertyControlCommand;
impl Into<Vec<u8>> for PropertyControlCommand { impl Into<Vec<u8>> for PropertyControlCommand {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
match self { match self {
PropertyControlCommand::Register { property_name, data_type, read_only } => {
let mut vec = vec![];
vec.extend(property_name.into_bytes());
vec.push(data_type);
vec.push(read_only as u8);
vec
},
PropertyControlCommand::Remove { property_name } => property_name.into_bytes(),
PropertyControlCommand::Get { property_name, data } => { PropertyControlCommand::Get { property_name, data } => {
let mut v = vec![]; let mut v = vec![];
v.extend(property_name.into_bytes()); v.extend(property_name.into_bytes());