This commit is contained in:
Dominic Grimm 2024-11-22 12:38:00 +01:00
parent 76b43a0386
commit 220a446fb6
21 changed files with 677 additions and 111 deletions

View file

@ -25,6 +25,8 @@ reqwest = { version = "0.12.9", default-features = false, features = [
"json",
"rustls-tls",
"stream",
"http2",
"charset",
] }
tinybmp = "0.6.0"
tokio = { version = "1.41.0", features = ["full"] }

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension>
<information>
<name>kdash</name>
<version>0.1.0</version>
<author>dergrimm</author>
<id>kdash</id>
</information>
<menus>
<menu type="json" dynamic="false">menu.json</menu>
</menus>
</extension>

View file

@ -1,8 +1,9 @@
#!/usr/bin/env sh
#!/bin/sh
DAEMON_PATH="/mnt/us/kdash"
DAEMON_PATH="/mnt/us/extensions/kdash"
DAEMON_ENV_FILE="${DAEMON_PATH}/kdash.env"
DAEMON_ENABLED_FILE="${DAEMON_PATH}/ENABLED"
DAEMON="./kdash_client"
DAEMONOPTS=""
@ -13,20 +14,30 @@ PIDFILE="${DAEMON_PATH}/${DAEMON}.pid"
# SCRIPTNAME="/etc/init.d/${NAME}"
case "$1" in
enable)
touch "$DAEMON_ENABLED_FILE"
;;
disable)
rm -f "$DAEMON_ENABLED_FILE"
;;
start)
printf "%-50s" "Starting $NAME..."
cd "$DAEMON_PATH" || exit
. "$DAEMON_ENV_FILE"
PID=$(
RUST_BACKTRACE=full RUST_LOG=debug $DAEMON "$DAEMONOPTS" >/dev/null 2>&1 &
echo $!
)
#echo "Saving PID" $PID " to " $PIDFILE
if [ -z "$PID" ]; then
printf "%s\n" "Fail"
if [ -e "$DAEMON_ENABLED_FILE" ]; then
printf "%-50s" "Starting $NAME..."
cd "$DAEMON_PATH" || exit
. "$DAEMON_ENV_FILE"
PID=$(
RUST_BACKTRACE=full RUST_LOG=debug $DAEMON "$DAEMONOPTS" >/dev/null 2>&1 &
echo $!
)
#echo "Saving PID" $PID " to " $PIDFILE
if [ -z "$PID" ]; then
printf "%s\n" "Fail"
else
echo "$PID" >"$PIDFILE"
printf "%s\n" "Ok"
fi
else
echo "$PID" >"$PIDFILE"
printf "%s\n" "Ok"
echo "Service not enabled. ENABLED file not found"
fi
;;
status)

View file

@ -0,0 +1,13 @@
{
"items": [
{
"name": "kdash",
"items": [
{ "name": "Enable", "priority": 0, "action": "./daemon.sh enable" },
{ "name": "Disable", "priority": 0, "action": "./daemon.sh disable" },
{ "name": "Start", "priority": 0, "action": "./daemon.sh start" },
{ "name": "Stop", "priority": 0, "action": "./daemon.sh stop" }
]
}
]
}

View file

@ -0,0 +1,4 @@
#!/bin/sh
sleep 120
/mnt/us/extensions/kdash/daemon.sh start

View file

@ -0,0 +1,3 @@
#!/bin/sh
/mnt/us/extensions/kdash/startup.sh &

View file

@ -2,6 +2,8 @@ use anyhow::{bail, Result};
use kdash_protocol::Orientation;
use url::Url;
use crate::battery::BatteryStatus;
#[derive(Debug)]
pub struct Api {
pub jwt: String,
@ -22,7 +24,7 @@ impl Api {
format!("Bearer {}", bearer)
}
pub async fn fetch_config(&self) -> Result<kdash_protocol::Config, reqwest::Error> {
pub async fn fetch_config(&self) -> Result<kdash_protocol::Config> {
let config = reqwest::Client::new()
.get(self.config_url.to_owned())
.header(
@ -37,6 +39,30 @@ impl Api {
Ok(config)
}
pub async fn fetch_config_with_post_device_state(
&self,
battery: &BatteryStatus,
) -> Result<kdash_protocol::Config> {
let config = reqwest::Client::new()
.post(self.config_url.to_owned())
.header(
reqwest::header::AUTHORIZATION,
Self::authorization_bearer(&self.jwt),
)
.json(&kdash_protocol::DeviceStatePost {
battery_charging: battery.is_charging,
battery_level: kdash_protocol::Percent::try_new(battery.percentage)?,
battery_current: battery.current,
battery_voltage: battery.voltage,
})
.send()
.await?
.json::<kdash_protocol::Config>()
.await?;
Ok(config)
}
pub async fn fetch_image<'a>(
&self,
size: (u32, u32),

View file

@ -1,4 +1,4 @@
use std::{net::IpAddr, path::PathBuf, rc::Rc, sync::Mutex};
use std::{net::IpAddr, rc::Rc, sync::Mutex};
use openlipc_dyn::Lipc;
@ -7,18 +7,11 @@ use crate::api;
pub struct State {
pub api: api::Api,
pub app_config: AppConfig,
pub config: Rc<Mutex<kdash_protocol::Config>>,
pub config: Rc<tokio::sync::Mutex<kdash_protocol::Config>>,
pub lipc: Rc<Mutex<Lipc>>,
}
pub struct AppConfig {
pub net: String,
pub router_ip: IpAddr,
pub assets_path: PathBuf,
}
impl AppConfig {
pub fn asset(&self, path: &str) -> PathBuf {
self.assets_path.join(path)
}
}

View file

@ -8,6 +8,7 @@ pub struct BatteryStatus {
pub is_charging: bool,
pub percentage: u8,
pub current: i16,
pub voltage: i16,
}
pub fn get_battery_status() -> Result<BatteryStatus> {
@ -23,10 +24,14 @@ pub fn get_battery_status() -> Result<BatteryStatus> {
let charge_current_str = utils::exec_command("gasgauge-info", &["-l"])?;
let charge_current = parse::parse_number_from_start_signed::<i16>(&charge_current_str)?;
let voltage_str = utils::exec_command("gasgauge-info", &["-v"])?;
let voltage = parse::parse_number_from_start_signed::<i16>(&voltage_str)?;
let battery = BatteryStatus {
is_charging,
percentage: charge,
current: charge_current,
voltage,
};
log::info!("Got battery status: {:?}", battery);
@ -44,13 +49,13 @@ pub fn restart_powerd_config_condition(
battery_config: &kdash_protocol::BatteryConfig,
) -> Result<()> {
if battery.is_charging
&& battery.percentage <= *battery_config.restart_powerd_threshold
&& battery.percentage <= battery_config.restart_powerd_threshold.into_inner()
&& battery.current <= 0
{
log::info!(
"Battery charge below threshold ({} <= {}): restarting powerd",
battery.percentage,
*battery_config.restart_powerd_threshold
battery_config.restart_powerd_threshold.into_inner()
);
restart_powerd()
} else {

View file

@ -1,5 +1,5 @@
use envconfig::Envconfig;
use std::{net, path::PathBuf};
use std::net;
use url::Url;
#[derive(Envconfig, Debug)]
@ -15,7 +15,4 @@ pub struct Config {
#[envconfig(from = "NET")]
pub net: String,
#[envconfig(from = "ASSETS")]
pub assets: PathBuf,
}

View file

@ -18,9 +18,9 @@ pub fn eips_clear() -> Result<()> {
utils::exec_command_discard("eips", &["-c"])
}
pub fn image_buf_to_raw<'a>(
buf: &'a image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
) -> ImageRawBE<'a, Gray8> {
pub fn image_buf_to_raw(
buf: &image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
) -> ImageRawBE<'_, Gray8> {
ImageRaw::new(buf.as_raw(), buf.dimensions().0)
}
@ -186,10 +186,7 @@ impl DrawTarget for FramebufferDisplay {
impl OriginDimensions for FramebufferDisplay {
fn size(&self) -> Size {
Size::new(
self.fb_orientation.virtual_x as u32,
self.fb_orientation.virtual_y as u32,
)
Size::new(self.fb_orientation.virtual_x, self.fb_orientation.virtual_y)
}
}

View file

@ -19,18 +19,18 @@ pub async fn run_once(
state: &app::State,
display: &mut fb::FramebufferDisplay,
) -> Result<Option<kdash_protocol::Config>> {
let config = state.config.lock().unwrap();
let config = state.config.lock().await;
utils::set_cpu_powersaving()?;
utils::prevent_screensaver(&state.lipc.lock().unwrap())?;
let battery = battery::get_battery_status()?;
battery::restart_powerd_config_condition(&battery, &config.battery)?;
if battery.percentage <= *config.battery.low {
if battery.percentage <= config.battery.low.into_inner() {
log::info!(
"Battery low: {}% <= {}%",
battery.percentage,
*config.battery.low
config.battery.low.into_inner()
);
Image::new(&*assets::ERROR_BATTERY_LOW_IMAGE, Point::zero())
@ -66,7 +66,11 @@ pub async fn run_once(
log::info!("Fetching config");
let fetch_succ = match state.api.fetch_config().await {
let fetch_succ = match state
.api
.fetch_config_with_post_device_state(&battery)
.await
{
Ok(value) => {
new_config = Some(value);
@ -107,7 +111,7 @@ pub async fn run_once(
.draw(display)?;
}
if battery.percentage <= *config.battery.alert {
if battery.percentage <= config.battery.alert.into_inner() {
let now_str = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
let text = format!("Battery at {}%, please charge!", battery.percentage);
let status_box = fb::widgets::status_box::StatusBox::new_with_default_style(

View file

@ -4,8 +4,6 @@ use envconfig::Envconfig;
use openlipc_dyn::Lipc;
use std::{rc::Rc, sync::Mutex, thread, time};
use kdash_client;
#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
@ -13,7 +11,9 @@ async fn main() -> Result<()> {
let env_config = kdash_client::config::Config::init_from_env().unwrap();
let api = kdash_client::api::Api::new(env_config.kdash_jwt, env_config.kdash_url)?;
let config = api.fetch_config().await?;
let battery = kdash_client::battery::get_battery_status()?;
let config = api.fetch_config_with_post_device_state(&battery).await?;
let lipc = Lipc::load(None)?;
@ -22,15 +22,14 @@ async fn main() -> Result<()> {
app_config: kdash_client::app::AppConfig {
net: env_config.net,
router_ip: env_config.router_ip,
assets_path: env_config.assets,
},
config: Rc::new(Mutex::new(config)),
config: Rc::new(tokio::sync::Mutex::new(config)),
lipc: Rc::new(Mutex::new(lipc)),
};
let mut display = kdash_client::fb::FramebufferDisplay::new(
kdash_client::fb::DEFAULT_FB,
state.config.lock().unwrap().device.orientation,
state.config.lock().await.device.orientation,
)?;
kdash_client::utils::kill_kindle()?;
@ -44,7 +43,7 @@ async fn main() -> Result<()> {
match kdash_client::run_once(&state, &mut display).await {
Ok(Some(new_config)) => {
log::info!("Updating config");
let mut config = state.config.lock().unwrap();
let mut config = state.config.lock().await;
if display.fb_orientation.orientation != new_config.device.orientation {
log::info!(
"Updating orientation: {:?} -> {:?}",
@ -65,7 +64,7 @@ async fn main() -> Result<()> {
}
}
let config = state.config.lock().unwrap();
let config = state.config.lock().await;
thread::sleep(config.time.delay_before_suspend.to_std()?);