From 6627174c6fbcf53dedfcac0be4fe6c761b064d07 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Sun, 11 Dec 2022 23:59:29 +0100 Subject: [PATCH] Started working on google home implementation --- Cargo.lock | 20 +++++ Cargo.toml | 1 + google-home/.gitignore | 1 + google-home/Cargo.lock | 129 +++++++++++++++++++++++++++++++ google-home/Cargo.toml | 17 ++++ google-home/src/lib.rs | 4 + google-home/src/request.rs | 43 +++++++++++ google-home/src/response.rs | 44 +++++++++++ google-home/src/response/sync.rs | 67 ++++++++++++++++ google-home/src/traits.rs | 9 +++ google-home/src/types.rs | 11 +++ 11 files changed, 346 insertions(+) create mode 100644 google-home/.gitignore create mode 100644 google-home/Cargo.lock create mode 100644 google-home/Cargo.toml create mode 100644 google-home/src/lib.rs create mode 100644 google-home/src/request.rs create mode 100644 google-home/src/response.rs create mode 100644 google-home/src/response/sync.rs create mode 100644 google-home/src/traits.rs create mode 100644 google-home/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index faecb1b..d6a4adf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,7 @@ name = "automation" version = "0.1.0" dependencies = [ "dotenv", + "google-home", "rumqttc", "serde", "serde_json", @@ -191,6 +192,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "google-home" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "uuid", +] + [[package]] name = "itoa" version = "1.0.4" @@ -609,6 +619,16 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "uuid" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index c1eca27..53ef255 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ rumqttc = "0.18" serde = { version ="1.0.149", features = ["derive"] } serde_json = "1.0.89" dotenv = "0.15.0" +google-home = {path = "./google-home"} diff --git a/google-home/.gitignore b/google-home/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/google-home/.gitignore @@ -0,0 +1 @@ +/target diff --git a/google-home/Cargo.lock b/google-home/Cargo.lock new file mode 100644 index 0000000..4c283ad --- /dev/null +++ b/google-home/Cargo.lock @@ -0,0 +1,129 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "google-home" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "uuid", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "serde" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "uuid" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/google-home/Cargo.toml b/google-home/Cargo.toml new file mode 100644 index 0000000..0908707 --- /dev/null +++ b/google-home/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "google-home" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version ="1.0.149", features = ["derive"] } +serde_json = "1.0.89" + +[dependencies.uuid] +version = "1.2.2" +features = [ + "v4", + "serde", +] diff --git a/google-home/src/lib.rs b/google-home/src/lib.rs new file mode 100644 index 0000000..948cafd --- /dev/null +++ b/google-home/src/lib.rs @@ -0,0 +1,4 @@ +pub mod request; +pub mod response; +pub mod types; +pub mod traits; diff --git a/google-home/src/request.rs b/google-home/src/request.rs new file mode 100644 index 0000000..32d955a --- /dev/null +++ b/google-home/src/request.rs @@ -0,0 +1,43 @@ +use serde::Deserialize; +use uuid::Uuid; + +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(tag = "intent")] +enum Intent { + #[serde(rename = "action.devices.SYNC")] + Sync, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Request { + request_id: Uuid, + inputs: Vec, +} + +#[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); + } +} + diff --git a/google-home/src/response.rs b/google-home/src/response.rs new file mode 100644 index 0000000..4b95f9c --- /dev/null +++ b/google-home/src/response.rs @@ -0,0 +1,44 @@ +pub mod sync; + +use serde::Serialize; +use uuid::Uuid; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Response { + request_id: Uuid, + payload: ResponsePayload, +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum ResponsePayload { + Sync(sync::Payload) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use crate::{response::sync::Device, types::Type, traits::Trait}; + + use super::*; + + #[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); + } +} diff --git a/google-home/src/response/sync.rs b/google-home/src/response/sync.rs new file mode 100644 index 0000000..2d059a0 --- /dev/null +++ b/google-home/src/response/sync.rs @@ -0,0 +1,67 @@ +use serde::Serialize; + +use crate::types::Type; +use crate::traits::Trait; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Payload { + user_agent_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + error_code: Option, + #[serde(skip_serializing_if = "Option::is_none")] + debug_string: Option, + devices: Vec, +} + +impl Payload { + pub fn new(user_agent_id: &str) -> Self { + Self { user_agent_id: user_agent_id.into(), error_code: None, debug_string: None, devices: Vec::new() } + } + + pub fn add_device(&mut self, device: Device) { + self.devices.push(device); + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Device { + pub id: String, + #[serde(rename = "type")] + pub device_type: Type, + pub traits: Vec, + pub name: DeviceName, + pub will_report_state: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub notification_supported_by_agent: Option, + #[serde(skip_serializing_if = "String::is_empty")] + pub room_hint: String, +} + +impl Device { + pub fn new(id: &str, name: &str, device_type: Type) -> Self { + Self { + id: id.into(), + device_type, + traits: Vec::new(), + name: DeviceName { + default_name: Vec::new(), + name: name.into(), + nicknames: Vec::new() }, + will_report_state: true, + notification_supported_by_agent: None, + room_hint: "".into(), + } + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DeviceName { + #[serde(skip_serializing_if = "Vec::is_empty")] + pub default_name: Vec, + pub name: String, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub nicknames: Vec, +} diff --git a/google-home/src/traits.rs b/google-home/src/traits.rs new file mode 100644 index 0000000..3cdcb4d --- /dev/null +++ b/google-home/src/traits.rs @@ -0,0 +1,9 @@ +use serde::Serialize; + +#[derive(Debug, Serialize)] +pub enum Trait { + #[serde(rename = "action.devices.traits.OnOff")] + OnOff, + #[serde(rename = "action.devices.traits.Scene")] + Scene, +} diff --git a/google-home/src/types.rs b/google-home/src/types.rs new file mode 100644 index 0000000..5ab4a93 --- /dev/null +++ b/google-home/src/types.rs @@ -0,0 +1,11 @@ +use serde::Serialize; + +#[derive(Debug, Serialize)] +pub enum Type { + #[serde(rename = "action.devices.types.KETTLE")] + Kettle, + #[serde(rename = "action.devices.types.OUTLET")] + Outlet, + #[serde(rename = "action.devices.types.SCENE")] + Scene, +}