"""Watch one thing and publish only when it changes (OpenMV, no model)

The general-purpose camera-as-sensor pattern. Point the camera at something,
describe the region you care about, and the device publishes a small message
when — and only when — that region changes state:

    {"state": "empty", "prev": "full", "conf": 0.81}

No video is uploaded. No model is loaded. Nothing is sent while the scene is
stable. A single ~200-byte message crosses the network when reality changes,
which is what makes this cheap enough to leave running forever.

The three presets below are the same twenty lines of code pointed at three
different problems. That is the point of this example: adapting it to a *new*
problem is a config change, not a rewrite.

  COFFEE POT     the carafe region goes light when the pot is empty
  INDICATOR LAMP a machine's status light — is the red lamp lit?
  TANK LEVEL     liquid is darker than the empty vessel above it

To find your ROI: run the example with DEBUG_ROI = True. It prints the measured
brightness of the region every frame so you can watch the number move as the
thing you care about changes, then pick a threshold between the two values.

Tags route the message onward — a Contact flow or a Strand workflow keyed on
the same tag turns it into a Slack message, a log entry, or a page.

Requirements:
- OpenMV cam with ``csi`` or ``sensor``
- Tendrl SDK with the vision extension (``package-vision.json``)
- Entity API key in ``/flash/config.json``
"""

import time

try:
    from tendrl.vision import Camera, Watch, roi_brightness, roi_level
except ImportError:
    raise SystemExit("This example requires the tendrl vision extension (package-vision.json)")

from tendrl import Client

# --- Configure for your deployment ------------------------------------------

# Preset: COFFEE POT — the carafe goes light (empty glass) when the coffee is gone.
ROI = (120, 80, 60, 90)       # (x, y, w, h) region to watch, in pixels
THRESHOLD = 0.45              # brightness 0.0-1.0; at/above this = STATES[1]
STATES = ("full", "empty")    # (below threshold, at-or-above threshold)
TAGS = ["coffee"]             # routes to a flow/workflow with the same tag

# Preset: INDICATOR LAMP — a lit lamp is much brighter than its housing.
# ROI = (200, 40, 30, 30)
# THRESHOLD = 0.60
# STATES = ("off", "lit")
# TAGS = ["machine-fault"]

# Preset: TANK LEVEL — liquid reads darker than the empty vessel above it.
# ROI = (140, 30, 40, 160)
# THRESHOLD = 0.50
# STATES = ("full", "low")
# TAGS = ["tank-low"]

DEBOUNCE = 5                  # consecutive agreeing frames before a change counts
COOLDOWN_SECONDS = 30         # minimum gap between published changes
CHECK_EVERY_MS = 500          # how often to look; slower = less power, less heat
DEBUG_ROI = False             # print the measured brightness every frame

# -----------------------------------------------------------------------------


def print_roi_level(cam):
    """Print the ROI's brightness so you can pick a threshold. Never returns."""
    print("Watching ROI %s — change the thing you care about and watch the number." % (ROI,))
    while True:
        level = roi_level(cam.snapshot(), ROI)
        print("  brightness %.3f  (threshold is %.2f)" % (level, THRESHOLD))
        time.sleep_ms(CHECK_EVERY_MS)


def main():
    print("=" * 60)
    print("Vision state watch: %s -> %s" % (STATES[0], STATES[1]))
    print("=" * 60)

    cam = Camera()  # DETECT mode (RGB565) by default

    if DEBUG_ROI:
        print_roi_level(cam)

    client = Client(mode="sync", debug=True)
    client.start()
    print("Waiting for network...")
    time.sleep(5)

    watch = Watch(
        client,
        detect=roi_brightness(roi=ROI, threshold=THRESHOLD, states=STATES),
        debounce=DEBOUNCE,
        cooldown_s=COOLDOWN_SECONDS,
        tags=TAGS,
        debug=True,
    )

    print(
        "Watching (debounce %d frames, cooldown %ds). Publishing only on change..."
        % (DEBOUNCE, COOLDOWN_SECONDS)
    )

    while True:
        watch.update(cam.snapshot())
        time.sleep_ms(CHECK_EVERY_MS)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("Stopped.")
