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

View file

@ -11,7 +11,7 @@ where
CharacteristicTypeInner::deserialize(deserializer).map(|v| v.into())
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum CharacteristicType {
AccessControlLevel,
@ -1228,7 +1228,7 @@ where
ServiceTypeInner::deserialize(deserializer).map(|v| v.into())
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub enum ServiceType {
AccessControl,
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};
pub use characteristics::{CharacteristicType, ServiceType};
mod characteristics;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone)]
pub struct DevicePairingData {
#[serde(rename = "AccessoryPairingID")]
pub accessory_pairing_id: String,
@ -23,88 +25,227 @@ pub struct DevicePairingData {
pub accessory_port: usize,
#[serde(rename = "Connection")]
pub connection: ConnectionType,
#[serde(rename = "accessories")]
pub accessories: Option<Vec<Accessory>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Accessory {
#[serde(rename = "aid")]
pub id: usize,
pub services: Vec<AccessoryService>,
#[serde(default, deserialize_with = "deserialize_accessories")]
pub accessories: HashMap<usize, HashMap<usize, AccessoryService>>,
}
#[derive(Deserialize)]
pub struct Accessories {
pub accessories: Vec<Accessory>,
#[serde(default, deserialize_with = "deserialize_accessories")]
pub accessories: HashMap<usize, HashMap<usize, AccessoryService>>,
}
impl Accessory {
pub fn get_service_ids(&self) -> Vec<String> {
self.services
.iter()
.flat_map(|service| {
service
fn deserialize_accessories<'de, D>(
deserializer: D,
) -> Result<HashMap<usize, HashMap<usize, AccessoryService>>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Accessory {
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
})
})
}
#[derive(Debug, Clone)]
pub struct AccessoryService {
pub name: Option<String>,
pub service_type: ServiceType,
pub primary: bool,
pub hidden: bool,
pub characteristics: HashMap<usize, ServiceCharacteristic>,
}
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()
.map(|c| format!("{}.{}", self.id, c.id))
})
.collect()
.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 characteristic_type: CharacteristicType,
pub perms: Vec<CharacteristicPermissions>,
pub value: Option<Data>,
pub event_notifications_enabled: Option<bool>,
pub enc: Option<bool>,
pub max_data_len: Option<usize>,
pub min_value: Option<f64>,
pub max_value: Option<f64>,
pub min_step: Option<f64>,
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(Serialize, Deserialize, Debug, Clone)]
pub struct AccessoryService {
#[serde(rename = "iid")]
pub id: usize,
#[serde(rename = "type", deserialize_with = "deserialize_service_type")]
service_type: ServiceType,
pub primary: bool,
pub hidden: bool,
pub characteristics: Vec<ServiceCharacteristic>,
#[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,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ServiceCharacteristic {
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")]
pub id: usize,
iid: usize,
#[serde(rename = "type", deserialize_with = "deserialize_characteristic_type")]
pub characteristic_type: CharacteristicType,
pub perms: Vec<String>,
characteristic_type: CharacteristicType,
perms: Vec<CharacteristicPermissions>,
#[serde(flatten)]
pub value: Option<Data>,
pub ev: Option<bool>,
pub enc: Option<bool>,
value: Option<Data>,
#[serde(rename = "ev")]
event_notifications_enabled: Option<bool>,
enc: Option<bool>,
#[serde(rename = "maxDataLen")]
pub max_data_len: Option<usize>,
max_data_len: Option<usize>,
#[serde(rename = "minValue")]
pub min_value: Option<f64>,
min_value: Option<f64>,
#[serde(rename = "maxValue")]
pub max_value: Option<f64>,
max_value: Option<f64>,
#[serde(rename = "minStep")]
pub min_step: Option<f64>,
pub unit: Option<String>,
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)]
pub struct Characteristics {
pub characteristics: Vec<ServiceCharacteristic>,
#[serde(deserialize_with = "deserialize_service_characteristics")]
pub characteristics: HashMap<usize, ServiceCharacteristic>,
}
#[allow(clippy::enum_variant_names)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "format", content = "value")]
#[serde(tag = "format", content = "value", rename_all = "lowercase")]
pub enum Data {
#[serde(rename = "string")]
String(String),
#[serde(rename = "bool")]
Bool(bool),
#[serde(rename = "data")]
Data(String),
#[serde(rename = "uint32")]
Uint(u32),
#[serde(rename = "float")]
Uint8(u8),
Uint16(u16),
Uint32(u32),
Uint64(u64),
Int(i32),
Float(f64),
String(String),
Tlv8(String),
Data(String),
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
@ -116,3 +257,13 @@ pub enum ConnectionType {
#[serde(rename = "ADDITIONAL_PAIRING")]
AdditionalPairing,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum Unit {
Celsius,
Percentage,
ArcDegrees,
Lux,
Seconds,
}