#!/usr/bin/env python3
"""Quick command-line test: read every DS18B20 on the 1-Wire bus.

Usage:
    python3 read_temps.py                 # read once, print, exit
    python3 read_temps.py --watch         # keep reading every 2s until Ctrl+C
    python3 read_temps.py --watch -i 5    # custom interval in seconds

No external dependencies. Must be run on the Raspberry Pi with the
1-Wire overlay enabled (see hardware-setup.md), as a user that can
read /sys/bus/w1/devices (root, or a member of a group with access).
"""

from __future__ import annotations

import argparse
import sys
import time
from pathlib import Path

DEFAULT_DEVICES_PATH = Path("/sys/bus/w1/devices")


def c_to_f(temp_c: float) -> float:
    return temp_c * 9.0 / 5.0 + 32.0


def find_devices(devices_path: Path) -> list[str]:
    if not devices_path.exists():
        return []
    return sorted(p.name for p in devices_path.iterdir() if p.name.startswith("28-"))


def read_temp_c(devices_path: Path, device_id: str) -> float | None:
    """Return temperature in Celsius, or None if the read/parse fails."""
    slave_file = devices_path / device_id / "w1_slave"
    try:
        text = slave_file.read_text()
    except OSError as exc:
        print(f"  ! could not read {slave_file}: {exc}", file=sys.stderr)
        return None

    lines = text.strip().splitlines()
    if len(lines) < 2 or "YES" not in lines[0]:
        return None  # CRC check failed

    marker = "t="
    if marker not in lines[1]:
        return None

    try:
        milli_c = int(lines[1].split(marker, 1)[1])
    except ValueError:
        return None

    if milli_c == 85000:
        # Datasheet power-on / fault sentinel value.
        return None

    return milli_c / 1000.0


def read_all(devices_path: Path) -> None:
    device_ids = find_devices(devices_path)

    if not device_ids:
        print(f"No 1-Wire devices found under {devices_path}.")
        print("Check: overlay enabled, wiring, and 4.7k pull-up to 3.3V.")
        return

    print(f"Found {len(device_ids)} device(s):")
    for device_id in device_ids:
        temp_c = read_temp_c(devices_path, device_id)
        if temp_c is None:
            print(f"  {device_id}: -- (no reading / CRC fail)")
        else:
            temp_f = c_to_f(temp_c)
            print(f"  {device_id}: {temp_c:6.3f} C   {temp_f:6.3f} F")


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--devices-path",
        type=Path,
        default=DEFAULT_DEVICES_PATH,
        help=f"1-Wire devices directory (default: {DEFAULT_DEVICES_PATH})",
    )
    parser.add_argument(
        "--watch",
        action="store_true",
        help="keep reading on a loop until Ctrl+C",
    )
    parser.add_argument(
        "-i",
        "--interval",
        type=float,
        default=2.0,
        help="seconds between reads in --watch mode (default: 2.0)",
    )
    args = parser.parse_args()

    if not args.watch:
        read_all(args.devices_path)
        return

    try:
        while True:
            print(f"\n--- {time.strftime('%Y-%m-%d %H:%M:%S')} ---")
            read_all(args.devices_path)
            time.sleep(args.interval)
    except KeyboardInterrupt:
        print("\nStopped.")


if __name__ == "__main__":
    main()
