# AI Trash Day Reminder — Auto-dismiss when cans are out, notify if they're not

Inspired by [the laundry line rain detector post](https://www.reddit.com/r/homeassistant/), I built a trash day reminder that uses a doorbell camera + LLM Vision to actually *see* whether the bins are at the curb — and automatically dismisses the reminder icon if they are.

---

## How it works

- Every hour on Mondays from 3 PM to 10 PM, it grabs a **cropped snapshot** from the front door camera focused on the curb where the cans live
- It sends that image to **LLM Vision** and asks: are trash cans present? (YES/NO)
- If **YES**: turns off the trash reminder `input_boolean` — the icon disappears from the dashboard automatically
- If **NO**:
  - At 8 PM: sends a push notification to both phones
  - At 10 PM: sends a second, less polite notification
  - Other hours: does nothing (silent checks only)
- At 11:59 PM Monday the dismissal flag resets, ready for next week

The crop step is important — it locks the camera view to *your* curb spot so the LLM doesn't get confused by a neighbor's bins down the street.

---

## Prerequisites

- [LLM Vision](https://github.com/valentinfrlch/ha-llmvision) custom component (HACS)
- A camera with an accessible RTSP stream or HA camera entity
- `ffmpeg` available (included in most HA installations)
- Mobile companion app for push notifications

---

## configuration.yaml

### Binary sensor (is it Monday?)

```yaml
template:
  - binary_sensor:
      - name: "Trash Day Monday"
        unique_id: trash_day_monday
        state: "{{ now().isoweekday() == 1 }}"
```

### Input boolean (dismissed flag + dashboard icon)

```yaml
input_boolean:
  trash_day_dismissed:
    name: "Trash Day Dismissed"
    icon: mdi:trash-can
```

Add this to a dashboard card and style it red when `on` — tap it to manually dismiss the reminder.

### Shell commands

The first command is the standard full-frame capture. The second applies an ffmpeg crop to isolate just your curb spot.

```yaml
shell_command:
  front_door_snapshot: >
    ffmpeg -y -hide_banner -loglevel error
    -rtsp_transport tcp
    -i "rtsp://YOUR_CAMERA_USER:YOUR_CAMERA_PASSWORD@YOUR_CAMERA_IP:554/YOUR_STREAM_PATH"
    -frames:v 1
    -q:v 3
    /config/www/snapshots/front_door_latest.jpg

  front_door_trash_snapshot: >
    ffmpeg -y -hide_banner -loglevel error
    -rtsp_transport tcp
    -i "rtsp://YOUR_CAMERA_USER:YOUR_CAMERA_PASSWORD@YOUR_CAMERA_IP:554/YOUR_STREAM_PATH"
    -vf "crop=175:145:455:525"
    -frames:v 1
    -q:v 3
    /config/www/snapshots/front_door_trash_latest.jpg
```

> **Note:** The crop values `crop=WIDTH:HEIGHT:X:Y` are pixel coordinates specific to my 1024×1024 fisheye camera. See the tuning section below.

### allowlist (if not already present)

```yaml
homeassistant:
  allowlist_external_dirs:
    - /config/www
```

---

## automations.yaml

### 1 — Hourly LLM vision check

```yaml
- id: 'trash_day_llm_check'
  alias: Trash Day - LLM Vision Check
  description: Hourly Mon 3-10 PM — auto-dismiss if cans visible; notify both phones
    at 8 PM and 10 PM if still not out
  triggers:
  - at: '15:00:00'
    trigger: time
  - at: '16:00:00'
    trigger: time
  - at: '17:00:00'
    trigger: time
  - at: '18:00:00'
    trigger: time
  - at: '19:00:00'
    trigger: time
  - at: '20:00:00'
    trigger: time
  - at: '21:00:00'
    trigger: time
  - at: '22:00:00'
    trigger: time
  conditions:
  - condition: time
    weekday:
    - mon
  - condition: state
    entity_id: input_boolean.trash_day_dismissed
    state: 'off'
  actions:
  - action: shell_command.front_door_trash_snapshot
  - delay: 00:00:03
  - response_variable: response
    data:
      provider: YOUR_LLMVISION_PROVIDER_ID
      remember: false
      use_memory: false
      include_filename: false
      target_width: 1280
      max_tokens: 100
      generate_title: false
      expose_images: false
      message: >-
        This is a cropped image of the curb directly in front of our house, taken
        from a front door camera. Are any trash cans or recycling bins visible at
        the curb? Reply with exactly one word: YES if cans are present, NO if not.
      image_file: /config/www/snapshots/front_door_trash_latest.jpg
    action: llmvision.image_analyzer
  - choose:
    - conditions:
      - condition: template
        value_template: "{{ (response.response_text | upper).startswith('YES') }}"
      sequence:
      - target:
          entity_id: input_boolean.trash_day_dismissed
        action: input_boolean.turn_on
    default:
    - condition: template
      value_template: "{{ now().hour in [20, 22] }}"
    - data:
        title: "\U0001F5D1 Trash Day Reminder"
        message: >-
          {% if now().hour == 20 %}
            It is 8 PM, do you know where YOUR trash cans are?
          {% else %}
            Put the trash out or live with the consequences for a whole week.
          {% endif %}
        data:
          image: /local/snapshots/front_door_trash_latest.jpg
          ttl: 0
          priority: high
      action: notify.YOUR_NOTIFY_SERVICE_1
    - data:
        title: "\U0001F5D1 Trash Day Reminder"
        message: >-
          {% if now().hour == 20 %}
            It is 8 PM, do you know where YOUR trash cans are?
          {% else %}
            Put the trash out or live with the consequences for a whole week.
          {% endif %}
        data:
          image: /local/snapshots/front_door_trash_latest.jpg
          url: /dashboard-security/front-door-live
      action: notify.YOUR_NOTIFY_SERVICE_2
  mode: single
```

### 2 — Reset dismissal flag at end of Monday

```yaml
- id: 'trash_day_reset'
  alias: Reset Trash Dismissal Monday Night
  triggers:
  - at: '23:59:00'
    trigger: time
  conditions:
  - condition: time
    weekday:
    - mon
  actions:
  - target:
      entity_id: input_boolean.trash_day_dismissed
    action: input_boolean.turn_off
  mode: single
```

---

## Tuning the crop

This is the most fiddly part but worth getting right. The goal is a tight crop on *your* curb spot so the LLM doesn't see neighbor bins.

**Step 1** — Add `front_door_snapshot` (the uncropped version) and call it from Developer Tools → Actions to get a full-frame reference image. Check its pixel dimensions.

**Step 2** — Open the image and draw a box around where your bins sit at the curb. Note the pixel coordinates (x, y, width, height).

**Step 3** — Set `front_door_trash_snapshot` with those values: `crop=WIDTH:HEIGHT:X:Y`

**Step 4** — Restart HA (shell_command changes require a full restart, not just a YAML reload), call the new command, and check the output image. Iterate until the bins are centered with a bit of padding.

A couple of tips:
- Fisheye lenses distort edges heavily — your curb is probably closer to center than you think
- Use fixed pixel values rather than `iw*` / `ih*` expressions; relative values can fail if the stream doesn't report dimensions fast enough
- The LLM is forgiving — it doesn't need a perfect frame, just enough that the bins are the dominant subject

---

## Dashboard icon

The `input_boolean.trash_day_dismissed` doubles as a manual dismiss button. I have it on the dashboard as a red trash can icon that's only visible on Mondays (using `binary_sensor.trash_day_monday` for conditional visibility). Tap it after taking out the bins to clear the reminder; the LLM check will also clear it automatically once it confirms the cans are out.
