316 lines
11 KiB
YAML
316 lines
11 KiB
YAML
# =============================================================================
|
|
# LITTER BOX MONITORING - MQTT Sensors & Automations
|
|
# =============================================================================
|
|
# Receives analysis results from litter_box_analyzer.py via MQTT and
|
|
# exposes them as Home Assistant entities with notifications.
|
|
#
|
|
# MQTT topics (published by the Python analyzer):
|
|
# litter_box/status/cat_present -> ON/OFF
|
|
# litter_box/status/motion_detected -> ON/OFF
|
|
# litter_box/status/motion_score -> float
|
|
# litter_box/status/box_state -> open/closed/unknown
|
|
# litter_box/status/box_diff_score -> float
|
|
# litter_box/status/litter_box_stuck -> ON/OFF
|
|
# litter_box/status/needs_scooping -> ON/OFF
|
|
# litter_box/status/cleanliness_score -> 0-100
|
|
# litter_box/status/status_message -> text
|
|
# =============================================================================
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# MQTT Binary Sensors
|
|
# ---------------------------------------------------------------------------
|
|
mqtt:
|
|
binary_sensor:
|
|
- name: "Litter Box - Cat Present"
|
|
unique_id: litter_box_cat_present
|
|
state_topic: "litter_box/status/cat_present"
|
|
payload_on: "ON"
|
|
payload_off: "OFF"
|
|
device_class: occupancy
|
|
icon: "mdi:cat"
|
|
|
|
- name: "Litter Box - Motion Detected"
|
|
unique_id: litter_box_motion_detected
|
|
state_topic: "litter_box/status/motion_detected"
|
|
payload_on: "ON"
|
|
payload_off: "OFF"
|
|
device_class: motion
|
|
icon: "mdi:motion-sensor"
|
|
|
|
- name: "Litter Box - Stuck Closed"
|
|
unique_id: litter_box_stuck_closed
|
|
state_topic: "litter_box/status/litter_box_stuck"
|
|
payload_on: "ON"
|
|
payload_off: "OFF"
|
|
device_class: problem
|
|
icon: "mdi:alert-circle"
|
|
|
|
- name: "Litter Box - Needs Scooping"
|
|
unique_id: litter_box_needs_scooping
|
|
state_topic: "litter_box/status/needs_scooping"
|
|
payload_on: "ON"
|
|
payload_off: "OFF"
|
|
device_class: problem
|
|
icon: "mdi:shovel"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# MQTT Numeric Sensors
|
|
# ---------------------------------------------------------------------------
|
|
sensor:
|
|
- name: "Litter Box - Motion Score"
|
|
unique_id: litter_box_motion_score
|
|
state_topic: "litter_box/status/motion_score"
|
|
unit_of_measurement: "%"
|
|
icon: "mdi:chart-bell-curve"
|
|
|
|
- name: "Litter Box - Cleanliness Score"
|
|
unique_id: litter_box_cleanliness_score
|
|
state_topic: "litter_box/status/cleanliness_score"
|
|
unit_of_measurement: "%"
|
|
icon: "mdi:star"
|
|
|
|
- name: "Litter Box - Box Diff Score"
|
|
unique_id: litter_box_box_diff_score
|
|
state_topic: "litter_box/status/box_diff_score"
|
|
icon: "mdi:compare"
|
|
|
|
# MQTT text/state sensors
|
|
- name: "Litter Box - Box State"
|
|
unique_id: litter_box_box_state
|
|
state_topic: "litter_box/status/box_state"
|
|
icon: "mdi:state-machine"
|
|
|
|
- name: "Litter Box - Status Message"
|
|
unique_id: litter_box_status_message
|
|
state_topic: "litter_box/status/status_message"
|
|
icon: "mdi:message-text"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper Toggles (for notification cooldown tracking)
|
|
# ---------------------------------------------------------------------------
|
|
input_boolean:
|
|
litter_box_stuck_notified:
|
|
name: "Litter Box Stuck - Notification Sent"
|
|
icon: "mdi:bell-off"
|
|
|
|
litter_box_cat_notified:
|
|
name: "Litter Box Cat - Notification Cooldown"
|
|
icon: "mdi:cat"
|
|
|
|
litter_box_scoop_notified:
|
|
name: "Litter Box Scoop - Notification Cooldown"
|
|
icon: "mdi:shovel"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Timers for cooldown resets
|
|
# ---------------------------------------------------------------------------
|
|
timer:
|
|
litter_box_cat_cooldown:
|
|
name: "Cat Visit Cooldown"
|
|
duration: "00:15:00"
|
|
restore: true
|
|
|
|
litter_box_scoop_cooldown:
|
|
name: "Scoop Reminder Cooldown"
|
|
duration: "04:00:00"
|
|
restore: true
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Automations
|
|
# ---------------------------------------------------------------------------
|
|
automation:
|
|
|
|
# --- Keep IR on at night so the camera can see the litter box ---
|
|
- id: litter_box_ir_on_at_sunset
|
|
alias: "Litter Box Camera - IR On At Sunset"
|
|
triggers:
|
|
- trigger: sun
|
|
event: sunset
|
|
actions:
|
|
- action: light.turn_on
|
|
target:
|
|
entity_id: light.litter_box_camera_ir_night_vision
|
|
mode: single
|
|
|
|
- id: litter_box_ir_off_at_sunrise
|
|
alias: "Litter Box Camera - IR Off At Sunrise"
|
|
triggers:
|
|
- trigger: sun
|
|
event: sunrise
|
|
actions:
|
|
- action: light.turn_off
|
|
target:
|
|
entity_id: light.litter_box_camera_ir_night_vision
|
|
mode: single
|
|
|
|
# --- Initialize ESPHome analysis entities so alerts can trigger from known off/clean states ---
|
|
- id: litter_box_initialize_analysis_status
|
|
alias: "Litter Box Camera - Initialize Analysis Status"
|
|
triggers:
|
|
- trigger: homeassistant
|
|
event: start
|
|
- trigger: time_pattern
|
|
minutes: "/30"
|
|
conditions:
|
|
- condition: template
|
|
value_template: >-
|
|
{{ states('binary_sensor.litter_box_camera_cat_present') in ['unknown', 'unavailable']
|
|
or states('binary_sensor.litter_box_camera_needs_scooping') in ['unknown', 'unavailable']
|
|
or states('binary_sensor.litter_box_camera_litter_level_low') in ['unknown', 'unavailable']
|
|
or states('sensor.litter_box_camera_cleanliness_score') in ['unknown', 'unavailable'] }}
|
|
actions:
|
|
- action: esphome.litter_box_cam_update_status
|
|
data:
|
|
cat_present: false
|
|
needs_scooping: false
|
|
litter_level_low: false
|
|
cleanliness_score: 100
|
|
mode: single
|
|
|
|
# --- Request a camera capture after IR turns on so analysis has a lit frame at night ---
|
|
- id: litter_box_capture_after_ir_on
|
|
alias: "Litter Box Camera - Capture After IR Turns On"
|
|
triggers:
|
|
- trigger: state
|
|
entity_id: light.litter_box_camera_ir_night_vision
|
|
to: "on"
|
|
actions:
|
|
- delay: "00:00:05"
|
|
- action: button.press
|
|
target:
|
|
entity_id: button.litter_box_camera_capture_now
|
|
mode: single
|
|
|
|
# --- Cat Detected Notification ---
|
|
- id: litter_box_cat_detected
|
|
alias: "Litter Box - Cat Detected Alert"
|
|
triggers:
|
|
- trigger: state
|
|
entity_id: binary_sensor.litter_box_camera_cat_present
|
|
from: "off"
|
|
to: "on"
|
|
conditions:
|
|
- condition: state
|
|
entity_id: input_boolean.litter_box_cat_notified
|
|
state: "off"
|
|
actions:
|
|
- action: input_boolean.turn_on
|
|
target:
|
|
entity_id: input_boolean.litter_box_cat_notified
|
|
- action: timer.start
|
|
target:
|
|
entity_id: timer.litter_box_cat_cooldown
|
|
- action: notify.mobile_app_joshuas_iphone_of_pain
|
|
data:
|
|
message: "🐱 A cat is using the litter box right now!"
|
|
title: "Litter Box Activity"
|
|
mode: single
|
|
|
|
# --- Reset cat notification cooldown ---
|
|
- id: litter_box_cat_cooldown_reset
|
|
alias: "Litter Box - Reset Cat Notification Cooldown"
|
|
triggers:
|
|
- trigger: event
|
|
event_type: timer.finished
|
|
event_data:
|
|
entity_id: timer.litter_box_cat_cooldown
|
|
actions:
|
|
- action: input_boolean.turn_off
|
|
target:
|
|
entity_id: input_boolean.litter_box_cat_notified
|
|
mode: single
|
|
|
|
# --- Stuck Closed Alert (status text confirms rotated closed) ---
|
|
- id: litter_box_stuck_alert
|
|
alias: "Litter Box - Stuck Closed Alert"
|
|
initial_state: true
|
|
triggers:
|
|
- trigger: state
|
|
entity_id: sensor.litter_box_camera_status_message
|
|
conditions:
|
|
- condition: template
|
|
value_template: >-
|
|
{{ 'stuck' in states('sensor.litter_box_camera_status_message') | lower
|
|
or 'closed' in states('sensor.litter_box_camera_status_message') | lower }}
|
|
- condition: state
|
|
entity_id: input_boolean.litter_box_stuck_notified
|
|
state: "off"
|
|
actions:
|
|
- action: input_boolean.turn_on
|
|
target:
|
|
entity_id: input_boolean.litter_box_stuck_notified
|
|
- action: notify.mobile_app_joshuas_iphone_of_pain
|
|
data:
|
|
message: "⚠️ The litter box is STUCK ROTATED CLOSED! Visual state is 'closed' — check the mechanism."
|
|
title: "Litter Box STUCK CLOSED!"
|
|
- action: notify.mobile_app_pollys_iphone
|
|
data:
|
|
message: "⚠️ The litter box is STUCK ROTATED CLOSED! Visual state is 'closed' — check the mechanism."
|
|
title: "Litter Box STUCK CLOSED!"
|
|
- action: notify.mailgun_smtp
|
|
data:
|
|
message: "The litter box camera detects the box in a rotated/closed position. The mechanism may be stuck mid-cycle."
|
|
title: "Litter Box Stuck Closed!"
|
|
target: "joshua@cnjmail.com"
|
|
mode: single
|
|
|
|
# --- Reset stuck notification when status returns to normal/open ---
|
|
- id: litter_box_stuck_reset
|
|
alias: "Litter Box - Reset Stuck Notification on Open"
|
|
initial_state: true
|
|
triggers:
|
|
- trigger: state
|
|
entity_id: sensor.litter_box_camera_status_message
|
|
conditions:
|
|
- condition: template
|
|
value_template: >-
|
|
{{ states('sensor.litter_box_camera_status_message') | lower in ['ok', 'calibrated. monitoring started.', 'image sent for analysis']
|
|
or 'open' in states('sensor.litter_box_camera_status_message') | lower }}
|
|
- condition: state
|
|
entity_id: input_boolean.litter_box_stuck_notified
|
|
state: "on"
|
|
actions:
|
|
- action: input_boolean.turn_off
|
|
target:
|
|
entity_id: input_boolean.litter_box_stuck_notified
|
|
mode: single
|
|
|
|
# --- Needs Scooping Alert ---
|
|
- id: litter_box_needs_scooping_alert
|
|
alias: "Litter Box - Needs Scooping Alert"
|
|
triggers:
|
|
- trigger: state
|
|
entity_id: binary_sensor.litter_box_camera_needs_scooping
|
|
from: "off"
|
|
to: "on"
|
|
conditions:
|
|
- condition: state
|
|
entity_id: input_boolean.litter_box_scoop_notified
|
|
state: "off"
|
|
actions:
|
|
- action: input_boolean.turn_on
|
|
target:
|
|
entity_id: input_boolean.litter_box_scoop_notified
|
|
- action: timer.start
|
|
target:
|
|
entity_id: timer.litter_box_scoop_cooldown
|
|
- action: notify.mobile_app_joshuas_iphone_of_pain
|
|
data:
|
|
message: "🧹 The litter box could use a scoop! Cleanliness: {{ states('sensor.litter_box_camera_cleanliness_score') }}%"
|
|
title: "Litter Box Needs Scooping"
|
|
mode: single
|
|
|
|
# --- Reset scoop notification cooldown ---
|
|
- id: litter_box_scoop_cooldown_reset
|
|
alias: "Litter Box - Reset Scoop Notification Cooldown"
|
|
triggers:
|
|
- trigger: event
|
|
event_type: timer.finished
|
|
event_data:
|
|
entity_id: timer.litter_box_scoop_cooldown
|
|
actions:
|
|
- action: input_boolean.turn_off
|
|
target:
|
|
entity_id: input_boolean.litter_box_scoop_notified
|
|
mode: single
|