use std::{fs::File, io::Write, time::Duration};
use iced::futures::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use tokio::{net::TcpStream, sync::mpsc};
use tokio_tungstenite::{tungstenite::Message, MaybeTlsStream, WebSocketStream};
use crate::config::HomeAssistantConfig;
type SocketStream = WebSocketStream<MaybeTlsStream<TcpStream>>;
pub enum ClientMessage {
}
pub async fn create(config: HomeAssistantConfig) -> mpsc::Receiver<ClientMessage> {
let (_send, recv) = mpsc::channel(10);
let uri = format!("ws://{}/api/websocket", config.uri);
let (mut connection, _response) = tokio_tungstenite::connect_async(&uri).await.unwrap();
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(10));
loop {
tokio::select! {
Some(message) = connection.next() => {
let message = message.unwrap();
eprintln!("recv: {message:?}");
#[allow(clippy::match_same_arms)]
match message {
Message::Pong(ts) => {
let ts = i128::from_be_bytes(ts.try_into().unwrap());
let ts = OffsetDateTime::from_unix_timestamp_nanos(ts).unwrap();
eprintln!("rtt: {}", OffsetDateTime::now_utc() - ts);
}
Message::Text(payload) => {
handle_hass_response(&config, serde_json::from_str(&payload).unwrap(), &mut connection).await;
}
Message::Close(_) => {
}
_ => {}
}
}
_ = interval.tick() => {
connection.send(Message::Ping(OffsetDateTime::now_utc().unix_timestamp_nanos().to_be_bytes().to_vec())).await.unwrap();
}
}
}
});
recv
}
async fn handle_hass_response(
config: &HomeAssistantConfig,
v: HassResponse,
socket: &mut SocketStream,
) {
#[allow(clippy::match_same_arms)]
match v {
HassResponse::AuthRequired => {
socket
.send(
HassRequest::Auth {
access_token: config.token.clone(),
}
.to_request(),
)
.await
.unwrap();
}
HassResponse::AuthOk => {
}
HassResponse::AuthInvalid => {}
HassResponse::Result(value) => {
File::create("test")
.unwrap()
.write_all(&serde_json::to_vec(&value).unwrap())
.unwrap();
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "result")]
#[allow(clippy::enum_variant_names)]
enum HassResponse {
AuthRequired,
AuthOk,
AuthInvalid,
Result(serde_json::Value),
}
#[derive(Serialize)]
#[serde(rename_all = "snake_case", tag = "type")]
enum HassRequest {
Auth {
access_token: String,
},
GetStates {
id: u32,
},
#[serde(rename = "config/area_registry/list")]
AreaRegistry {
id: u32,
},
#[serde(rename = "config/entity_registry/list")]
EntityRegistry {
id: u32,
},
#[serde(rename = "config/device_registry/list")]
DeviceRegistry {
id: u32,
},
}
impl HassRequest {
pub fn to_request(&self) -> Message {
Message::text(serde_json::to_string(&self).unwrap())
}
}