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:
@@ -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)
|
||||||
build_dashboard(conn, Path(cfg["output"]))
|
if not no_html:
|
||||||
|
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
21
synology-task.sh
Normal 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
|
||||||
Reference in New Issue
Block a user