#!/usr/bin/env bash
set -euo pipefail

# Install and start the KORH weather logger on the LOFT Ubuntu server.
# Run as bbl: bash install_korh_weather_logger.sh

LOG_DIR=/home/bbl/html
LOG_FILE="$LOG_DIR/KORH.txt"
PYTHON_FILE=/usr/local/sbin/korh_weather_logger.py
UNIT_FILE=/etc/systemd/system/korh-weather-logger.service

if ! id bbl >/dev/null 2>&1; then
    echo "Expected Ubuntu account 'bbl' was not found." >&2
    exit 1
fi
if [ ! -d "$LOG_DIR" ]; then
    echo "Expected web data directory $LOG_DIR was not found." >&2
    exit 1
fi
if ! getent group www-data >/dev/null; then
    echo "Expected group 'www-data' was not found." >&2
    exit 1
fi

sudo tee "$PYTHON_FILE" >/dev/null <<'PYTHON'
#!/usr/bin/env python3
"""Sample the latest NWS KORH observation every 20 minutes."""

from datetime import datetime, timezone
from pathlib import Path
from time import sleep, time
from urllib.request import Request, urlopen
from zoneinfo import ZoneInfo
import json
import sys

URL = "https://api.weather.gov/stations/KORH/observations/latest"
LOG_FILE = Path("/home/bbl/html/KORH.txt")
EASTERN = ZoneInfo("America/New_York")
INTERVAL_SECONDS = 20 * 60


def value_and_unit(properties, name):
    item = properties.get(name) or {}
    return item.get("value"), (item.get("unitCode") or "").lower()


def temperature_c(properties, name):
    value, unit = value_and_unit(properties, name)
    if value is None:
        return None
    value = float(value)
    if "degf" in unit or "fahrenheit" in unit:
        return (value - 32.0) * 5.0 / 9.0
    return value  # NWS observation API SI temperature is degrees Celsius.


def pressure_hpa(properties):
    # Prefer sea-level pressure when reported; otherwise retain the reported
    # barometric/altimeter pressure and label which one supplied the value.
    for key, label in (("seaLevelPressure", "sea_level"),
                       ("barometricPressure", "barometric")):
        value, unit = value_and_unit(properties, key)
        if value is None:
            continue
        value = float(value)
        if "inhg" in unit or "in_hg" in unit or "inch" in unit:
            value *= 33.8638866667
        elif "pa" in unit and "hpa" not in unit:
            value /= 100.0
        return value, label
    return None, "na"


def wind_mph(properties):
    value, unit = value_and_unit(properties, "windSpeed")
    if value is None:
        return None
    value = float(value)
    if "m_s" in unit or "m/s" in unit:
        return value * 2.2369362921
    if "km_h" in unit or "km/h" in unit:
        return value / 1.609344
    if "knot" in unit or "knots" in unit:
        return value * 1.150779448
    return value  # NWS SI observations commonly report wind in km/h.


def fmt(value, places=2):
    return "na" if value is None else f"{value:.{places}f}"


def sample_once():
    request = Request(
        URL,
        headers={
            "Accept": "application/geo+json",
            "User-Agent": "KORH-heating-weather-logger/1.0 (local archive)",
        },
    )
    with urlopen(request, timeout=25) as response:
        document = json.load(response)

    properties = document["properties"]
    obs_text = properties.get("timestamp")
    if not obs_text:
        raise ValueError("NWS response did not include an observation timestamp")
    observed = datetime.fromisoformat(obs_text.replace("Z", "+00:00"))
    if observed.tzinfo is None:
        observed = observed.replace(tzinfo=timezone.utc)

    captured_utc = datetime.now(timezone.utc)
    captured_local = captured_utc.astimezone(EASTERN)
    epoch = int(captured_utc.timestamp())
    p_hpa, p_source = pressure_hpa(properties)
    rh, _ = value_and_unit(properties, "relativeHumidity")
    direction, _ = value_and_unit(properties, "windDirection")
    speed = wind_mph(properties)
    age = max(0.0, (captured_utc - observed.astimezone(timezone.utc)).total_seconds())

    fields = [
        "src=KORH",
        f"obs_utc={observed.astimezone(timezone.utc).isoformat(timespec='seconds')}",
        f"obs_age_s={age:.0f}",
        f"tC={fmt(temperature_c(properties, 'temperature'))}",
        f"rh={fmt(None if rh is None else float(rh))}",
        f"dewC={fmt(temperature_c(properties, 'dewpoint'))}",
        f"p_hpa={fmt(p_hpa)}",
        f"p_source={p_source}",
        f"wind_mph={fmt(speed)}",
        f"wind_from_deg={fmt(None if direction is None else float(direction), 0)}",
    ]
    line = f"{epoch},{captured_local.isoformat(timespec='seconds')},{'&'.join(fields)}\n"
    with LOG_FILE.open("a", encoding="utf-8") as log:
        log.write(line)
        log.flush()
    print(line, end="", flush=True)


def main():
    print(f"KORH logger started; writing {LOG_FILE}; sampling every 20 minutes.", flush=True)
    while True:
        try:
            sample_once()
        except Exception as exc:
            print(f"Sample failed: {type(exc).__name__}: {exc}", file=sys.stderr, flush=True)
        # Align subsequent samples to 00, 20, and 40 minutes past each hour.
        now = time()
        next_tick = (int(now // INTERVAL_SECONDS) + 1) * INTERVAL_SECONDS
        sleep(max(1.0, next_tick - now))


if __name__ == "__main__":
    main()
PYTHON

sudo chmod 0755 "$PYTHON_FILE"

sudo tee "$UNIT_FILE" >/dev/null <<'UNIT'
[Unit]
Description=Log KORH weather observations every 20 minutes
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=bbl
Group=www-data
UMask=0002
ExecStart=/usr/bin/python3 /usr/local/sbin/korh_weather_logger.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
UNIT

sudo touch "$LOG_FILE"
sudo chown bbl:www-data "$LOG_FILE"
sudo chmod 0664 "$LOG_FILE"
sudo systemctl daemon-reload
sudo systemctl enable --now korh-weather-logger.service

echo "KORH weather logger installed and started."
echo "Log: $LOG_FILE"
echo "Check status: sudo systemctl status korh-weather-logger.service"
echo "Follow samples: tail -f $LOG_FILE"
