# ============================================================================= # 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" - action: notify.notify_events 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" - action: notify.notify_events data: message: "⚠️ The litter box is STUCK ROTATED CLOSED! Visual state is 'closed' — check the mechanism." title: "Litter Box STUCK CLOSED!" 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" - action: notify.notify_events 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