# sensors.yaml - All sensors for monitoring, device management, and household automation # Hardware monitoring sensors - platform: openhardwaremonitor host: 10.0.0.245 port: 8085 # SNMP sensors for NVX monitoring - platform: snmp host: 10.0.0.232 baseoid: 1.3.6.1.2.1.1.5.0 name: "NVX Name" - platform: snmp host: 10.0.0.232 baseoid: 1.3.6.1.4.1.4526.18.4.1.2.1 name: "NVX Fan RPM" - platform: snmp host: 10.0.0.232 baseoid: 1.3.6.1.4.1.4526.18.5.1.2.1 name: "NVX Temp Value" # Command line sensors - platform: command_line name: "Chore Summary" command: "cat /config/www/chore_summary.txt" scan_interval: 60 - platform: command_line name: "Chore Log Raw" command: "cat /config/www/chore_log.txt" scan_interval: 60 # Time and date sensors - platform: time_date display_options: - "time" - "date" # All template sensors (using legacy format for compatibility) - platform: template sensors: # Waste collection sensors green_bin: friendly_name: "Greens Bin" value_template: > {% set start_date = as_timestamp('2026-04-12 00:00:00') %} {% set now_ts = now().timestamp() %} {% set weeks_since_start = ((now_ts - start_date) / 604800) | round(0, 'floor') | int %} {{ now().weekday() == 6 and weeks_since_start % 2 == 0 }} recycling_bin: friendly_name: "Recycling Bin" value_template: > {% set start_date = as_timestamp('2026-04-12 00:00:00') %} {% set now_ts = now().timestamp() %} {% set weeks_since_start = ((now_ts - start_date) / 604800) | round(0, 'floor') | int %} {{ now().weekday() == 6 and weeks_since_start % 2 == 1 }} # Time-based sensors is_weekday: friendly_name: "Is Weekday" value_template: > {% set now = now() %} {{ now.weekday() >= 0 and now.weekday() <= 4 }} current_day_name: friendly_name: "Current Day Name" value_template: > {% set now = now() %} {% set day_number = now.weekday() %} {{ ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][day_number] }} is_morning: friendly_name: "Is Morning" value_template: > {% set now = now() %} {{ now.hour >= 6 and now.hour < 12 }} is_afternoon: friendly_name: "Is Afternoon" value_template: > {% set now = now() %} {{ now.hour >= 12 and now.hour < 18 }} is_evening: friendly_name: "Is Evening" value_template: > {% set now = now() %} {{ now.hour >= 18 and now.hour < 21 }} is_bedtime: friendly_name: "Is Bedtime" value_template: > {% set now = now() %} {{ now.hour >= 21 and now.hour <= 23 }} is_weekend: friendly_name: "Is Weekend" value_template: > {% set now = now() %} {{ now.weekday() >= 5 }} # School term sensors school_term_1: friendly_name: "School Term 1" value_template: > {% set now = as_timestamp(now()) %} {% set start = as_timestamp(strptime('2026-01-27', '%Y-%m-%d')) %} {% set end = as_timestamp(strptime('2026-04-02', '%Y-%m-%d')) %} {{ now >= start and now <= end }} school_term_2: friendly_name: "School Term 2" value_template: > {% set now = as_timestamp(now()) %} {% set start = as_timestamp(strptime('2026-04-20', '%Y-%m-%d')) %} {% set end = as_timestamp(strptime('2026-06-26', '%Y-%m-%d')) %} {{ now >= start and now <= end }} school_term_3: friendly_name: "School Term 3" value_template: > {% set now = as_timestamp(now()) %} {% set start = as_timestamp(strptime('2026-07-13', '%Y-%m-%d')) %} {% set end = as_timestamp(strptime('2026-09-18', '%Y-%m-%d')) %} {{ now >= start and now <= end }} school_term_4: friendly_name: "School Term 4" value_template: > {% set now = as_timestamp(now()) %} {% set start = as_timestamp(strptime('2026-10-05', '%Y-%m-%d')) %} {% set end = as_timestamp(strptime('2026-12-18', '%Y-%m-%d')) %} {{ now >= start and now <= end }} is_school_term: friendly_name: "Is School Term" value_template: > {{ states('sensor.school_term_1') == 'True' or states('sensor.school_term_2') == 'True' or states('sensor.school_term_3') == 'True' or states('sensor.school_term_4') == 'True' }} current_date_debug: friendly_name: "Current Date Debug" value_template: > {{ now().date() }} # Task management sensors active_tasks_count: friendly_name: "Active Tasks Count" value_template: > {% set tasks = [ 'input_boolean.task_dishwasher_unload_pending', 'input_boolean.task_washing_machine_unload_pending', 'input_boolean.task_dryer_unload_pending', 'input_boolean.task_bins_out_pending', 'input_boolean.task_bins_in_pending', 'input_boolean.task_kitty_litter_clean_pending', 'input_boolean.task_kitty_litter_change_pending' ] %} {{ tasks | select('is_state', 'on') | list | count }} pending_tasks_list: friendly_name: "Pending Tasks" value_template: > {% set task_names = { 'input_boolean.task_dishwasher_unload_pending': 'Unload Dishwasher', 'input_boolean.task_washing_machine_unload_pending': 'Unload Washing Machine', 'input_boolean.task_dryer_unload_pending': 'Unload Dryer', 'input_boolean.task_bins_out_pending': 'Put Bins Out', 'input_boolean.task_bins_in_pending': 'Bring Bins In', 'input_boolean.task_kitty_litter_clean_pending': 'Clean Kitty Litter', 'input_boolean.task_kitty_litter_change_pending': 'Change Kitty Litter' } %} {% set pending = [] %} {% for entity, name in task_names.items() %} {% if is_state(entity, 'on') %} {% set pending = pending + [name] %} {% endif %} {% endfor %} {{ pending | join(', ') }} last_kitty_litter_change: friendly_name: "Last Kitty Litter Change" value_template: > {{ states('input_datetime.last_kitty_litter_change') | as_timestamp | timestamp_custom('%Y-%m-%d') if states('input_datetime.last_kitty_litter_change') != 'unknown' else 'Never' }} # Weekly task totals for each person lou_weekly_tasks: friendly_name: "Lou's Weekly Tasks" value_template: "{{ states('counter.chores_completed_lou') | int }}" icon_template: mdi:account jess_weekly_tasks: friendly_name: "Jess's Weekly Tasks" value_template: "{{ states('counter.chores_completed_jess') | int }}" icon_template: mdi:account william_weekly_tasks: friendly_name: "William's Weekly Tasks" value_template: "{{ states('counter.chores_completed_william') | int }}" icon_template: mdi:account xander_weekly_tasks: friendly_name: "Xander's Weekly Tasks" value_template: "{{ states('counter.chores_completed_xander') | int }}" icon_template: mdi:account bella_weekly_tasks: friendly_name: "Bella's Weekly Tasks" value_template: "{{ states('counter.chores_completed_bella') | int }}" icon_template: mdi:account # Total weekly tasks total_weekly_tasks: friendly_name: "Total Weekly Tasks" value_template: > {{ states('counter.chores_completed_lou') | int + states('counter.chores_completed_jess') | int + states('counter.chores_completed_william') | int + states('counter.chores_completed_xander') | int + states('counter.chores_completed_bella') | int }} icon_template: mdi:clipboard-list # Percentage calculations for pie chart lou_percentage: friendly_name: "Lou's Percentage" value_template: > {% set total = states('sensor.total_weekly_tasks') | int %} {% set lou_tasks = states('counter.chores_completed_lou') | int %} {% if total > 0 %} {{ ((lou_tasks / total) * 100) | round(1) }} {% else %} 0 {% endif %} unit_of_measurement: "%" jess_percentage: friendly_name: "Jess's Percentage" value_template: > {% set total = states('sensor.total_weekly_tasks') | int %} {% set jess_tasks = states('counter.chores_completed_jess') | int %} {% if total > 0 %} {{ ((jess_tasks / total) * 100) | round(1) }} {% else %} 0 {% endif %} unit_of_measurement: "%" william_percentage: friendly_name: "William's Percentage" value_template: > {% set total = states('sensor.total_weekly_tasks') | int %} {% set william_tasks = states('counter.chores_completed_william') | int %} {% if total > 0 %} {{ ((william_tasks / total) * 100) | round(1) }} {% else %} 0 {% endif %} unit_of_measurement: "%" xander_percentage: friendly_name: "Xander's Percentage" value_template: > {% set total = states('sensor.total_weekly_tasks') | int %} {% set xander_tasks = states('counter.chores_completed_xander') | int %} {% if total > 0 %} {{ ((xander_tasks / total) * 100) | round(1) }} {% else %} 0 {% endif %} unit_of_measurement: "%" bella_percentage: friendly_name: "Bella's Percentage" value_template: > {% set total = states('sensor.total_weekly_tasks') | int %} {% set bella_tasks = states('counter.chores_completed_bella') | int %} {% if total > 0 %} {{ ((bella_tasks / total) * 100) | round(1) }} {% else %} 0 {% endif %} unit_of_measurement: "%" # Network and device management sensors total_devices_online: friendly_name: "Total Devices Online" icon_template: mdi:devices unit_of_measurement: "devices" value_template: > {{ [ states('device_tracker.william_pc'), states('device_tracker.william_laptop'), states('device_tracker.william_phone'), states('device_tracker.jess_laptop_1'), states('device_tracker.jess_phone'), states('device_tracker.bella_desktop'), states('device_tracker.bella_phone'), states('device_tracker.xander_gaming_pc'), states('device_tracker.xander_console') ] | select('eq', 'home') | list | count }} availability_template: > {{ has_value('device_tracker.william_pc') and has_value('device_tracker.jess_laptop_1') and has_value('device_tracker.bella_phone') }} # NOTE: This sensor MUST exclude its own entity_id, otherwise every # recalculation changes its own state, which retriggers the template, # causing a "Template loop detected" storm. The full entity list is # intentionally NOT stored as an attribute (it exceeded the 16384-byte # recorder limit and was rejected every cycle); only a capped preview # is exposed for dashboards. unavailable_entities: friendly_name: "Unavailable Entities" icon_template: > {{ 'mdi:check-circle' if states('sensor.unavailable_entities')|int == 0 else 'mdi:alert-circle' }} unit_of_measurement: "entities" value_template: > {{ states | selectattr('state', 'in', ['unavailable', 'unknown']) | rejectattr('domain', 'eq', 'group') | rejectattr('entity_id', 'eq', 'sensor.unavailable_entities') | rejectattr('entity_id', 'search', 'sun\.') | list | count }} attribute_templates: # Capped to first 50 entity_ids to stay well under the 16 KB # recorder attribute limit. Drop this block entirely if you don't # need the preview on a dashboard. entities_preview: > {{ (states | selectattr('state', 'in', ['unavailable', 'unknown']) | rejectattr('domain', 'eq', 'group') | rejectattr('entity_id', 'eq', 'sensor.unavailable_entities') | rejectattr('entity_id', 'search', 'sun\.') | map(attribute='entity_id') | list | sort)[:50] }} family_devices_status: friendly_name: "Family Devices Status" icon_template: mdi:account-multiple value_template: > {% set devices = [ 'device_tracker.william_pc', 'device_tracker.william_phone', 'device_tracker.jess_laptop_1', 'device_tracker.jess_phone', 'device_tracker.bella_desktop', 'device_tracker.bella_phone', 'device_tracker.xander_gaming_pc' ] %} {% set online = devices | select('is_state', 'home') | list | count %} {% set total = devices | count %} {{ online }}/{{ total }} network_performance_score: friendly_name: "Network Performance Score" unit_of_measurement: "%" icon_template: > {% set score = states('sensor.network_performance_score') | int(0) %} {% if score >= 80 %}mdi:speedometer {% elif score >= 60 %}mdi:speedometer-medium {% else %}mdi:speedometer-slow {% endif %} value_template: > {% if is_state('binary_sensor.ping_router', 'on') %} {% set ping_time = state_attr('binary_sensor.ping_router', 'round_trip_time_avg') | float(0) %} {% if ping_time < 10 %}100 {% elif ping_time < 50 %}80 {% elif ping_time < 100 %}60 {% elif ping_time < 200 %}40 {% else %}20 {% endif %} {% else %} 0 {% endif %} blocked_devices_count: friendly_name: "Blocked Devices Count" icon_template: mdi:block-helper unit_of_measurement: "devices" value_template: > {{ states.input_boolean | selectattr('entity_id', 'match', '.*_blocked$') | selectattr('state', 'eq', 'on') | list | count }} attribute_templates: blocked_devices: > {{ states.input_boolean | selectattr('entity_id', 'match', '.*_blocked$') | selectattr('state', 'eq', 'on') | map(attribute='name') | list }} bella_devices_status: friendly_name: "Bella Devices Status" icon_template: mdi:account-child value_template: > {% set blocked = states.input_boolean | selectattr('entity_id', 'match', '^input_boolean\.bella_.*_blocked$') | selectattr('state', 'eq', 'on') | list | count %} {% set total = 7 %} {% if blocked == 0 %}All Access {% elif blocked == total %}Fully Blocked {% else %}{{ blocked }}/{{ total }} Blocked {% endif %} xander_devices_status: friendly_name: "Xander Devices Status" icon_template: mdi:account-child value_template: > {% set blocked = states.input_boolean | selectattr('entity_id', 'match', '^input_boolean\.xander_.*_blocked$') | selectattr('state', 'eq', 'on') | list | count %} {% set total = 7 %} {% if blocked == 0 %}All Access {% elif blocked == total %}Fully Blocked {% else %}{{ blocked }}/{{ total }} Blocked {% endif %} william_devices_status: friendly_name: "William Devices Status" icon_template: mdi:account-child value_template: > {% set blocked = states.input_boolean | selectattr('entity_id', 'match', '^input_boolean\.william_.*_blocked$') | selectattr('state', 'eq', 'on') | list | count %} {% set total = 7 %} {% if blocked == 0 %}All Access {% elif blocked == total %}Fully Blocked {% else %}{{ blocked }}/{{ total }} Blocked {% endif %} # Device blocking statistics devices_currently_blocked: friendly_name: "Devices Currently Blocked" icon_template: mdi:shield-lock unit_of_measurement: "devices" value_template: > {{ states.input_boolean | selectattr('entity_id', 'match', '.*_blocked$') | selectattr('state', 'eq', 'on') | list | count }} # System health monitoring system_health_score: friendly_name: "System Health Score" unit_of_measurement: "%" icon_template: > {% set score = states('sensor.system_health_score') | int(0) %} {% if score >= 90 %}mdi:heart-pulse {% elif score >= 70 %}mdi:heart-half-full {% else %}mdi:heart-broken {% endif %} value_template: > {% set router_ok = 1 if is_state('binary_sensor.ping_router', 'on') else 0 %} {% set auth_ok = 1 if states('input_text.router_token') not in ['unknown', '', 'unavailable'] else 0 %} {% set internet_ok = 1 if is_state('binary_sensor.internet_connectivity', 'on') else 0 %} {% set unavailable = states('sensor.unavailable_entities') | int(0) %} {% set health_penalty = (unavailable * 2) | int %} {% set base_score = ((router_ok + auth_ok + internet_ok) * 30) | int %} {{ [base_score - health_penalty, 0] | max }} # Network security level network_security_level: friendly_name: "Network Security Level" icon_template: > {% set level = states('sensor.network_security_level') %} {% if level == 'High' %}mdi:shield-check {% elif level == 'Medium' %}mdi:shield-half-full {% else %}mdi:shield-alert {% endif %} value_template: > {% set blocked = states('sensor.devices_currently_blocked') | int(0) %} {% if blocked >= 10 %}High {% elif blocked >= 5 %}Medium {% elif blocked >= 1 %}Low {% else %}Off {% endif %} # Router device count router_device_count: friendly_name: "Router Connected Devices" icon_template: mdi:devices unit_of_measurement: "devices" value_template: > {{ states.input_boolean | selectattr('entity_id', 'match', '.*_online$') | selectattr('state', 'eq', 'on') | list | count }} # Router access control rules count router_access_control_rules: friendly_name: "Active Access Control Rules" icon_template: mdi:shield-check unit_of_measurement: "rules" value_template: > {{ states('sensor.devices_currently_blocked') | int(0) }} # MAC address change detection devices_with_changed_macs: friendly_name: "Devices with Changed MACs" icon_template: mdi:alert-circle unit_of_measurement: "devices" value_template: > {{ states.input_boolean | selectattr('entity_id', 'match', '.*_mac_changed$') | selectattr('state', 'eq', 'on') | list | count }}