2026-03-16 22:46:48 +01:00
|
|
|
"""Constants for the screen locker module."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
2026-06-21 20:11:16 +02:00
|
|
|
from gatelock.log_integrity import DEFAULT_HMAC_KEY_FILE
|
|
|
|
|
|
|
|
|
|
# Single-sourced from gatelock so the literal key path can't drift.
|
|
|
|
|
HMAC_KEY_FILE = DEFAULT_HMAC_KEY_FILE
|
2026-05-14 19:52:15 +02:00
|
|
|
SICK_LOCKOUT_SECONDS = 120 # base 2 minutes wait when sick (escalates with usage)
|
2026-03-16 22:46:48 +01:00
|
|
|
PHONE_PENALTY_DELAY_DEMO = 10
|
2026-03-27 16:13:58 +01:00
|
|
|
PHONE_PENALTY_DELAY_PRODUCTION = 100
|
2026-05-14 19:52:15 +02:00
|
|
|
# Penalty added to phone-penalty timer when ADB / phone unavailable
|
|
|
|
|
# (so unplugging phone does not become an easy escape into sick mode).
|
|
|
|
|
NO_PHONE_EXTRA_LOCKOUT_SECONDS = 480 # extra 8 minutes on top of base
|
|
|
|
|
# Sick day rate-limiting (rolling windows). Once any window is exhausted
|
|
|
|
|
# the "I'm sick" button disappears entirely.
|
|
|
|
|
SICK_BUDGET_PER_7_DAYS = 1
|
|
|
|
|
SICK_BUDGET_PER_30_DAYS = 3
|
|
|
|
|
SICK_BUDGET_PER_90_DAYS = 10
|
|
|
|
|
# Each sick day in the trailing 30 days doubles the wait countdown.
|
|
|
|
|
SICK_LOCKOUT_MULTIPLIER_PER_RECENT = 2
|
|
|
|
|
# Minimum chars in the freeform sick justification.
|
|
|
|
|
SICK_JUSTIFICATION_MIN_CHARS = 120
|
|
|
|
|
# How many past sick justifications to show on the dialog (read-only).
|
|
|
|
|
SICK_HISTORY_REVIEW_COUNT = 10
|
|
|
|
|
# Forced read-only delay before SUBMIT enables when a commitment was made.
|
|
|
|
|
SICK_COMMITMENT_FORCED_READ_SECONDS = 5
|
|
|
|
|
# Breaking a commitment counts as this many sick budget days.
|
|
|
|
|
SICK_COMMITMENT_PENALTY_DAYS = 2
|
|
|
|
|
# How long the commitment prompt stays visible after a workout unlock.
|
|
|
|
|
COMMITMENT_PROMPT_TIMEOUT_SECONDS = 15
|
2026-03-16 22:46:48 +01:00
|
|
|
ADB_TIMEOUT = 15
|
2026-06-21 20:11:16 +02:00
|
|
|
# Workout app JSON candidate paths on the phone, in the order the app prefers
|
|
|
|
|
# when writing (see sync_service.dart). The app writes to the primary /sdcard/
|
|
|
|
|
# path first and only falls back to its app-external files dir if /sdcard/ is
|
|
|
|
|
# not writable, so the locker must check both — primary first.
|
|
|
|
|
WORKOUT_APP_JSON_REMOTES = (
|
|
|
|
|
"/sdcard/workout_result.json",
|
|
|
|
|
"/storage/emulated/0/Android/data/com.kuhy.workout_app/files/workout_result.json",
|
2026-03-16 22:46:48 +01:00
|
|
|
)
|
2026-06-21 20:11:16 +02:00
|
|
|
# Port the workout app's HTTP server listens on (no ADB/developer-options needed).
|
|
|
|
|
WORKOUT_HTTP_PORT = 8765
|
|
|
|
|
MIN_WORKOUT_DURATION_MINUTES = 60
|
2026-06-25 17:50:24 +02:00
|
|
|
RUNNERUP_PACKAGES = ("org.runnerup", "org.runnerup.free")
|
|
|
|
|
RUNNERUP_DB_SDCARD_TMP = "/sdcard/.runnerup_tmp_verification.db"
|
|
|
|
|
MIN_RUN_DURATION_MINUTES = 30
|
|
|
|
|
MIN_RUN_DISTANCE_KM = 5.0
|
|
|
|
|
RUNNERUP_ACCEPTED_SPORTS: frozenset[int] = frozenset({0, 3, 5})
|
|
|
|
|
# 0=RUNNING, 3=ORIENTEERING, 5=TREADMILL
|
2026-04-09 21:44:13 +02:00
|
|
|
MAX_CLOCK_SKEW_SECONDS = 300 # 5 minutes max time skew from NTP
|
2026-05-01 19:07:34 +02:00
|
|
|
EARLY_BIRD_START_HOUR = 5
|
|
|
|
|
EARLY_BIRD_END_HOUR = 8
|
|
|
|
|
EARLY_BIRD_END_MINUTE = 30
|
2026-06-29 11:27:48 +02:00
|
|
|
HEAT_SKIP_TEMP_THRESHOLD: int = 33 # °C — above this the heat-skip dialog is offered
|
2026-06-29 11:23:19 +02:00
|
|
|
HEAT_SKIP_CITY: str = "Warsaw"
|
2026-03-16 22:46:48 +01:00
|
|
|
SHUTDOWN_CONFIG_FILE = Path("/etc/shutdown-schedule.conf")
|
|
|
|
|
# Helper script path (relative to this file)
|
|
|
|
|
ADJUST_SHUTDOWN_SCRIPT = Path(__file__).resolve().parent / "adjust_shutdown_schedule.sh"
|
|
|
|
|
# State file to track sick day usage and original config values
|
|
|
|
|
SICK_DAY_STATE_FILE = Path(__file__).resolve().parent / "sick_day_state.json"
|
2026-05-14 19:52:15 +02:00
|
|
|
# Persistent sick-day history (rate-limit, debt, commitments, justifications).
|
|
|
|
|
# Distinct from SICK_DAY_STATE_FILE which is a one-day shutdown-config snapshot.
|
|
|
|
|
SICK_HISTORY_FILE = Path(__file__).resolve().parent / "sick_history.json"
|
2026-05-22 16:00:15 +02:00
|
|
|
# JSON list of ISO date strings ("YYYY-MM-DD") for which the screen lock is skipped.
|
|
|
|
|
SCHEDULED_SKIPS_FILE = Path(__file__).resolve().parent / "scheduled_skips.json"
|
Add auto-fill RunnerUp scan, carrot bonuses, and --status interface
- Refactor RunnerUp verification: extract RunnerUpDbMixin (_runnerup_db.py),
split _scan_and_fill_week_runnerup into a helper _try_fill_runnerup_for_date
to keep cyclomatic complexity ≤10
- Generalise TCX lookup to any date in the ISO week (was today-only); all gap
days Mon→today auto-filled on every startup and 08:30 timer firing
- Add _adjust_shutdown_time_by(): +1h per extra workout beyond the 4-workout
minimum, capped at midnight (hour=24)
- Add _shutdown_base.py: daily reset of shutdown config to a stored base so
the bonus doesn't silently accumulate across days
- Add _extra_benefits.py: streak tracking, skip credits (earn (n-4) credits
for 5+ workout weeks), early-bird extension to 09:00 for eligible weeks
- Add --status mode (_status.py): non-locking CLI view showing per-day
breakdown (✓/✗), RunnerUp auto-scan, bonus status, shutdown time, streak,
skip credits, and early-bird status
- Hook carrot into _check_non_verify_exits: bonus applied whenever auto-fill
pushes weekly count above the minimum
- Pass all pre-commit hooks (ruff, mypy, pylint, bandit, shellcheck,
codespell, max-file-length); 508 tests at 100% branch coverage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017auyHmf2ZwQcDAwXaSo7KX
2026-06-28 08:08:35 +02:00
|
|
|
# State file tracking streak, skip credits, and early-bird extension weeks.
|
|
|
|
|
EXTRA_BENEFITS_FILE = Path(__file__).resolve().parent / "extra_benefits_state.json"
|
|
|
|
|
# State file storing the base (pre-bonus) shutdown hours and last reset date.
|
|
|
|
|
SHUTDOWN_BASE_FILE = Path(__file__).resolve().parent / "shutdown_base.json"
|
2026-05-28 07:43:06 +02:00
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Wake-alarm integration (originally from wake_alarm._constants / _state).
|
|
|
|
|
# These must match the values used by the companion wake_alarm service.
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Days on which the wake alarm fires (0=Mon … 6=Sun).
|
|
|
|
|
ALARM_DAYS: frozenset[int] = frozenset({0, 4, 5, 6})
|
|
|
|
|
# How many hours after midnight the alarm triggers (configurable in wake_alarm).
|
|
|
|
|
WAKE_AFTER_HOURS: int = 8
|
|
|
|
|
# Path to the rtcwake binary.
|
|
|
|
|
RTCWAKE_BIN: str = "/usr/sbin/rtcwake"
|
|
|
|
|
# State file written by wake_alarm; read here to check for workout skip.
|
2026-05-28 20:59:35 +02:00
|
|
|
WAKE_STATE_FILE = (
|
|
|
|
|
Path(__file__).resolve().parent.parent / "wake_alarm" / "wake_state.json"
|
|
|
|
|
)
|
2026-06-25 17:58:06 +02:00
|
|
|
|
|
|
|
|
# Directories where RunnerUp writes per-activity TCX exports (File Synchronizer).
|
|
|
|
|
# Listed in preference order; both resolve to the same path on most devices.
|
|
|
|
|
RUNNERUP_EXPORT_DIRS: tuple[str, ...] = (
|
|
|
|
|
"/sdcard/Documents/RunnerUp",
|
|
|
|
|
"/storage/emulated/0/Documents/RunnerUp",
|
|
|
|
|
)
|