Add --no-html flag and Synology Task Scheduler setup

--no-html skips dashboard generation for deployments where river.php
reads the SQLite DB directly. synology-task.sh is a ready-to-configure
wrapper for DSM Task Scheduler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 00:36:07 +01:00
parent 9a0308d380
commit fd11c956d0
2 changed files with 30 additions and 6 deletions

View File

@@ -437,7 +437,7 @@ def load_or_create_config(args: argparse.Namespace, session: requests.Session) -
# Main # Main
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
def poll_once(cfg: dict, session: requests.Session, verbose: bool = True, backfill_days: int = 0) -> None: def poll_once(cfg: dict, session: requests.Session, verbose: bool = True, backfill_days: int = 0, no_html: bool = False) -> None:
stations = fetch_level_stations(session, cfg["lat"], cfg["lon"], cfg["radius_km"]) stations = fetch_level_stations(session, cfg["lat"], cfg["lon"], cfg["radius_km"])
conn = init_db(Path(cfg["db"])) conn = init_db(Path(cfg["db"]))
if backfill_days: if backfill_days:
@@ -447,13 +447,15 @@ def poll_once(cfg: dict, session: requests.Session, verbose: bool = True, backfi
else: else:
readings = extract_readings(stations) readings = extract_readings(stations)
new_rows = store_readings(conn, readings) new_rows = store_readings(conn, readings)
if not no_html:
build_dashboard(conn, Path(cfg["output"])) build_dashboard(conn, Path(cfg["output"]))
conn.close() conn.close()
if verbose: if verbose:
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
suffix = "" if no_html else f" -> {cfg['output']}"
print( print(
f"[{ts}] {len(readings)} readings from {len(stations)} stations " f"[{ts}] {len(readings)} readings from {len(stations)} stations "
f"({new_rows} new) -> {cfg['output']}" f"({new_rows} new){suffix}"
) )
@@ -470,6 +472,7 @@ def main() -> None:
parser.add_argument("--interval", type=int, default=900, help="Seconds between polls in --daemon mode (default: 900)") parser.add_argument("--interval", type=int, default=900, help="Seconds between polls in --daemon mode (default: 900)")
parser.add_argument("--backfill", action="store_true", help="Fetch full history (up to --backfill-days) for all stations, then exit") parser.add_argument("--backfill", action="store_true", help="Fetch full history (up to --backfill-days) for all stations, then exit")
parser.add_argument("--backfill-days", type=int, default=28, dest="backfill_days", help="How many days of history to backfill (default: 28, max: 28)") parser.add_argument("--backfill-days", type=int, default=28, dest="backfill_days", help="How many days of history to backfill (default: 28, max: 28)")
parser.add_argument("--no-html", action="store_true", dest="no_html", help="Skip HTML dashboard generation (use when a web page reads the DB directly)")
args = parser.parse_args() args = parser.parse_args()
session = requests.Session() session = requests.Session()
@@ -485,7 +488,7 @@ def main() -> None:
if args.backfill: if args.backfill:
try: try:
poll_once(cfg, session, backfill_days=min(args.backfill_days, 28)) poll_once(cfg, session, backfill_days=min(args.backfill_days, 28), no_html=args.no_html)
except requests.RequestException as exc: except requests.RequestException as exc:
print(f"Backfill failed: {exc}", file=sys.stderr) print(f"Backfill failed: {exc}", file=sys.stderr)
sys.exit(1) sys.exit(1)
@@ -493,13 +496,13 @@ def main() -> None:
print(f"Running as daemon, polling every {args.interval}s. Ctrl+C to stop.") print(f"Running as daemon, polling every {args.interval}s. Ctrl+C to stop.")
while True: while True:
try: try:
poll_once(cfg, session) poll_once(cfg, session, no_html=args.no_html)
except requests.RequestException as exc: except requests.RequestException as exc:
print(f"Poll failed: {exc}", file=sys.stderr) print(f"Poll failed: {exc}", file=sys.stderr)
time.sleep(args.interval) time.sleep(args.interval)
else: else:
try: try:
poll_once(cfg, session) poll_once(cfg, session, no_html=args.no_html)
except requests.RequestException as exc: except requests.RequestException as exc:
print(f"Poll failed: {exc}", file=sys.stderr) print(f"Poll failed: {exc}", file=sys.stderr)
sys.exit(1) sys.exit(1)

21
synology-task.sh Normal file
View File

@@ -0,0 +1,21 @@
#!/bin/sh
# River monitor poller — run this via Synology Task Scheduler every 15 minutes.
#
# Setup:
# 1. Copy river_monitor.py to your Synology (e.g. /volume1/homes/simon/river_monitor.py)
# 2. Set WEB_DATA_DIR below to the data/ folder inside your web repo on the Synology
# 3. In DSM: Control Panel → Task Scheduler → Create → Scheduled Task → User-defined script
# - User: your DSM user (needs write access to WEB_DATA_DIR)
# - Schedule: every 15 minutes
# - Task: sh /volume1/homes/simon/synology-task.sh
# 4. Run once manually with --backfill to populate history, then let the schedule take over
SCRIPT=/volume1/homes/simon/river_monitor.py
WEB_DATA_DIR=/volume1/web/data
DB=$WEB_DATA_DIR/river_levels.db
LOG=$WEB_DATA_DIR/river-monitor.log
/usr/bin/python3 "$SCRIPT" \
--db "$DB" \
--no-html \
>> "$LOG" 2>&1