diff --git a/org.batteryd/battery-viewer b/org.batteryd/battery-viewer old mode 100644 new mode 100755 index 8d26636..5d1d00a --- a/org.batteryd/battery-viewer +++ b/org.batteryd/battery-viewer @@ -167,7 +167,7 @@ class BatteryChart(Gtk.DrawingArea): t_span = t_max - t_min energies = [s[2] for s in self.samples] - e_min = min(energies) + e_min = 0 e_max = max(energies) if e_max <= e_min: e_max = e_min + 1 @@ -395,6 +395,7 @@ class SessionRow(Adw.ActionRow): t_start = datetime.fromtimestamp(start_ts).strftime('%H:%M') t_end = datetime.fromtimestamp(end_ts).strftime('%H:%M') duration = end_ts - start_ts + hours = duration / 3600 if status == 'Discharging': icon = 'battery-level-50-symbolic' @@ -406,20 +407,71 @@ class SessionRow(Adw.ActionRow): icon = 'battery-level-100-symbolic' arrow = '→' + subtitle = f'{t_start} – {t_end} · {format_duration(duration)}' + if hours > 0 and status in ('Discharging', 'Charging'): + rate = abs(end_level - start_level) / hours + subtitle += f' · {rate:.1f}%/hr' + self.set_title(status) - self.set_subtitle(f'{t_start} – {t_end} · {format_duration(duration)}') + self.set_subtitle(subtitle) img = Gtk.Image.new_from_icon_name(icon) img.set_pixel_size(24) self.add_prefix(img) label = Gtk.Label() - label.set_markup(f'{start_level:.0f}% {arrow} {end_level:.0f}%') + delta = end_level - start_level + if status == 'Discharging': + label.set_markup(f'−{abs(delta):.0f}%') + elif status == 'Charging': + label.set_markup(f'+{abs(delta):.0f}%') + else: + label.set_markup(f'{abs(delta):.0f}%') label.add_css_class('caption') label.set_valign(Gtk.Align.CENTER) self.add_suffix(label) +class GapRow(Adw.ActionRow): + """Inferred gap between sessions (suspend/shutdown).""" + + def __init__(self, prev_end_ts, prev_end_level, next_start_ts, next_start_level): + super().__init__() + + t_start = datetime.fromtimestamp(prev_end_ts).strftime('%H:%M') + t_end = datetime.fromtimestamp(next_start_ts).strftime('%H:%M') + duration = next_start_ts - prev_end_ts + delta = next_start_level - prev_end_level + + if abs(delta) < 0.5: + kind = 'Shutdown / Hibernate' + icon = 'system-shutdown-symbolic' + elif delta > 0: + kind = 'Suspended (charged)' + icon = 'battery-level-50-charging-symbolic' + else: + kind = 'Suspended' + icon = 'media-playback-pause-symbolic' + + self.set_title(kind) + self.set_subtitle(f'{t_start} – {t_end} · {format_duration(duration)}') + self.add_css_class('dim-label') + + img = Gtk.Image.new_from_icon_name(icon) + img.set_pixel_size(24) + self.add_prefix(img) + + if abs(delta) >= 0.5: + label = Gtk.Label() + if delta > 0: + label.set_markup(f'+{abs(delta):.0f}%') + else: + label.set_markup(f'−{abs(delta):.0f}%') + label.add_css_class('caption') + label.set_valign(Gtk.Align.CENTER) + self.add_suffix(label) + + # ── Main window ─────────────────────────────────────────── class BatteryWindow(Adw.ApplicationWindow): @@ -572,14 +624,41 @@ class BatteryWindow(Adw.ApplicationWindow): self.session_group.set_margin_top(16) self.content.append(self.session_group) + # merge adjacent sessions with same status and small gaps + MERGE_GAP = 180 # 3 minutes — merge if same status and gap < this + GAP_THRESHOLD = 180 # show gap row if gap >= this between different statuses + + merged = [] for s in sessions: + start_ts, end_ts, start_lvl, end_lvl, status = s + if merged: + p_start, p_end, p_slvl, p_elvl, p_status = merged[-1] + gap = start_ts - p_end + if p_status == status and gap < MERGE_GAP: + # extend previous session + merged[-1] = (p_start, end_ts, p_slvl, end_lvl, status) + continue + merged.append(s) + + prev = None + for s in merged: start_ts, end_ts, start_lvl, end_lvl, status = s if end_ts - start_ts < 30: + prev = s continue + + if prev is not None: + _, prev_end, _, prev_end_lvl, _ = prev + gap = start_ts - prev_end + if gap > GAP_THRESHOLD: + gap_row = GapRow(prev_end, prev_end_lvl, start_ts, start_lvl) + self.session_group.add(gap_row) + row = SessionRow(start_ts, end_ts, start_lvl, end_lvl, status) self.session_group.add(row) + prev = s - if not sessions: + if not merged: empty = Adw.ActionRow(title='No sessions recorded') empty.add_css_class('dim-label') self.session_group.add(empty) diff --git a/org.batteryd/batteryd b/org.batteryd/batteryd index 935ea44..00aca93 100644 --- a/org.batteryd/batteryd +++ b/org.batteryd/batteryd @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/python3 -sP """ batteryd — battery tracking daemon diff --git a/screentimed/org.screentimed.desktop b/screentimed/org.screentimed.Viewer.desktop similarity index 87% rename from screentimed/org.screentimed.desktop rename to screentimed/org.screentimed.Viewer.desktop index 0ef785a..5aaf76e 100644 --- a/screentimed/org.screentimed.desktop +++ b/screentimed/org.screentimed.Viewer.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Name=ScreenTime +Name=Screentime Viewer Comment=View your screen time usage Exec=screentime-viewer Icon=preferences-system-time-symbolic diff --git a/screentimed/screentime-viewer b/screentimed/screentime-viewer old mode 100644 new mode 100755 diff --git a/screentimed/screentimed b/screentimed/screentimed old mode 100644 new mode 100755