hashmapping

This commit is contained in:
Alex Janka 2024-02-23 10:06:32 +11:00
parent 015563947e
commit 65c976c851
3 changed files with 222 additions and 66 deletions

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use chacha20poly1305::{ use chacha20poly1305::{
aead::generic_array::GenericArray, AeadInPlace, ChaCha20Poly1305, KeyInit, Nonce, aead::generic_array::GenericArray, AeadInPlace, ChaCha20Poly1305, KeyInit, Nonce,
}; };
@ -8,7 +10,7 @@ use tokio::{
}; };
use crate::{ use crate::{
pairing_data::{Accessory, ServiceCharacteristic}, pairing_data::{AccessoryService, ServiceCharacteristic},
tlv8::TlvEncode, tlv8::TlvEncode,
HomekitError, HomekitError,
}; };
@ -81,7 +83,9 @@ impl AccessorySocket {
.await .await
} }
pub async fn get_accessories(&mut self) -> Result<Vec<Accessory>, HomekitError> { pub async fn get_accessories(
&mut self,
) -> Result<HashMap<usize, HashMap<usize, AccessoryService>>, HomekitError> {
let response = self.req("/accessories", Method::GET, None, &[]).await?; let response = self.req("/accessories", Method::GET, None, &[]).await?;
let accessories: crate::pairing_data::Accessories = serde_json::from_slice(&response)?; let accessories: crate::pairing_data::Accessories = serde_json::from_slice(&response)?;
Ok(accessories.accessories) Ok(accessories.accessories)
@ -90,11 +94,12 @@ impl AccessorySocket {
pub async fn get_characteristics( pub async fn get_characteristics(
&mut self, &mut self,
characteristics: &[String], characteristics: &[String],
) -> Result<Vec<ServiceCharacteristic>, HomekitError> { ) -> Result<HashMap<usize, ServiceCharacteristic>, HomekitError> {
let characteristic_request = characteristics.join(","); let characteristic_request = characteristics.join(",");
let url = let url =
format!("/characteristics?id={characteristic_request}&meta=1&perms=1&type=1&ev=1"); format!("/characteristics?id={characteristic_request}&meta=1&perms=1&type=1&ev=1");
let response = self.req(&url, Method::GET, None, &[]).await?; let response = self.req(&url, Method::GET, None, &[]).await?;
log::info!("got: {}", std::str::from_utf8(&response).unwrap());
let characteristics: crate::pairing_data::Characteristics = let characteristics: crate::pairing_data::Characteristics =
serde_json::from_slice(&response)?; serde_json::from_slice(&response)?;

View file

@ -11,7 +11,7 @@ where
CharacteristicTypeInner::deserialize(deserializer).map(|v| v.into()) CharacteristicTypeInner::deserialize(deserializer).map(|v| v.into())
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum CharacteristicType { pub enum CharacteristicType {
AccessControlLevel, AccessControlLevel,
@ -1228,7 +1228,7 @@ where
ServiceTypeInner::deserialize(deserializer).map(|v| v.into()) ServiceTypeInner::deserialize(deserializer).map(|v| v.into())
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub enum ServiceType { pub enum ServiceType {
AccessControl, AccessControl,
AccessoryInformation, AccessoryInformation,

View file

@ -1,11 +1,13 @@
use serde::{Deserialize, Serialize}; use std::collections::HashMap;
use serde::{Deserialize, Deserializer, Serialize};
use characteristics::{deserialize_characteristic_type, deserialize_service_type}; use characteristics::{deserialize_characteristic_type, deserialize_service_type};
pub use characteristics::{CharacteristicType, ServiceType}; pub use characteristics::{CharacteristicType, ServiceType};
mod characteristics; mod characteristics;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
pub struct DevicePairingData { pub struct DevicePairingData {
#[serde(rename = "AccessoryPairingID")] #[serde(rename = "AccessoryPairingID")]
pub accessory_pairing_id: String, pub accessory_pairing_id: String,
@ -23,88 +25,227 @@ pub struct DevicePairingData {
pub accessory_port: usize, pub accessory_port: usize,
#[serde(rename = "Connection")] #[serde(rename = "Connection")]
pub connection: ConnectionType, pub connection: ConnectionType,
#[serde(rename = "accessories")] #[serde(default, deserialize_with = "deserialize_accessories")]
pub accessories: Option<Vec<Accessory>>, pub accessories: HashMap<usize, HashMap<usize, AccessoryService>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Accessory {
#[serde(rename = "aid")]
pub id: usize,
pub services: Vec<AccessoryService>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct Accessories { pub struct Accessories {
pub accessories: Vec<Accessory>, #[serde(default, deserialize_with = "deserialize_accessories")]
pub accessories: HashMap<usize, HashMap<usize, AccessoryService>>,
} }
impl Accessory { fn deserialize_accessories<'de, D>(
pub fn get_service_ids(&self) -> Vec<String> { deserializer: D,
self.services ) -> Result<HashMap<usize, HashMap<usize, AccessoryService>>, D::Error>
.iter() where
.flat_map(|service| { D: Deserializer<'de>,
service {
.characteristics #[derive(Deserialize)]
.iter() struct Accessory {
.map(|c| format!("{}.{}", self.id, c.id)) aid: usize,
#[serde(deserialize_with = "deserialize_accessory_service")]
services: HashMap<usize, AccessoryService>,
}
Vec::<Accessory>::deserialize(deserializer).map(|v| {
v.into_iter().fold(HashMap::new(), |mut map, accessory| {
map.insert(accessory.aid, accessory.services);
map
})
}) })
.collect()
}
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Debug, Clone)]
pub struct AccessoryService { pub struct AccessoryService {
#[serde(rename = "iid")] pub name: Option<String>,
pub id: usize, pub service_type: ServiceType,
#[serde(rename = "type", deserialize_with = "deserialize_service_type")]
service_type: ServiceType,
pub primary: bool, pub primary: bool,
pub hidden: bool, pub hidden: bool,
pub characteristics: Vec<ServiceCharacteristic>, pub characteristics: HashMap<usize, ServiceCharacteristic>,
} }
#[derive(Serialize, Deserialize, Debug, Clone)] fn deserialize_accessory_service<'de, D>(
deserializer: D,
) -> Result<HashMap<usize, AccessoryService>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct AccessoryServiceInner {
#[serde(skip)]
name: Option<String>,
iid: usize,
#[serde(rename = "type", deserialize_with = "deserialize_service_type")]
service_type: ServiceType,
primary: bool,
hidden: bool,
#[serde(deserialize_with = "deserialize_service_characteristics")]
characteristics: HashMap<usize, ServiceCharacteristic>,
}
impl From<AccessoryServiceInner> for AccessoryService {
fn from(val: AccessoryServiceInner) -> Self {
Self {
name: val.name,
service_type: val.service_type,
primary: val.primary,
hidden: val.hidden,
characteristics: val.characteristics,
}
}
}
let mut services = Vec::<AccessoryServiceInner>::deserialize(deserializer)?;
for service in &mut services {
if let Some((name_key, Some(Data::String(name)))) = service
.characteristics
.iter()
.find(|(_, v)| v.characteristic_type == CharacteristicType::Name)
.map(|(k, v)| (*k, v.value.clone()))
{
service.name = Some(name);
service.characteristics.remove(&name_key);
}
}
Ok(services
.into_iter()
.fold(HashMap::new(), |mut map, service| {
map.insert(service.iid, service.into());
map
}))
}
#[derive(Debug, Clone)]
pub struct ServiceCharacteristic { pub struct ServiceCharacteristic {
#[serde(rename = "iid")]
pub id: usize,
#[serde(rename = "type", deserialize_with = "deserialize_characteristic_type")]
pub characteristic_type: CharacteristicType, pub characteristic_type: CharacteristicType,
pub perms: Vec<String>, pub perms: Vec<CharacteristicPermissions>,
#[serde(flatten)]
pub value: Option<Data>, pub value: Option<Data>,
pub ev: Option<bool>, pub event_notifications_enabled: Option<bool>,
pub enc: Option<bool>, pub enc: Option<bool>,
#[serde(rename = "maxDataLen")]
pub max_data_len: Option<usize>, pub max_data_len: Option<usize>,
#[serde(rename = "minValue")]
pub min_value: Option<f64>, pub min_value: Option<f64>,
#[serde(rename = "maxValue")]
pub max_value: Option<f64>, pub max_value: Option<f64>,
#[serde(rename = "minStep")]
pub min_step: Option<f64>, pub min_step: Option<f64>,
pub unit: Option<String>, pub unit: Option<Unit>,
}
impl ServiceCharacteristic {
pub fn update_from(&mut self, other: &ServiceCharacteristic) {
self.value = other.value.clone();
if other.event_notifications_enabled.is_some() {
self.event_notifications_enabled = other.event_notifications_enabled;
}
if other.enc.is_some() {
self.enc = other.enc;
}
if other.max_data_len.is_some() {
self.max_data_len = other.max_data_len;
}
if other.min_value.is_some() {
self.min_value = other.min_value;
}
if other.max_value.is_some() {
self.max_value = other.max_value;
}
if other.min_step.is_some() {
self.min_step = other.min_step;
}
if other.unit.is_some() {
self.unit = other.unit;
}
}
}
#[derive(Deserialize, Debug, Clone)]
pub enum CharacteristicPermissions {
#[serde(rename = "pr")]
PairedRead,
#[serde(rename = "pw")]
PairedWrite,
#[serde(rename = "ev")]
Events,
#[serde(rename = "aa")]
AdditionalAuthorization,
#[serde(rename = "tw")]
TimedWrite,
#[serde(rename = "hd")]
Hidden,
#[serde(rename = "wr")]
WriteResponse,
}
fn deserialize_service_characteristics<'de, D>(
deserializer: D,
) -> Result<HashMap<usize, ServiceCharacteristic>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct ServiceCharacteristicInner {
#[serde(rename = "iid")]
iid: usize,
#[serde(rename = "type", deserialize_with = "deserialize_characteristic_type")]
characteristic_type: CharacteristicType,
perms: Vec<CharacteristicPermissions>,
#[serde(flatten)]
value: Option<Data>,
#[serde(rename = "ev")]
event_notifications_enabled: Option<bool>,
enc: Option<bool>,
#[serde(rename = "maxDataLen")]
max_data_len: Option<usize>,
#[serde(rename = "minValue")]
min_value: Option<f64>,
#[serde(rename = "maxValue")]
max_value: Option<f64>,
#[serde(rename = "minStep")]
min_step: Option<f64>,
unit: Option<Unit>,
}
impl From<ServiceCharacteristicInner> for ServiceCharacteristic {
fn from(value: ServiceCharacteristicInner) -> Self {
Self {
characteristic_type: value.characteristic_type,
perms: value.perms,
value: value.value,
event_notifications_enabled: value.event_notifications_enabled,
enc: value.enc,
max_data_len: value.max_data_len,
min_value: value.min_value,
max_value: value.max_value,
min_step: value.min_step,
unit: value.unit,
}
}
}
Vec::<ServiceCharacteristicInner>::deserialize(deserializer).map(|v| {
v.into_iter()
.fold(HashMap::new(), |mut map, characteristic| {
map.insert(characteristic.iid, characteristic.into());
map
})
})
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct Characteristics { pub struct Characteristics {
pub characteristics: Vec<ServiceCharacteristic>, #[serde(deserialize_with = "deserialize_service_characteristics")]
pub characteristics: HashMap<usize, ServiceCharacteristic>,
} }
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "format", content = "value")] #[serde(tag = "format", content = "value", rename_all = "lowercase")]
pub enum Data { pub enum Data {
#[serde(rename = "string")]
String(String),
#[serde(rename = "bool")]
Bool(bool), Bool(bool),
#[serde(rename = "data")] Uint8(u8),
Data(String), Uint16(u16),
#[serde(rename = "uint32")] Uint32(u32),
Uint(u32), Uint64(u64),
#[serde(rename = "float")] Int(i32),
Float(f64), Float(f64),
String(String),
Tlv8(String),
Data(String),
} }
#[derive(Serialize, Deserialize, Debug, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
@ -116,3 +257,13 @@ pub enum ConnectionType {
#[serde(rename = "ADDITIONAL_PAIRING")] #[serde(rename = "ADDITIONAL_PAIRING")]
AdditionalPairing, AdditionalPairing,
} }
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum Unit {
Celsius,
Percentage,
ArcDegrees,
Lux,
Seconds,
}