Commit Graph

25 Commits

Author SHA1 Message Date
dc801e8b05 Add change explanation: phone-driven-alarm-timer
Some checks failed
pre-commit / pre-commit (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.12) (push) Has been cancelled
Interactive walkthrough of the move from hibernate/rtcwake scheduling to the
per-minute trigger timer: why the old model broke when the PC stopped being
shut down, the login-fire hole that came with it, the fire window and its
catch-up, and the four-step alarm resolution chain.

Includes a simulator that runs the real _decide() rule, so the window and the
gate short-circuit can be poked at rather than just read about.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 09:17:12 +02:00
997a1c7f40 MIGRATION.md: correct the pinned crdt_sync_dart tag to v0.2.0
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
The doc claimed the migration pinned crdt_sync_dart-v0.3.0. It never did:
phone_app/pubspec.yaml has said v0.2.0 since the migration commit itself
(512fdd5), and pubspec.lock resolves it to 5be99ab. MIGRATION.md was written
afterwards (21aa3bd) and got the number wrong, so the doc drifted, not the
code — the build has always been v0.2.0.

A v0.3.0 tag does exist upstream. Say so explicitly, and say that not using it
is an untaken decision rather than a mismatch to reconcile, so the next reader
doesn't "fix" the pubspec to match a doc that was wrong.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 08:59:00 +02:00
d3e81174ea Fix package discovery so install.sh gets past step 1
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Adding the Flutter phone_app/ directory gave setuptools two top-level
candidates in a flat layout, so it refused to build:

    error: Multiple top-level packages discovered in a flat-layout:
    ['phone_app', 'wake_alarm']

install.sh runs under `set -euo pipefail` and does the editable install
first, so every run since has aborted at step 1 — silently skipping the
systemd units, the sleep hook, the fan script and the shutdown wrapper.
That is why /usr/local/bin/shutdown was still the pre-token-fallback
version: not a forgotten reinstall, a broken one.

Declare the build backend explicitly and restrict discovery to
wake_alarm*, so phone_app is ignored. All 9 install steps now run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 08:28:04 +02:00
87d2629e88 Fire the alarm at the phone's time via a per-minute timer
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
The PC is no longer shut down at night, which broke the scheduling model
outright. The wake time used to be decided once, at hibernate, and baked
into the RTC by shutdown-wrapper.sh. With no shutdown there is no resume,
so sleep-hook.sh never ran and nothing fired the alarm at all.

Worse, wake-alarm.service was WantedBy=graphical-session.target and
enabled by install.sh, while _should_run_alarm() gates on day / dismissed
/ workout-logged but has no *time* gate. On an always-on machine that
target is reached on every login, so the alarm fired at login time on any
alarm day — which is every day.

Replace both with a systemd user timer ticking once a minute:

- _sync.py reads the alarm the phone pushed to kuhyx/syncs, mirroring
  shutdown-wrapper.sh's jq parse so the two readers cannot drift. Caches
  each read, then degrades fresh cache -> fetch -> stale cache ->
  FALLBACK_ALARM_HOUR, so a network blip can never cost a wake-up.
  stdlib-only: the units run /usr/bin/python, so a dependency here is a
  production install-path hazard.
- _trigger.py fires morning-routine.service (never wake-alarm.service —
  that skips the workout lock) when local time is inside
  [alarm, alarm + TRIGGER_CATCHUP_MINUTES). The catch-up window covers a
  suspended or just-booted machine; starting an already-active unit is a
  no-op, so an alarm on screen needs no extra dedupe state.
- wake-alarm.service loses its [Install]; install.sh disables it.

A timer rather than a resident scheduler: each tick is a fresh process,
so there is no daemon to die silently and no wall-clock jump to handle
across suspend/resume. Ticks are per-minute for exact-minute firing, but
only hit the network when the cache is older than 5 minutes — ~288
GitHub reads/day, and phone changes land within ~5 minutes.

Degraded reads log at INFO, not WARNING: at one tick a minute a
persistent condition would be 1440 identical journal lines a day. The one
WARNING kept is _trigger firing on a non-phone alarm time.

Token resolution gains a fallback to the sibling apps' repo-scoped
sync_token, in both readers, so one PAT covers every app on kuhyx/syncs.

Note this surfaced that wake-alarm-sync/ does not exist in kuhyx/syncs at
all: the phone has never completed a push since the crdt_sync migration,
so the hibernate path had been silently using the +8h fallback. Fixing
that is a phone-side action (reconnect GitHub with repo scope, then one
"Set Alarm & Sync"). Alarms edited directly in the Clock app still do not
reach the sync — that needs an ACTION_NEXT_ALARM_CLOCK_CHANGED receiver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 20:21:17 +02:00
64c25e67e9 phone_app: replace the stock Flutter launcher icon
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Adopt the shared icon family (python_pkg/app_icons in testsAndMisc): a
charcoal #1B1D21 field carrying an orange clock glyph, orange being this
app's own theme seedColor.

The clock is deliberately drawn without bell "ears" - at launcher size they
read as noise rather than as an alarm clock.

Adds the adaptive-icon layers the project never had, including an Android 13
monochrome layer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 16:59:38 +02:00
21aa3bdba4 Add MIGRATION.md documenting the Gist -> crdt_sync move
Some checks failed
pre-commit / pre-commit (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.12) (push) Has been cancelled
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019KwUCPESvE5CjCtmUFz8xQ
2026-07-11 23:55:10 +02:00
512fdd5c95 Migrate phone_app alarm sync from GitHub Gist to crdt_sync
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
phone_app: the alarm now syncs through the shared crdt_sync transport
(v0.2.0) into the fixed private repo kuhyx/syncs, replacing the bespoke
Gist. AlarmSyncService is the sole writer: it holds the alarm locally in a
crdt_sync LogStore (one HLC-stamped Record) and pushes it to
wake-alarm-sync/devices/phone/alarm.json. gist_service.dart is deleted;
SyncSettings drops the Gist id and keeps only a token; the device-flow scope
moves from `gist` to `repo` (the Contents API needs private-repo access).

shutdown-wrapper.sh: the PC reader now fetches that alarm from the Contents
API (Accept: application/vnd.github.raw, so no base64) and parses the
crdt_sync Record Log with jq (.alarm.fields.alarm_hour[0]). Single-file read
of the sole "phone" device; unchanged fallback to WAKE_AFTER_HOURS on any
miss so a parse failure can never brick the wake. Config moves from
gist_sync.json {token, gist_id} to sync.json {token}.

Verified: flutter analyze clean + tests pass; shellcheck clean and the full
get_synced_alarm_epoch verified against the real Log format; on-device the
app builds, installs with adb install -r (no data loss), launches and
renders. Live round-trip needs a one-time repo-scoped re-auth on the phone
and ~/.config/wake_alarm/sync.json on the PC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019KwUCPESvE5CjCtmUFz8xQ
2026-07-11 22:45:14 +02:00
d2f75f7f21 Replace sleep-and-hope audio waits with per-cycle presence checks
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Two spots blocked the alarm on a timer, guessing when the monitor's HDMI
sink would be "ready":

- _activate_all_outputs polled _list_real_sinks in a loop with
  time.sleep, waiting up to 20s for ALARM_AUDIO_SINK to appear.
- main() slept DISPLAY_WAKE_WAIT_SECONDS (5s) after waking the display
  before audio setup.

Both are gone. Activation now forces every card's output profile on and
activates whatever sink is present at that instant; the beep loop's
_play_on_all_sinks already re-lists real sinks every cycle, so a slow
monitor is swept in the moment it actually enumerates — a real presence
check, never a timed guess. main() wakes the display and proceeds
immediately; the monitor powers on in parallel.

Removes now-dead constants ALARM_AUDIO_CARD, ALARM_AUDIO_PROFILE,
ALARM_AUDIO_SINK_WAIT_SECONDS, ALARM_AUDIO_SINK_POLL_SECONDS,
DISPLAY_WAKE_WAIT_SECONDS. Activation returns in ~0.26s (was up to 20s);
the test suite drops from ~12s to ~2s (two main() tests were each really
sleeping 5s). 100% branch coverage retained.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SJ6HYXhBmYs3SiGBZv1rF
2026-07-11 18:51:03 +02:00
142f4484d8 Play alarm audio on every sink, not one hardcoded output
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
At wake time PipeWire often has only the auto_null sink (verified from
journalctl on 2026-07-11: "ONLY auto_null PipeWire sink available — alarm
will be SILENT"). The old code forced only the single HDMI monitor card
on and waited for its one sink; when that never enumerated, all audio
drained into auto_null = silence.

Now activation forces *every* card's output profile on, unmutes and maxes
every sink, and the beep loop plays on *all* sinks each cycle
(paplay --device), re-enumerating so late/added outputs are covered. A
switched-off external speaker can't be revived by software, so every
other output is tried in parallel instead of betting on one. Prior
per-card profile and per-sink volume/mute are captured and restored on
dismiss.

Concurrent paplay children are now reaped each cycle (was 1/cycle, would
have been N/cycle of <defunct> over the 30-min loud phase).

Extract the pactl sink/card management into wake_alarm/_sinks.py to keep
_audio.py under the 500-line limit. 100% branch coverage retained.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SJ6HYXhBmYs3SiGBZv1rF
2026-07-11 18:12:39 +02:00
d8e5126aab Add CI-mirror pre-push gate + requirements-sync check
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Mirrors the CI-green safeguards rolled out repo-wide (see screen-locker):
- scripts/check_requirements_sync.py: pre-commit hook that fails if a
  package import isn't declared in requirements.txt. Added a kasa ->
  python-kasa entry to the import->distribution map (python-kasa ships the
  `kasa` import, already declared correctly in requirements.txt).
- scripts/ci_mirror.sh: pre-push gate = requirements.txt-only venv
  (hash-gated) running `pre-commit run --all-files` + `pytest`, replacing
  the plain pytest-coverage pre-push hook.

Verified: mirror green (210 passed). Only the tooling files are committed;
unrelated in-flight work in the tree is left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018PMRRVVkeRyrSaaqueMZKG
2026-07-11 17:51:16 +02:00
0704e44912 Add wake_alarm_sync phone-side Flutter companion app
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
New Flutter app (com.kuhy.wake_alarm_sync) that sets the phone alarm and
writes the wake time to a GitHub Gist for cross-device sync: Gist service,
GitHub device-flow auth, sync settings, home/settings screens. Source only;
build/.dart_tool/.idea artifacts excluded by the app's .gitignore.

Not yet gated by CI (wake-alarm CI is Python-only) and pending on-device
verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RRCdR4D8SaVrN2xraMQxM
2026-07-10 23:16:11 +02:00
32d0994ba6 Fire alarm daily, harden dismissal challenge; ignore wake_state.json
- _constants.py: ALARM_DAYS now every day (was Mon/Fri/Sat/Sun);
  DISMISS_CODE_LENGTH 8->10 and DISMISS_ROUNDS_REQUIRED 2->4 so
  dismissing forces staying awake longer.
- _challenges.py / _alarm.py: expanded dismissal challenge logic, with
  matching test updates.
- shutdown-wrapper.sh: updated hibernate/rtcwake wrapper.
- .gitignore: restore the missing rule for wake_alarm/wake_state.json
  (HMAC dismissal runtime state — must never be tracked, per CLAUDE.md).

Tests: 191 passed, 100% coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RRCdR4D8SaVrN2xraMQxM
2026-07-10 23:14:40 +02:00
643c55e204 Point gatelock at the consolidated utils monorepo
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
gatelock was folded into github.com/kuhyx/utils as a subdirectory;
update the pip git URL across requirements.txt, pyproject.toml, and
.pre-commit-config.yaml to the new location with the renamed tag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 14:01:43 +02:00
9e46bcd2a2 Document the durable-clone editable-install requirement
A one-off `pip install "wake_alarm @ git+..."` from a scratch
directory was used during the original cutover; that's a non-editable
snapshot that a later git push silently never reaches. Document the
correct deployment convention (install.sh from a durable ~/wake-alarm
clone) so this doesn't drift again.
2026-06-22 12:54:27 +02:00
407d7cbf8f Extract wake_alarm from testsAndMisc as a standalone repo
Rewrites python_pkg.wake_alarm imports to wake_alarm, vendors the
shared configure_logging helper, drops the monorepo PYTHONPATH from
install.sh and the systemd unit (package is now pip-installed), and
untracks wake_state.json (runtime HMAC state, now gitignored). Scaffolds
standalone lint/test config copied from the already-corrected diet_guard
scaffold (pylint --fail-under=10 with tests excluded and the
use-implicit-booleaness/consider-using-with disables, mypy's actual
disabled-error-code set, ruff ALL, bandit, 100% branch coverage), plus
the wave.Wave_write generated-members fix this package's _audio.py needs.
2026-06-22 12:31:40 +02:00
dd01b6f846 Fix the real cause of the wake_alarm CI coverage gap
The previous two fix attempts (removing -n 4 xdist parallelism, ruling
out a Python 3.12-vs-3.14 difference) both targeted the wrong cause for
the coverage gap on _alarm_display.py:71. The actual bug:
_restore_display() was never mocked in the _block_extra_devices fixture
shared by test_alarm_part2/3/4.py, so every test exercising
on_close()/_lock.close() ran the REAL _restore_display(), which calls
the REAL shutil.which('xset'). My dev machine has /usr/bin/xset
installed, so it always hit the "found" branch by accident; the CI
runner doesn't, so it always hit the "missing" branch instead, and
there was no dedicated unit test for the "found" path at all.

Fix: mock _restore_display in that fixture (matching its already-mocked
siblings _restore_fans/_restore_alarm_audio/turn_off_plug), and add a
dedicated, hermetic test for the xset-found branch that doesn't depend
on whether the running machine actually has xset on PATH. Side benefit:
the test suite no longer makes a real `xset s on` subprocess call on
the developer's desktop as an untracked side effect of running tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A7vbgtFfZmfxJtN5DdtJky
2026-06-22 09:14:29 +02:00
3a2e63af9c Migrate wake_alarm to the shared gatelock backend
Third and final leg of the diet_guard -> screen-locker -> wake_alarm
gatelock extraction. WakeAlarm now composes gatelock.GateRoot +
LockWindow(mode="soft") instead of driving tk.Tk() directly, and moves
hardware teardown into on_close() so it runs on every exit path
(including SIGTERM), closing the prior gap where killing the process
left fans maxed. _state.py's HMAC import moves to gatelock.log_integrity,
the last call site of python_pkg.shared.log_integrity, which is deleted.

Manually verified on the real display: fullscreen overlay renders,
xdotool-typed input reaches the dismiss-code Entry (mode="soft" keeps
the existing typing-focus behavior), and SIGTERM exits within ~0.4s
while restoring hardware state. Full repo suite: 949 passed, 100%
branch coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A7vbgtFfZmfxJtN5DdtJky
2026-06-22 07:33:49 +02:00
db2be1f6a1 feat: split oversized modules for 500-line limit, fix kasa coverage gap
Split diet_guard/_gatelock.py, wake_alarm/_alarm.py, and the
usage_report.py/_usage_report_parsing.py pair into focused
sub-modules so every Python file is <= 500 lines, satisfying
test_file_length.py. Install python-kasa into .venv (declared in
requirements but missing after the 3.13->3.14 venv upgrade),
fixing 8 failing smart_plug tests and restoring 100% coverage.

Also includes prior in-progress work from the working tree: the
wake_alarm Progress/View/Hardware field-grouping refactor,
brother_printer query module + tests, diet_guard foodbank/state/cli
updates, new shared coerce/logging_setup helpers, morning_routine
orchestrator tweaks, dwm window-manager config, gaming scripts, and
misc maintenance/digital-wellbeing script updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 07:19:37 +02:00
dfe3fc6e27 refactor: split wake_alarm modules, fix ruff violations, enforce global coverage
- Split _alarm.py (1059 lines) into _alarm.py + _audio.py + _challenges.py
- Split test files (1305 / 725 lines) into 6 files, all under 500 lines
- Replace random.* with secrets.* (S311); fix RUF001, SIM117, E501 ruff errors
- Rewrite pytest_changed_packages.py to always run all packages with global
  --cov python_pkg coverage (100% branch coverage enforced across whole tree)
- Add DISMISS_ROUNDS_REQUIRED=2 and DISMISS_FLASH_SECONDS=4 to _constants.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 22:13:32 +02:00
8db7b8eb2a morning_routine: unified alarm+lock orchestrator, fix alarm audio/reliability
- New python_pkg/morning_routine package: sequential orchestrator runs wake
  alarm then workout lock as blocking subprocesses (one fullscreen owner at
  a time). Deployed as morning-routine.service; sleep hook updated to start
  it on hibernate-resume instead of the standalone wake-alarm.service.

- wake_alarm: force G27Q HDMI card profile on at alarm time, poll up to 6s
  for sink to appear, set as default + unmute 100%. Alarm now persists until
  the typeable code is entered (no more silent 30-min give-up). Service gets
  DISPLAY=:0 + ExecStartPre sleep 1 to fix cold-boot Tkinter crash.

- phone_focus_mode/config.sh: whitelist Revolut, mObywatel, VaultKitBypass.

- 100% branch coverage maintained across wake_alarm and morning_routine.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 18:55:27 +02:00
7aebf8e333 wake_alarm: louder + typeable + multi-fan
- Drop overrideredirect on the dismiss UI: on X11 it bypassed the WM and
  silently killed keyboard focus on the Entry, so the user could not type
  the dismiss code. -fullscreen + -topmost still cover the whole screen.
- Add _max_sink_volume() / _restore_sink_volume(): unmute the default
  PulseAudio/PipeWire sink and raise it to 100% at alarm start, restore
  the original volume + mute state on dismiss. This is the biggest audio
  lever — pcspkr is hardware-fixed and was already maxed.
- pcspkr: 3 back-to-back 1.5s beeps per cycle (was 1x 0.8s) at near-S16
  max amplitude.
- Fans script: control every NCT pwm[1-9] channel, persist per-channel
  pre-alarm state to /run/wake-alarm-fans.state so restore needs no args.
- Tests: 100% branch coverage on python_pkg/wake_alarm/ (140 passed).
2026-05-24 16:20:34 +02:00
3109d6053c wake_alarm: Tapo P110 plug control, fan ramp via sudo, loud warnings
- Add python-kasa-based smart-plug control (_smart_plug.py) with
  turn_on_plug / turn_off_plug called around the alarm window.
  Reads ~/.config/wake_alarm/tapo.json (host/email/password).
- Hard timeout (TAPO_TIMEOUT_SECONDS) so plug never blocks the alarm.
- Install fan-control script + sudoers entry (install.sh step 6);
  _max_fans / _restore_fans now invoke it via /usr/bin/sudo -n so
  pwm1_enable writes succeed.
- Remove ntfy.sh push notifications entirely (silent no-op was useless).
- Replace every silent skip with _logger.warning() so failures are
  loud: missing xset / xrandr / speaker-test, unreadable hwmon files,
  fan script errors, missing Tapo config, kasa import failure, etc.
- wake-alarm.service: Restart=on-failure with 10s backoff.
- Tests: 100% line+branch coverage on python_pkg/wake_alarm.
2026-05-23 19:51:26 +02:00
a776372f20 Split modules, fix tests, fix pre-commit batching
- steam_backlog_enforcer: extract _hltb_search.py and _scanning_confidence.py;
  split oversized test files into *_part2/3/4.py
- screen_locker: extract _early_bird.py and _window_setup.py from screen_lock.py;
  fix patch targets in tests (screen_lock.* -> _window_setup.*)
- wake_alarm: use shutil.which('xset') to avoid S607; add TestDisplayHelpers tests
- linux_configuration/usage_report: split into _parsing.py and _types.py;
  add bin/__init__.py (INP001); fix RUF002 (× -> x)
- pre-commit: add require_serial: true to pytest-coverage hook to prevent
  file batching across 24 CPU cores (was causing 12 parallel partial-coverage runs)
2026-05-22 22:48:28 +02:00
db21d3015f wake_alarm + midnight_shutdown: hibernate on alarm nights instead of poweroff
- install.sh: install sleep-hook.sh to systemd-sleep hooks (step 3) and
  shutdown-wrapper.sh to /usr/local/bin/shutdown (step 5)
- shutdown-wrapper.sh: new script that intercepts shutdown calls and
  redirects to rtcwake -m disk on alarm nights (Mon/Fri/Sat/Sun), pass-
  through for reboots and cancel commands
- sleep-hook.sh: new systemd-sleep hook that restarts wake-alarm.service
  for each logged-in user after hibernate resume
- setup_midnight_shutdown.sh: check wake-alarm day; if yes set RTC alarm
  and hibernate, otherwise fall back to systemctl poweroff
2026-05-22 16:01:04 +02:00
3a51a10ca9 Add tests and fix pre-commit issues across all projects
- C/lichess_random_engine, vocabulary_curve, misc/split,
  1dvelocitysimulator, opening_learner: test suites added
- CPP/miscelanious: tests added
- TS/battery-status, champions_leauge_scores, two-inputs: tests added
- python_pkg/fm24_searcher, wake_alarm: new packages added
- Fix ruff/cppcheck/eslint/clang-format failures
- Update .gitignore for C/C++ build artifacts
2026-04-12 20:45:24 +02:00