use std::thread; use tokio::select; use tokio::sync::broadcast; use tokio::task::LocalSet; use deckster_mode::shared::handler_communication::{HandlerCommand, HandlerEvent, HandlerInitializationError, InitialHandlerMessage, KeyEvent}; use deckster_mode::shared::path::KeyPath; use deckster_mode::{send_command, DecksterHandler}; use crate::config::{GlobalConfig, KeyConfig, KeyMode}; use crate::ha_client::{HaClient, StateUpdate}; pub struct Handler { events_sender: broadcast::Sender, } impl Handler { pub fn new(data: InitialHandlerMessage) -> Result { let events_sender = broadcast::Sender::::new(5); let mut subscribed_entity_ids = Vec::new(); for c in data.key_configs.values() { subscribed_entity_ids.push(c.mode.state_entity_id().clone()) } thread::spawn({ let events_sender = events_sender.clone(); move || { let runtime = tokio::runtime::Builder::new_current_thread().enable_time().enable_io().build().unwrap(); let task_set = LocalSet::new(); let ha_client = task_set.block_on( &runtime, HaClient::new(data.global_config.base_url, data.global_config.token, subscribed_entity_ids), ); for (path, config) in data.key_configs { task_set.spawn_local(manage_key(events_sender.subscribe(), ha_client.clone(), path, config)); } runtime.block_on(task_set) } }); Ok(Handler { events_sender }) } } impl DecksterHandler for Handler { fn handle(&mut self, event: HandlerEvent) { // No receivers being available can be ignored. _ = self.events_sender.send(event); } } async fn manage_key(mut events: broadcast::Receiver, ha_client: HaClient, path: KeyPath, config: KeyConfig) { let state_entity_id = config.mode.state_entity_id(); if let Some(state) = &config.disconnected_state { send_command(HandlerCommand::SetKeyStyle { path: path.clone(), value: config.style.get(state).cloned(), }) } let mut state_updates = ha_client.subscribe_to_state_updates(); loop { select! { Ok(update) = state_updates.recv() => { match update { StateUpdate::Disconnected => { if let Some(state) = &config.disconnected_state { send_command(HandlerCommand::SetKeyStyle { path: path.clone(), value: config.style.get(state).cloned() }) } } StateUpdate::Actual(update) => { if &update.entity_id == state_entity_id { send_command(HandlerCommand::SetKeyStyle { path: path.clone(), value: config.style.get(&update.state).cloned() }) } } } } Ok(HandlerEvent::Key { path: p, event }) = events.recv() => { if p != path { continue } if let KeyEvent::Press = event { match &config.mode { KeyMode::Toggle { entity_id } => { ha_client.toggle_entity(entity_id).await; } KeyMode::Button { button_entity_id, .. } => { ha_client.press_button_entity(button_entity_id).await; } } } } } } }