hashmapping
This commit is contained in:
parent
015563947e
commit
65c976c851
|
@ -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)?;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
|
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()
|
.iter()
|
||||||
.flat_map(|service| {
|
.find(|(_, v)| v.characteristic_type == CharacteristicType::Name)
|
||||||
service
|
.map(|(k, v)| (*k, v.value.clone()))
|
||||||
.characteristics
|
{
|
||||||
.iter()
|
service.name = Some(name);
|
||||||
.map(|c| format!("{}.{}", self.id, c.id))
|
service.characteristics.remove(&name_key);
|
||||||
})
|
}
|
||||||
.collect()
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct AccessoryService {
|
pub enum CharacteristicPermissions {
|
||||||
#[serde(rename = "iid")]
|
#[serde(rename = "pr")]
|
||||||
pub id: usize,
|
PairedRead,
|
||||||
#[serde(rename = "type", deserialize_with = "deserialize_service_type")]
|
#[serde(rename = "pw")]
|
||||||
service_type: ServiceType,
|
PairedWrite,
|
||||||
pub primary: bool,
|
#[serde(rename = "ev")]
|
||||||
pub hidden: bool,
|
Events,
|
||||||
pub characteristics: Vec<ServiceCharacteristic>,
|
#[serde(rename = "aa")]
|
||||||
|
AdditionalAuthorization,
|
||||||
|
#[serde(rename = "tw")]
|
||||||
|
TimedWrite,
|
||||||
|
#[serde(rename = "hd")]
|
||||||
|
Hidden,
|
||||||
|
#[serde(rename = "wr")]
|
||||||
|
WriteResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
fn deserialize_service_characteristics<'de, D>(
|
||||||
pub struct ServiceCharacteristic {
|
deserializer: D,
|
||||||
#[serde(rename = "iid")]
|
) -> Result<HashMap<usize, ServiceCharacteristic>, D::Error>
|
||||||
pub id: usize,
|
where
|
||||||
#[serde(rename = "type", deserialize_with = "deserialize_characteristic_type")]
|
D: Deserializer<'de>,
|
||||||
pub characteristic_type: CharacteristicType,
|
{
|
||||||
pub perms: Vec<String>,
|
#[derive(Deserialize)]
|
||||||
#[serde(flatten)]
|
struct ServiceCharacteristicInner {
|
||||||
pub value: Option<Data>,
|
#[serde(rename = "iid")]
|
||||||
pub ev: Option<bool>,
|
iid: usize,
|
||||||
pub enc: Option<bool>,
|
#[serde(rename = "type", deserialize_with = "deserialize_characteristic_type")]
|
||||||
#[serde(rename = "maxDataLen")]
|
characteristic_type: CharacteristicType,
|
||||||
pub max_data_len: Option<usize>,
|
perms: Vec<CharacteristicPermissions>,
|
||||||
#[serde(rename = "minValue")]
|
#[serde(flatten)]
|
||||||
pub min_value: Option<f64>,
|
value: Option<Data>,
|
||||||
#[serde(rename = "maxValue")]
|
#[serde(rename = "ev")]
|
||||||
pub max_value: Option<f64>,
|
event_notifications_enabled: Option<bool>,
|
||||||
#[serde(rename = "minStep")]
|
enc: Option<bool>,
|
||||||
pub min_step: Option<f64>,
|
#[serde(rename = "maxDataLen")]
|
||||||
pub unit: Option<String>,
|
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,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue