#!/bin/sh
# State-of-charge indicator for the front-panel Battery LED.

LED_DIR="/sys/class/leds/Battery"
BATTERYMANAGER="/usr/bin/batterymanager"

POLL_INTERVAL=30

LOW_THRESHOLD=20
CRIT_THRESHOLD=10
HYSTERESIS=3

LOW_DELAY_ON=500
LOW_DELAY_OFF=500
CRIT_DELAY_ON=300
CRIT_DELAY_OFF=1700

BRIGHTNESS=255

band="off"
fail_logged=0

battery_status=""
power_status=""
charging_status=""
charge_status=""

log() {
	logger -t battery-led "$@"
}

led_write() {
	echo "$2" > "$LED_DIR/$1" 2>/dev/null
}

apply_band() {
	case "$1" in
		off)
			led_write trigger none
			led_write brightness 0
			;;
		solid)
			led_write trigger none
			led_write brightness "$BRIGHTNESS"
			;;
		low)
			led_write trigger timer
			led_write delay_on "$LOW_DELAY_ON"
			led_write delay_off "$LOW_DELAY_OFF"
			led_write brightness "$BRIGHTNESS"
			;;
		crit)
			led_write trigger timer
			led_write delay_on "$CRIT_DELAY_ON"
			led_write delay_off "$CRIT_DELAY_OFF"
			led_write brightness "$BRIGHTNESS"
			;;
	esac
}

band_still_applied() {
	local active brightness

	active=$(sed -n 's/[^[]*\[\([^]]*\)\].*/\1/p' "$LED_DIR/trigger" 2>/dev/null)

	case "$1" in
		off)
			brightness=$(cat "$LED_DIR/brightness" 2>/dev/null)
			[ "$active" = "none" ] && [ "$brightness" = "0" ]
			;;
		solid)
			brightness=$(cat "$LED_DIR/brightness" 2>/dev/null)
			[ "$active" = "none" ] && [ -n "$brightness" ] && [ "$brightness" != "0" ]
			;;
		low)
			[ "$active" = "timer" ] &&
				[ "$(cat "$LED_DIR/delay_on" 2>/dev/null)" = "$LOW_DELAY_ON" ]
			;;
		crit)
			[ "$active" = "timer" ] &&
				[ "$(cat "$LED_DIR/delay_on" 2>/dev/null)" = "$CRIT_DELAY_ON" ]
			;;
	esac
}

read_state() {
	local json
	json=$("$BATTERYMANAGER" --json 2>/dev/null) || return 1
	[ -n "$json" ] || return 1

	battery_status=$(echo "$json" | jq -r '.battery_status // empty')
	power_status=$(echo "$json" | jq -r '.power_status // empty')
	charging_status=$(echo "$json" | jq -r '.charging_status // empty')
	charge_status=$(echo "$json" | jq -r '.charge_status // empty')

	[ -n "$battery_status" ]
}

decide_band() {
	local pct="$1"
	local enter_crit="$CRIT_THRESHOLD"
	local enter_low="$LOW_THRESHOLD"

	case "$band" in
		crit) enter_crit=$((CRIT_THRESHOLD + HYSTERESIS)) ;;
		low) enter_low=$((LOW_THRESHOLD + HYSTERESIS)) ;;
	esac

	if [ "$pct" -lt "$enter_crit" ]; then
		echo crit
	elif [ "$pct" -le "$enter_low" ]; then
		echo low
	else
		echo solid
	fi
}

cleanup() {
	apply_band off
	exit 0
}

trap cleanup INT TERM

[ -d "$LED_DIR" ] || {
	log "$LED_DIR is missing, nothing to drive"
	exit 1
}

apply_band "$band"

while :; do
	if read_state; then
		fail_logged=0

		case "$battery_status:$charge_status" in
			Connected:*[!0-9]* | Connected:) next="off" ;;
			Connected:*)
				if [ "$power_status" = "Connected" ] || [ "$charging_status" = "Full" ]; then
					next="solid"
				else
					next=$(decide_band "$charge_status")
				fi
				;;
			*) next="off" ;;
		esac
	else
		if [ "$fail_logged" = "0" ]; then
			log "batterymanager read failed, turning the Battery LED off"
			fail_logged=1
		fi
		next="off"
	fi

	if [ "$next" != "$band" ]; then
		log "charge ${charge_status:-n/a}%, battery ${battery_status:-unknown}, power ${power_status:-unknown}: $band -> $next"
		band="$next"
		apply_band "$band"
	elif ! band_still_applied "$band"; then
		apply_band "$band"
	fi

	sleep "$POLL_INTERVAL"
done
