1) Purpose
The fastest possible housebreaking (especially in condos/high-rises) by making sure you always know exactly when your dog is due to go out. This focuses strictly on potty timing drivers: Eat/Drink and Out/Pee/Poop. These actions set two different timers, send push notifications, and record everything to a history log.
Yes my dog’s name is Chicken Nugget. Ignore the furbo switch – that isn’t in the below. That was a hard kill to the camera when I’m home.
2) What it Does
This project adds a compact “Potty Tracker” widget and backend logic to Home Assistant. By tapping a button when an event occurs, you update a “Due Time,” and Home Assistant alerts you via mobile notifications when that time expires. It’s more of a human automator than involving smart devices. No doubt this could be adapted for many other things beyond dog training.
Key Features:
- One-tap buttons: OUT / PEE / POOP / EAT.
- Accident Modifier (BAD): A toggle to tag the next entry as an indoor accident for pattern tracking.
- Live Countdown: A “Due in / Overdue” markdown display.
- Dual Timers: Configurable intervals for standard outings vs. immediate post-meal needs.
- Smart Sleep Windows: Notifications only auto-enable if the next timer lands between 06:00 and 22:00.
- Anti-Spam Alerts: Overdue notifications fire in specific windows (Immediate, 10m late, 30m late).
NOTE: I have a “BEDTIME” script that turns off the notifcation switch when I go to bed.
3) Background and Training Details
Potty training in a condo involves a built-in delay (hallways, elevators, and distance to grass). Consistency is the only way to succeed. This tracker makes the “when do we go again?” decision data-driven so you can stay ahead of the dog’s biological clock. The old “rub their nose in it” method has shown to achieve slower results with other negative consequences.
The Training Strategy:
- The Reward Window: Dogs associate action with reward in a tiny window (1–3 seconds). We use high-value treats (like dehydrated smelt) immediately upon outdoor success.
- The Signal: Pair this with a predictable “ask” (like a bell by the door). Our dog is a 1-year-old rescue stray; literature suggests that for rescues, it can take 2 to 6 months of total consistency before they are fully reliable.
- Pattern Recognition: By tagging indoor accidents with the “Accident” modifier, you can see if your intervals are too long or if a specific meal time is causing issues.
4) The Code
Dependencies
- HACS:
custom:button-card - Home Assistant Mobile App: For push notifications.
- Logbook: (Core integration).
A. Helpers (configuration.yaml)
Add these to your YAML or create them via the Settings > Helpers UI.
input_boolean:
dog_bad_modifier:
name: "Accident Modifier"
icon: mdi:alert-decagram
dog_notifications_enabled:
name: "Potty Notifications"
icon: mdi:bell-ring
dog_show_log:
name: "Show Potty History"
icon: mdi:history
input_datetime:
dog_due_time:
name: "Next Potty Break Due"
has_date: true
has_time: true
icon: mdi:clock-alert
input_number:
dog_interval_out:
name: "Timer: Out/Pee/Poop"
min: 0.5
max: 8
step: 0.5
mode: box
unit_of_measurement: "h"
initial: 3.0
icon: mdi:door-open
dog_interval_eat:
name: "Timer: After Eating"
min: 0.5
max: 8
step: 0.5
mode: box
unit_of_measurement: "h"
initial: 1.0
icon: mdi:food-drumstick
input_text:
dog_activity_log_store:
name: "Potty History Store"
icon: mdi:history
B. Script (scripts.yaml)
This script calculates the timers and handles the “Smart Notification Window.” The first script calculates the timer and handles re-enabling notifications. The second is an example of the “Bed Button” logic to silence alerts at night.
dog_log_activity:
alias: Log Potty Activity
icon: mdi:dog
mode: single
fields:
activity_type:
description: "Activity Type"
sequence:
# --- SMART NOTIFICATION LOGIC ---
# Re-enable alerts ONLY if the next alert falls between 06:00 and 22:00.
- choose:
- conditions:
- condition: template
value_template: >
{% set interval = states('input_number.dog_interval_eat')|float
if activity_type == 'Eat'
else states('input_number.dog_interval_out')|float %}
{% set next_alert = (now() + timedelta(hours=interval)).strftime('%H:%M') %}
{{ '06:00' <= next_alert <= '22:00' }}
sequence:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.dog_notifications_enabled
# Log to History (Check for accident modifier)
- choose:
- conditions:
- condition: state
entity_id: input_boolean.dog_bad_modifier
state: "on"
sequence:
- service: logbook.log
data:
name: "Dog Tracker"
message: "⚠️ ACCIDENT: {{ activity_type }}"
entity_id: input_text.dog_activity_log_store
- service: input_boolean.turn_off
target:
entity_id: input_boolean.dog_bad_modifier
default:
- service: logbook.log
data:
name: "Dog Tracker"
message: "{{ activity_type }}"
entity_id: input_text.dog_activity_log_store
# Set the Countdown
- service: input_datetime.set_datetime
target:
entity_id: input_datetime.dog_due_time
data:
timestamp: >
{% if activity_type == 'Eat' %}
{{ (now().timestamp() + (states('input_number.dog_interval_eat')|float * 3600)) }}
{% else %}
{{ (now().timestamp() + (states('input_number.dog_interval_out')|float * 3600)) }}
{% endif %}
# Bed Button / Go to Sleep Script
go_bed_actions:
alias: Bed Button
sequence:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.dog_notifications_enabled
# [Add other bedtime actions here like turning off lights]
C. Automation (automations.yaml)
Handles overdue alerts. Replace mobile_app_your_phone with your actual notify service.
- id: 'dog_potty_countdown_alert'
alias: Dog Potty Overdue Alert
trigger:
- platform: time_pattern
minutes: "/5"
condition:
- condition: state
entity_id: input_boolean.dog_notifications_enabled
state: 'on'
- condition: template
value_template: >
{% set due = states('input_datetime.dog_due_time') %}
{% if due not in ['unknown', 'unavailable'] %}
{% set due_dt = due | as_datetime | as_local %}
{% set overdue_min = ((now() - due_dt).total_seconds() / 60) | int %}
{{ overdue_min >= 0 and (
overdue_min < 5 or
(overdue_min >= 10 and overdue_min < 15) or
(overdue_min >= 30 and overdue_min < 35)
) }}
{% else %}
false
{% endif %}
action:
- service: notify.mobile_app_your_phone
data:
message: "Potty break needed! Timer expired."
title: "Dog Alert 🐶"
data:
ttl: 0
priority: high
D. Dashboard Card (Lovelace YAML)
type: vertical-stack
title: Dog Potty Tracker 🐶
cards:
- type: grid
columns: 6
cards:
- type: custom:button-card
icon: mdi:door-open
name: OUT
tap_action:
action: call-service
service: script.dog_log_activity
service_data: {activity_type: Out}
show_name: true
- type: custom:button-card
icon: mdi:emoticon-poop
name: POOP
tap_action:
action: call-service
service: script.dog_log_activity
service_data: {activity_type: Poop}
show_name: true
- type: custom:button-card
icon: mdi:water
name: PEE
tap_action:
action: call-service
service: script.dog_log_activity
service_data: {activity_type: Pee}
show_name: true
- type: custom:button-card
icon: mdi:food-drumstick
name: EAT
tap_action:
action: call-service
service: script.dog_log_activity
service_data: {activity_type: Eat}
show_name: true
- type: custom:button-card
entity: input_boolean.dog_bad_modifier
name: ACCIDENT
icon: mdi:alert-decagram
show_name: true
tap_action: {action: toggle}
styles:
card:
- background-color: "[[[ return states['input_boolean.dog_bad_modifier'].state === 'on' ? 'red' : 'initial'; ]]]"
- type: custom:button-card
entity: input_boolean.dog_show_log
name: LOGS
icon: mdi:clipboard-list
show_name: true
tap_action: {action: toggle}
- type: markdown
content: >
<center><h2>
{% set due_state = states('input_datetime.dog_due_time') %}
{% if due_state not in ['unknown', 'unavailable'] %}
{% set due = due_state | as_datetime | as_local %}
{% set remaining = (due - now()).total_seconds() %}
{% if remaining > 0 %}
✅ Due in: {{ (remaining / 3600) | int }}:{{ '%02d' % ((remaining % 3600) / 60) | int }}
{% else %}
🚨 OVERDUE: {{ (remaining | abs / 3600) | int }}:{{ '%02d' % ((remaining | abs % 3600) / 60) | int }}
{% endif %}
{% else %} Ready... {% endif %}
</h2></center>
- type: entities
entities:
- entity: input_boolean.dog_notifications_enabled
- entity: input_number.dog_interval_out
- entity: input_number.dog_interval_eat
- type: conditional
conditions:
- entity: input_boolean.dog_show_log
state: "on"
card:
type: logbook
entities: [input_text.dog_activity_log_store]
hours_to_show: 24
