use core::sync::atomic::Ordering;
use embassy_futures::join::join;
use embassy_futures::select::{Either, select};
use embassy_time::{Duration, Instant, Timer, with_timeout};
use trouble_host::prelude::*;
use super::ble_server::Server;
use crate::ble::SLEEPING_STATE;
use crate::input_device::battery::{BATTERY_UPDATE, BatteryState};
use crate::keyboard::LAST_KEY_TIMESTAMP;
#[gatt_service(uuid = service::BATTERY)]
pub(crate) struct BatteryService {
#[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])]
#[characteristic(uuid = characteristic::BATTERY_LEVEL, read, notify)]
pub(crate) level: u8,
}
pub(crate) struct BleBatteryServer<'stack, 'server, 'conn, P: PacketPool> {
pub(crate) battery_level: Characteristic<u8>,
pub(crate) conn: &'conn GattConnection<'stack, 'server, P>,
}
impl<'stack, 'server, 'conn, P: PacketPool> BleBatteryServer<'stack, 'server, 'conn, P> {
pub(crate) fn new(server: &Server, conn: &'conn GattConnection<'stack, 'server, P>) -> Self {
Self {
battery_level: server.battery_service.level,
conn,
}
}
}
impl<P: PacketPool> BleBatteryServer<'_, '_, '_, P> {
pub(crate) async fn run(&mut self) {
Timer::after_secs(2).await;
let first_report = async {
loop {
if let BatteryState::Normal(level) = BATTERY_UPDATE.wait().await {
if let Err(e) = self.battery_level.notify(self.conn, &level).await {
error!("Failed to notify battery level: {:?}", e);
} else {
return;
}
}
embassy_time::Timer::after_secs(2).await;
}
};
with_timeout(Duration::from_secs(30), first_report).await.ok();
loop {
let battery_state = self.wait_until_battery_state_available().await;
if let BatteryState::Normal(level) = BATTERY_UPDATE.try_take().unwrap_or(battery_state)
&& let Err(e) = self.battery_level.notify(self.conn, &level).await
{
error!("Failed to notify battery level: {:?}", e);
}
}
}
async fn wait_until_battery_state_available(&mut self) -> BatteryState {
loop {
let timeout = async {
loop {
embassy_time::Timer::after_secs(1800).await;
if !SLEEPING_STATE.load(Ordering::Acquire) {
break;
}
}
};
let (battery_state, last_press) =
join(BATTERY_UPDATE.wait(), select(timeout, LAST_KEY_TIMESTAMP.wait())).await;
let last_press = match last_press {
Either::First(_) => Instant::now().as_secs() as u32,
Either::Second(last_press) => last_press,
};
let current_time = Instant::now().as_secs() as u32;
if current_time.saturating_sub(last_press) < 60 {
return battery_state;
}
}
}
}