Implemented data structures for SYNC, QUERY, and EXECUTE
This commit is contained in:
parent
6627174c6f
commit
e4369ebf41
|
@ -1,43 +1,24 @@
|
||||||
|
pub mod sync;
|
||||||
|
pub mod query;
|
||||||
|
pub mod execute;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(tag = "intent")]
|
#[serde(tag = "intent", content = "payload")]
|
||||||
enum Intent {
|
enum Intent {
|
||||||
#[serde(rename = "action.devices.SYNC")]
|
#[serde(rename = "action.devices.SYNC")]
|
||||||
Sync,
|
Sync,
|
||||||
|
#[serde(rename = "action.devices.QUERY")]
|
||||||
|
Query(query::Payload),
|
||||||
|
#[serde(rename = "action.devices.EXECUTE")]
|
||||||
|
Execute(execute::Payload),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct Request {
|
struct Request {
|
||||||
request_id: Uuid,
|
pub request_id: Uuid,
|
||||||
inputs: Vec<Intent>,
|
pub inputs: Vec<Intent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_sync_request() {
|
|
||||||
|
|
||||||
let json = r#"{
|
|
||||||
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"intent": "action.devices.SYNC"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
let req: Request = serde_json::from_str(json).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(req.request_id, Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap());
|
|
||||||
assert_eq!(req.inputs.len(), 1);
|
|
||||||
assert_eq!(req.inputs[0], Intent::Sync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
104
google-home/src/request/execute.rs
Normal file
104
google-home/src/request/execute.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Payload {
|
||||||
|
commands: Vec<Command>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Command {
|
||||||
|
devices: Vec<Device>,
|
||||||
|
execution: Vec<CommandType>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Device {
|
||||||
|
id: String,
|
||||||
|
// customData
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "command", content = "params")]
|
||||||
|
pub enum CommandType {
|
||||||
|
#[serde(rename = "action.devices.commands.OnOff")]
|
||||||
|
OnOff {
|
||||||
|
on: bool
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use super::*;
|
||||||
|
use crate::request::{Request, Intent};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
|
||||||
|
let json = r#"{
|
||||||
|
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"intent": "action.devices.EXECUTE",
|
||||||
|
"payload": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"id": "123",
|
||||||
|
"customData": {
|
||||||
|
"fooValue": 74,
|
||||||
|
"barValue": true,
|
||||||
|
"bazValue": "sheepdip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "456",
|
||||||
|
"customData": {
|
||||||
|
"fooValue": 36,
|
||||||
|
"barValue": false,
|
||||||
|
"bazValue": "moarsheep"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"execution": [
|
||||||
|
{
|
||||||
|
"command": "action.devices.commands.OnOff",
|
||||||
|
"params": {
|
||||||
|
"on": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let req: Request = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", req);
|
||||||
|
|
||||||
|
assert_eq!(req.request_id, Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap());
|
||||||
|
assert_eq!(req.inputs.len(), 1);
|
||||||
|
match &req.inputs[0] {
|
||||||
|
Intent::Execute(payload) => {
|
||||||
|
assert_eq!(payload.commands.len(), 1);
|
||||||
|
assert_eq!(payload.commands[0].devices.len(), 2);
|
||||||
|
assert_eq!(payload.commands[0].devices[0].id, "123");
|
||||||
|
assert_eq!(payload.commands[0].devices[1].id, "456");
|
||||||
|
assert_eq!(payload.commands[0].execution.len(), 1);
|
||||||
|
match payload.commands[0].execution[0] {
|
||||||
|
CommandType::OnOff{on} => assert_eq!(on, true),
|
||||||
|
// _ => panic!("Expected OnOff")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => panic!("Expected Execute intent")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
71
google-home/src/request/query.rs
Normal file
71
google-home/src/request/query.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Payload {
|
||||||
|
pub devices: Vec<Device>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Device {
|
||||||
|
pub id: String,
|
||||||
|
// customData
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::request::{Request, Intent};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
|
||||||
|
let json = r#"{
|
||||||
|
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"intent": "action.devices.QUERY",
|
||||||
|
"payload": {
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"id": "123",
|
||||||
|
"customData": {
|
||||||
|
"fooValue": 74,
|
||||||
|
"barValue": true,
|
||||||
|
"bazValue": "foo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "456",
|
||||||
|
"customData": {
|
||||||
|
"fooValue": 12,
|
||||||
|
"barValue": false,
|
||||||
|
"bazValue": "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let req: Request = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", req);
|
||||||
|
|
||||||
|
assert_eq!(req.request_id, Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap());
|
||||||
|
assert_eq!(req.inputs.len(), 1);
|
||||||
|
match &req.inputs[0] {
|
||||||
|
Intent::Query(payload) => {
|
||||||
|
assert_eq!(payload.devices.len(), 2);
|
||||||
|
assert_eq!(payload.devices[0].id, "123");
|
||||||
|
assert_eq!(payload.devices[1].id, "456");
|
||||||
|
},
|
||||||
|
_ => panic!("Expected Query intent")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
33
google-home/src/request/sync.rs
Normal file
33
google-home/src/request/sync.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::request::{Request, Intent};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
|
||||||
|
let json = r#"{
|
||||||
|
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"intent": "action.devices.SYNC"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let req: Request = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", req);
|
||||||
|
|
||||||
|
assert_eq!(req.request_id, Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap());
|
||||||
|
assert_eq!(req.inputs.len(), 1);
|
||||||
|
match req.inputs[0] {
|
||||||
|
Intent::Sync => {},
|
||||||
|
_ => panic!("Expected Sync intent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
pub mod query;
|
||||||
|
pub mod execute;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -10,35 +12,31 @@ pub struct Response {
|
||||||
payload: ResponsePayload,
|
payload: ResponsePayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
fn new(request_id: Uuid, payload: ResponsePayload) -> Self {
|
||||||
|
Self { request_id, payload }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum ResponsePayload {
|
pub enum ResponsePayload {
|
||||||
Sync(sync::Payload)
|
Sync(sync::Payload),
|
||||||
|
Query(query::Payload),
|
||||||
|
Execute(execute::Payload),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
mod tests {
|
#[serde(rename_all = "camelCase")]
|
||||||
use std::str::FromStr;
|
pub struct State {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
on: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
use crate::{response::sync::Device, types::Type, traits::Trait};
|
impl State {
|
||||||
|
fn on(mut self, state: bool) -> Self {
|
||||||
use super::*;
|
self.on = Some(state);
|
||||||
|
self
|
||||||
#[test]
|
|
||||||
fn serialize_sync_response() {
|
|
||||||
let mut sync_resp = sync::Payload::new("Dreaded_X");
|
|
||||||
|
|
||||||
let mut device = Device::new("kitchen/kettle", "Kettle", Type::Kettle);
|
|
||||||
device.traits.push(Trait::OnOff);
|
|
||||||
device.room_hint = "Kitchen".into();
|
|
||||||
sync_resp.add_device(device);
|
|
||||||
|
|
||||||
let resp = Response{ request_id: Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap(), payload: ResponsePayload::Sync(sync_resp) };
|
|
||||||
|
|
||||||
println!("{:?}", resp);
|
|
||||||
|
|
||||||
let json = serde_json::to_string(&resp).unwrap();
|
|
||||||
|
|
||||||
println!("{}", json);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
98
google-home/src/response/execute.rs
Normal file
98
google-home/src/response/execute.rs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::response::State;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Payload {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error_code: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub debug_string: Option<String>,
|
||||||
|
commands: Vec<Command>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Payload {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { error_code: None, debug_string: None, commands: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_command(&mut self, command: Command) {
|
||||||
|
self.commands.push(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Command {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error_code: Option<String>,
|
||||||
|
|
||||||
|
ids: Vec<String>,
|
||||||
|
status: Status,
|
||||||
|
pub states: Option<States>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn new(status: Status) -> Self {
|
||||||
|
Self { error_code: None, ids: Vec::new(), status, states: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_id(&mut self, id: &str) {
|
||||||
|
self.ids.push(id.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct States {
|
||||||
|
pub online: bool,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub state: Option<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
pub enum Status {
|
||||||
|
Success,
|
||||||
|
Pending,
|
||||||
|
Offline,
|
||||||
|
Exceptions,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use super::*;
|
||||||
|
use crate::response::{Response, ResponsePayload, State};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() {
|
||||||
|
let mut execute_resp = Payload::new();
|
||||||
|
|
||||||
|
let state = State::default().on(true);
|
||||||
|
let mut command = Command::new(Status::Success);
|
||||||
|
command.states = Some(States {
|
||||||
|
online: true,
|
||||||
|
state: Some(state)
|
||||||
|
});
|
||||||
|
command.ids.push("123".into());
|
||||||
|
execute_resp.add_command(command);
|
||||||
|
|
||||||
|
let mut command = Command::new(Status::Error);
|
||||||
|
command.error_code = Some("deviceTurnedOff".into());
|
||||||
|
command.ids.push("456".into());
|
||||||
|
execute_resp.add_command(command);
|
||||||
|
|
||||||
|
let resp = Response::new(Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap(), ResponsePayload::Execute(execute_resp));
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&resp).unwrap();
|
||||||
|
|
||||||
|
println!("{}", json);
|
||||||
|
|
||||||
|
// @TODO Add a known correct output to test against
|
||||||
|
}
|
||||||
|
}
|
83
google-home/src/response/query.rs
Normal file
83
google-home/src/response/query.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::response::State;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Payload {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error_code: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub debug_string: Option<String>,
|
||||||
|
devices: HashMap<String, Device>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Payload {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { error_code: None, debug_string: None, devices: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_device(&mut self, id: &str, device: Device) {
|
||||||
|
self.devices.insert(id.into(), device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
pub enum Status {
|
||||||
|
Success,
|
||||||
|
Offline,
|
||||||
|
Exceptions,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Device {
|
||||||
|
online: bool,
|
||||||
|
status: Status,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error_code: Option<String>,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub state: Option<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
pub fn new(online: bool, status: Status) -> Self {
|
||||||
|
Self { online, status, error_code: None, state: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use super::*;
|
||||||
|
use crate::response::{Response, ResponsePayload, State};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() {
|
||||||
|
let mut query_resp = Payload::new();
|
||||||
|
|
||||||
|
let state = State::default().on(true);
|
||||||
|
let mut device = Device::new(true, Status::Success);
|
||||||
|
device.state = Some(state);
|
||||||
|
query_resp.add_device("123", device);
|
||||||
|
|
||||||
|
let state = State::default().on(false);
|
||||||
|
let mut device = Device::new(true, Status::Success);
|
||||||
|
device.state = Some(state);
|
||||||
|
query_resp.add_device("456", device);
|
||||||
|
|
||||||
|
let resp = Response::new(Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap(), ResponsePayload::Query(query_resp));
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&resp).unwrap();
|
||||||
|
|
||||||
|
println!("{}", json);
|
||||||
|
|
||||||
|
// @TODO Add a known correct output to test against
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,17 +6,17 @@ use crate::traits::Trait;
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Payload {
|
pub struct Payload {
|
||||||
user_agent_id: String,
|
agent_user_id: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
error_code: Option<String>,
|
pub error_code: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
debug_string: Option<String>,
|
pub debug_string: Option<String>,
|
||||||
devices: Vec<Device>,
|
pub devices: Vec<Device>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Payload {
|
impl Payload {
|
||||||
pub fn new(user_agent_id: &str) -> Self {
|
pub fn new(agent_user_id: &str) -> Self {
|
||||||
Self { user_agent_id: user_agent_id.into(), error_code: None, debug_string: None, devices: Vec::new() }
|
Self { agent_user_id: agent_user_id.into(), error_code: None, debug_string: None, devices: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_device(&mut self, device: Device) {
|
pub fn add_device(&mut self, device: Device) {
|
||||||
|
@ -27,9 +27,9 @@ impl Payload {
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub id: String,
|
id: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub device_type: Type,
|
device_type: Type,
|
||||||
pub traits: Vec<Trait>,
|
pub traits: Vec<Trait>,
|
||||||
pub name: DeviceName,
|
pub name: DeviceName,
|
||||||
pub will_report_state: bool,
|
pub will_report_state: bool,
|
||||||
|
@ -37,6 +37,10 @@ pub struct Device {
|
||||||
pub notification_supported_by_agent: Option<bool>,
|
pub notification_supported_by_agent: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "String::is_empty")]
|
#[serde(skip_serializing_if = "String::is_empty")]
|
||||||
pub room_hint: String,
|
pub room_hint: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub device_info: Option<DeviceInfo>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attributes: Option<Attributes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
|
@ -46,12 +50,14 @@ impl Device {
|
||||||
device_type,
|
device_type,
|
||||||
traits: Vec::new(),
|
traits: Vec::new(),
|
||||||
name: DeviceName {
|
name: DeviceName {
|
||||||
default_name: Vec::new(),
|
default_names: Vec::new(),
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
nicknames: Vec::new() },
|
nicknames: Vec::new() },
|
||||||
will_report_state: true,
|
will_report_state: false,
|
||||||
notification_supported_by_agent: None,
|
notification_supported_by_agent: None,
|
||||||
room_hint: "".into(),
|
room_hint: "".into(),
|
||||||
|
device_info: None,
|
||||||
|
attributes: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,8 +66,66 @@ impl Device {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeviceName {
|
pub struct DeviceName {
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub default_name: Vec<String>,
|
pub default_names: Vec<String>,
|
||||||
pub name: String,
|
name: String,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub nicknames: Vec<String>,
|
pub nicknames: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DeviceInfo {
|
||||||
|
#[serde(skip_serializing_if = "String::is_empty")]
|
||||||
|
pub manufacturer: String,
|
||||||
|
#[serde(skip_serializing_if = "String::is_empty")]
|
||||||
|
pub model: String,
|
||||||
|
#[serde(skip_serializing_if = "String::is_empty")]
|
||||||
|
pub hw_version: String,
|
||||||
|
#[serde(skip_serializing_if = "String::is_empty")]
|
||||||
|
pub sw_version: String,
|
||||||
|
// attributes
|
||||||
|
// customData
|
||||||
|
// otherDeviceIds
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Attributes {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use super::*;
|
||||||
|
use crate::{response::{Response, ResponsePayload}, types::Type, traits::Trait};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() {
|
||||||
|
let mut sync_resp = Payload::new("1836.15267389");
|
||||||
|
|
||||||
|
let mut device = Device::new("123", "Night light", Type::Kettle);
|
||||||
|
device.traits.push(Trait::OnOff);
|
||||||
|
device.name.default_names.push("My Outlet 1234".to_string());
|
||||||
|
device.name.nicknames.push("wall plug".to_string());
|
||||||
|
|
||||||
|
device.room_hint = "kitchen".into();
|
||||||
|
device.device_info = Some(DeviceInfo {
|
||||||
|
manufacturer: "lights-out-inc".to_string(),
|
||||||
|
model: "hs1234".to_string(),
|
||||||
|
hw_version: "3.2".to_string(),
|
||||||
|
sw_version: "11.4".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
sync_resp.add_device(device);
|
||||||
|
|
||||||
|
let resp = Response::new(Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap(), ResponsePayload::Sync(sync_resp));
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&resp).unwrap();
|
||||||
|
|
||||||
|
println!("{}", json);
|
||||||
|
|
||||||
|
assert_eq!(json, r#"{"requestId":"ff36a3cc-ec34-11e6-b1a0-64510650abcf","payload":{"agentUserId":"1836.15267389","devices":[{"id":"123","type":"action.devices.types.KETTLE","traits":["action.devices.traits.OnOff"],"name":{"defaultNames":["My Outlet 1234"],"name":"Night light","nicknames":["wall plug"]},"willReportState":false,"roomHint":"kitchen","deviceInfo":{"manufacturer":"lights-out-inc","model":"hs1234","hwVersion":"3.2","swVersion":"11.4"}}]}}"#)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user