websocket.rs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. use awc::{Client, BoxedSocket, ws::{Frame, Message, Codec}};
  2. use actix_codec::Framed;
  3. use futures_util::{SinkExt, StreamExt};
  4. use serde_json::{Value, from_str, json};
  5. use tokio::sync::mpsc;
  6. pub async fn connect_to_ha(
  7. ip: String,
  8. token: String
  9. ) -> Result<Framed<BoxedSocket, Codec>, Box<dyn std::error::Error>> {
  10. let client = Client::new();
  11. let (_resp, mut connection) = client
  12. .ws(format!("ws://{}:8123/api/websocket", ip))
  13. .connect()
  14. .await?;
  15. while let Some(Ok(msg)) = connection.next().await {
  16. if let Frame::Text(bytes) = msg {
  17. let text = String::from_utf8(bytes.to_vec())?;
  18. let parsed: Value = from_str(&text)?;
  19. if parsed["type"] == "auth_required" {
  20. let auth_msg = json!({
  21. "type": "auth",
  22. "access_token": token
  23. });
  24. connection.send(Message::Text(auth_msg.to_string().into())).await?;
  25. }
  26. if parsed["type"] == "auth_ok" {
  27. /*let areas_msg = json!({"type": "config/area_registry/list", "id": message_id});
  28. connection.send(Message::Text(areas_msg.to_string().into())).await?;
  29. message_id += 1;*/
  30. /*let devices_msg = json!({"type": "config/device_registry/list", "id": message_id});
  31. connection.send(Message::Text(devices_msg.to_string().into())).await?;
  32. message_id += 1;*/
  33. /*let entities_msg = json!({"type": "config/entity_registry/list", "id": message_id});
  34. connection.send(Message::Text(entities_msg.to_string().into())).await?;
  35. message_id += 1;*/
  36. return Ok(connection);
  37. }
  38. if parsed["type"] == "auth_invalid" {
  39. return Err("HA authentication.failed".into());
  40. }
  41. }
  42. }
  43. Err("Websocket closed before authentication completed".into())
  44. }
  45. pub async fn listen(
  46. ip: String,
  47. token: String,
  48. mut outbound_rx: mpsc::UnboundedReceiver<Message>
  49. ) {
  50. loop {
  51. let mut connection = match connect_to_ha(ip.clone(), token.clone()).await {
  52. Ok(c) => c,
  53. Err(e) => {
  54. eprintln!("Failed to connect to HA: {e}. Retrying in 10s...");
  55. tokio::time::sleep(std::time::Duration::from_secs(10)).await;
  56. continue;
  57. }
  58. };
  59. loop {
  60. tokio::select! {
  61. msg = connection.next() => {
  62. match msg {
  63. Some(Ok(Frame::Text(bytes))) => {
  64. let text = match String::from_utf8(bytes.to_vec()) {
  65. Ok(t) => t,
  66. Err(_) => continue
  67. };
  68. let parsed: Value = match from_str(&text) {
  69. Ok(v) => v,
  70. Err(_) => continue
  71. };
  72. if parsed["type"] == "result" {
  73. println!("result");
  74. println!("{:#?}", parsed);
  75. }
  76. }
  77. Some(Ok(Frame::Ping(bytes))) => {
  78. let _ = connection.send(Message::Pong(bytes)).await;
  79. }
  80. Some(Ok(Frame::Close(reason))) => {
  81. println!("HA closed connection: {:?}", reason);
  82. break;
  83. }
  84. Some(Err(e)) => {
  85. eprintln!("Websocket error: {e}");
  86. break;
  87. }
  88. None => break,
  89. _ => {}
  90. }
  91. }
  92. Some(out_msg) = outbound_rx.recv() => {
  93. if let Err(e) = connection.send(out_msg).await {
  94. eprintln!("Failed to send outbound message: {e}");
  95. }
  96. }
  97. }
  98. }
  99. println!("HA connection closed, reconnecting in 10s...");
  100. tokio::time::sleep(std::time::Duration::from_secs(10)).await;
  101. }
  102. }