diff --git a/src/lib/components/shell/Battery.svelte b/src/lib/components/shell/Battery.svelte
index 097c6d0..ff581a6 100644
--- a/src/lib/components/shell/Battery.svelte
+++ b/src/lib/components/shell/Battery.svelte
@@ -1,11 +1,13 @@
{#if battery}
-
-
- {battery.level}%
-
+
+
+
+ {#if open}
+ (open = false)} />
+ {/if}
+
{/if}
diff --git a/src/lib/desktop/battery.ts b/src/lib/desktop/battery.ts
index fb30391..b383cd1 100644
--- a/src/lib/desktop/battery.ts
+++ b/src/lib/desktop/battery.ts
@@ -1,11 +1,18 @@
-const hour = 3600 * 1000;
+const minute = 60 * 1000;
+const hour = 60 * minute;
+const dayDuration = 24 * hour;
const dischargeDuration = 5 * hour;
const chargeDuration = 1 * hour;
const cycleDuration = dischargeDuration + chargeDuration;
+const cycleCountStart = Date.UTC(2025, 10, 21);
+
+export const batteryCapacityWh = 42;
export interface BatteryState {
level: number;
charging: boolean;
+ minutesRemaining: number;
+ chargeCycles: number;
}
function easternWallClockMs(date: Date): number {
@@ -24,12 +31,24 @@ function easternWallClockMs(date: Date): number {
}
export function batteryAt(date: Date): BatteryState {
- const position = easternWallClockMs(date) % cycleDuration;
+ const wallClock = easternWallClockMs(date);
+ const position = wallClock % cycleDuration;
+ const chargeCycles = Math.floor((wallClock - cycleCountStart) / dayDuration);
if (position < dischargeDuration) {
- return { level: Math.round(100 - (position / dischargeDuration) * 100), charging: false };
+ return {
+ level: Math.round(100 - (position / dischargeDuration) * 100),
+ charging: false,
+ minutesRemaining: Math.round((dischargeDuration - position) / minute),
+ chargeCycles
+ };
}
- return { level: Math.round(((position - dischargeDuration) / chargeDuration) * 100), charging: true };
+ return {
+ level: Math.round(((position - dischargeDuration) / chargeDuration) * 100),
+ charging: true,
+ minutesRemaining: Math.round((cycleDuration - position) / minute),
+ chargeCycles
+ };
}
export function batteryIcon({ level, charging }: BatteryState): string {
@@ -41,3 +60,32 @@ export function batteryIcon({ level, charging }: BatteryState): string {
if (level > 10) return 'mdi:battery-20';
return 'mdi:battery-alert';
}
+
+export function isBatteryFull({ level }: BatteryState): boolean {
+ return level >= 100;
+}
+
+export function isBatteryLow({ level, charging }: BatteryState): boolean {
+ return level < 20 && !charging;
+}
+
+export function batteryStatusLabel(battery: BatteryState): string {
+ if (isBatteryFull(battery)) return 'Fully charged';
+ return battery.charging ? 'Charging' : 'Discharging';
+}
+
+export function batteryHealth({ chargeCycles }: BatteryState): number {
+ return Math.max(80, 100 - Math.round(chargeCycles / 40));
+}
+
+export function powerRateLabel({ charging }: BatteryState): string {
+ const durationInHours = (charging ? chargeDuration : dischargeDuration) / hour;
+ const watts = (batteryCapacityWh / durationInHours).toFixed(1);
+ return charging ? `+${watts}W` : `-${watts}W`;
+}
+
+export function formatDuration(totalMinutes: number): string {
+ const hours = Math.floor(totalMinutes / 60);
+ const minutes = totalMinutes % 60;
+ return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
+}
\ No newline at end of file