You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
622 lines
32 KiB
622 lines
32 KiB
######################################################################
|
|
# @CCOSTAN - Follow Me on X
|
|
# For more info visit https://www.vcloudinfo.com/click-here
|
|
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
|
|
# -------------------------------------------------------------------
|
|
# BearClaw Integration - Home Assistant dispatch + lifecycle callbacks
|
|
# Home Assistant dispatches jobs to codex_appliance and receives structured lifecycle callbacks.
|
|
# -------------------------------------------------------------------
|
|
# Related Issue: 1797
|
|
# Related Issue: 1845
|
|
# Notes: Telegram ingress now lives directly in the dedicated codex_appliance VM.
|
|
# Notes: Most BearClaw decision logic runs in codex_appliance/server.js.
|
|
# Notes: GitHub capture behavior (issue creation/labels/research flow) belongs in codex_appliance, not HA YAML.
|
|
# Notes: Shared script helper `script.joanna_dispatch` lives in config/script/joanna_dispatch.yaml.
|
|
# Notes: The callback webhook writes JOANNA activity entries to logbook for traceability and optional HA alerts.
|
|
# Notes: Adaptive TeslaMate road-trip milestone/arrival/final callbacks become compact family mobile summaries; Joanna sends destination/date reports and a private final route map to Carlo and Stacey.
|
|
# Notes: Progress and destination-arrival callbacks emit tesla_road_trip_active so Vacation Mode can use the signal when the whole family is away.
|
|
# Notes: Status telemetry polling expects !secret bearclaw_status_url (token header stays !secret bearclaw_token).
|
|
# Notes: Status telemetry includes the latest 12 dispatches in the rolling 24-hour window for the Joanna Activity drill-down.
|
|
# Notes: Status telemetry exposes compact scheduled-job warning/error health for the Systems dashboard.
|
|
# Notes: Status telemetry distinguishes the compact BearClaw working-memory index from the federated OneNote KB.
|
|
# Notes: sensor.joanna_onenote_kb_health marks the federated KB warning after 90 minutes without a successful sync.
|
|
# Notes: Status telemetry also exposes compact, user-keyed step trends and personal records for the Health dashboard.
|
|
# Notes: Retired QMD health telemetry is no longer imported or materialized in Home Assistant.
|
|
# Notes: TeslaMate road-trip walkthrough: https://youtu.be/9-9T6v17NEw
|
|
# Notes: TeslaMate road-trip companion post: https://www.vcloudinfo.com/2026/07/teslamate-road-trip-reports-codex.html
|
|
# Notes: Nightly Duplicati verification calls a codex_appliance admin endpoint and returns structured health to HA via response_variable.
|
|
# Notes: v2 intake is the primary HA contract; legacy command/ingest routes remain appliance-side shims.
|
|
# Notes: Command payload supports async_only for automation-first queueing when immediate inline handling is not required.
|
|
# Notes: Command payload supports optional metadata for HA dispatch context snapshots.
|
|
# Notes: HA automation dispatches default to BearClaw's ops domain so wording like NAS "health" cannot route to the health coach.
|
|
# Notes: Blog: https://www.vcloudinfo.com/2026/03/joanna-dispatch-telemetry-home-assistant-infrastructure-dashboard/
|
|
######################################################################
|
|
|
|
rest_command:
|
|
bearclaw_command:
|
|
url: !secret bearclaw_command_url
|
|
method: post
|
|
timeout: 30
|
|
content_type: application/json
|
|
headers:
|
|
x-codex-token: !secret bearclaw_token
|
|
payload: >
|
|
{
|
|
"kind": "command",
|
|
"transport": "ha",
|
|
"source": {{ source | default('home_assistant') | tojson }},
|
|
"actor": {
|
|
"id": {{ user | default('carlo') | tojson }}
|
|
},
|
|
"conversation": {
|
|
"id": {{ source | default('home_assistant') | tojson }},
|
|
"type": "automation"
|
|
},
|
|
"input": {
|
|
"text": {{ text | tojson }},
|
|
"context": {{ context | default(none) | tojson }},
|
|
"callback": {{ callback | default(none) | tojson }}
|
|
},
|
|
"routing": {
|
|
"domainHint": {{ domain_hint | default('ops', true) | tojson }},
|
|
"laneHint": {{ lane_hint | default('joanna.ops', true) | tojson }}
|
|
},
|
|
"replyTargets": [
|
|
{
|
|
"type": "ha",
|
|
"callbackEventType": "lifecycle"
|
|
}
|
|
],
|
|
"metadata": {{ metadata | default({}, true) | tojson }},
|
|
"priority": {{ priority | default(none) | tojson }},
|
|
"async_only": {{ async_only | default(false) | tojson }}
|
|
}
|
|
|
|
bearclaw_ingest:
|
|
url: !secret bearclaw_ingest_url
|
|
method: post
|
|
content_type: application/json
|
|
headers:
|
|
x-codex-token: !secret bearclaw_token
|
|
payload: >
|
|
{
|
|
"kind": "event",
|
|
"transport": "ha",
|
|
"source": "homeassistant",
|
|
"actor": {
|
|
"id": "system"
|
|
},
|
|
"conversation": {
|
|
"id": "homeassistant",
|
|
"type": "automation"
|
|
},
|
|
"input": {
|
|
"text": {{ summary | default('event') | tojson }},
|
|
"event": {
|
|
"summary": {{ summary | default('event') | tojson }},
|
|
"wake": {{ wake | default(false) | tojson }},
|
|
"priority": {{ priority | default(none) | tojson }},
|
|
"source": "homeassistant"
|
|
}
|
|
},
|
|
"replyTargets": []
|
|
}
|
|
|
|
bearclaw_duplicati_verify:
|
|
url: !secret bearclaw_duplicati_verify_url
|
|
method: post
|
|
timeout: 60
|
|
content_type: application/json
|
|
headers:
|
|
x-codex-token: !secret bearclaw_token
|
|
payload: >
|
|
{
|
|
"reason": {{ reason | default('home_assistant') | tojson }}
|
|
}
|
|
|
|
bearclaw_telegram_send:
|
|
url: !secret bearclaw_telegram_send_url
|
|
method: post
|
|
timeout: 30
|
|
content_type: application/json
|
|
headers:
|
|
x-codex-token: !secret bearclaw_token
|
|
payload: >
|
|
{
|
|
"message": {{ message | tojson }},
|
|
"parse_mode": {{ parse_mode | default('plain_text') | tojson }},
|
|
"disable_web_page_preview": {{ disable_web_page_preview | default(true) | tojson }},
|
|
"chat_id": {{ chat_id | default(none) | tojson }},
|
|
"user": {{ user | default('carlo') | tojson }}
|
|
}
|
|
sensor:
|
|
- platform: rest
|
|
name: BearClaw Status Telemetry
|
|
unique_id: bearclaw_status_telemetry
|
|
resource: !secret bearclaw_status_url
|
|
method: GET
|
|
scan_interval: 60
|
|
timeout: 30
|
|
headers:
|
|
x-codex-token: !secret bearclaw_token
|
|
value_template: "{{ 'ok' if value_json.ok | default(false) else 'error' }}"
|
|
json_attributes:
|
|
- dispatchStats
|
|
- dispatchActivity
|
|
- queue
|
|
- active
|
|
- platform
|
|
- transports
|
|
- followups
|
|
- memoryIndex
|
|
- oneNoteKb
|
|
- healthInsights
|
|
- scheduledJobsHealth
|
|
|
|
template:
|
|
- sensor:
|
|
- name: BearClaw Scheduled Job Health
|
|
unique_id: bearclaw_scheduled_job_health
|
|
icon: mdi:calendar-alert
|
|
availability: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') %}
|
|
{{ health is mapping and health.get('status') in ['ok', 'warning', 'error'] }}
|
|
state: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') | default({}, true) %}
|
|
{{ health.get('status', 'unknown') }}
|
|
attributes:
|
|
issue_count: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') | default({}, true) %}
|
|
{{ health.get('issueCount', 0) | int(0) }}
|
|
error_count: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') | default({}, true) %}
|
|
{{ health.get('errorCount', 0) | int(0) }}
|
|
warning_count: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') | default({}, true) %}
|
|
{{ health.get('warningCount', 0) | int(0) }}
|
|
summary: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') | default({}, true) %}
|
|
{{ health.get('summary', 'Scheduled-job health unavailable') }}
|
|
issues: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') | default({}, true) %}
|
|
{{ health.get('issues', []) }}
|
|
generated_at: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'scheduledJobsHealth') | default({}, true) %}
|
|
{{ health.get('generatedAt') }}
|
|
|
|
- name: Joanna OneNote KB Health
|
|
unique_id: joanna_onenote_kb_health
|
|
icon: mdi:notebook-check-outline
|
|
availability: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') %}
|
|
{{ kb is mapping }}
|
|
state: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{% set last_sync_ts = as_timestamp(kb.get('lastSyncAt'), 0) %}
|
|
{% set age_minutes = ((as_timestamp(now()) - last_sync_ts) / 60) if last_sync_ts > 0 else 999999 %}
|
|
{% if not kb.get('ok', false) or kb.get('lastSyncError') not in [none, ''] or kb.get('lastSchedulerError') not in [none, ''] %}
|
|
error
|
|
{% elif age_minutes > 90 %}
|
|
warning
|
|
{% else %}
|
|
ok
|
|
{% endif %}
|
|
attributes:
|
|
docs: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('docs', 0) | int(0) }}
|
|
chunks: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('chunks', 0) | int(0) }}
|
|
last_sync_at: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('lastSyncAt') }}
|
|
last_modified_at: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('lastModifiedAt') }}
|
|
source_accounts: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('sourceAccounts', []) }}
|
|
pending_digest: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('pendingDigest', 0) | int(0) }}
|
|
pending_synthesis: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('pendingSynthesisAll', 0) | int(0) }}
|
|
carlo_docs: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('sources', {}).get('carlo', {}).get('docs', 0) | int(0) }}
|
|
stacey_docs: >-
|
|
{% set kb = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteKb') | default({}, true) %}
|
|
{{ kb.get('sources', {}).get('stacey', {}).get('docs', 0) | int(0) }}
|
|
|
|
- name: Joanna HA Dispatches 24h
|
|
unique_id: joanna_ha_dispatches_24h
|
|
icon: mdi:calendar-clock
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('dispatched', 0) | int(0) }}
|
|
|
|
- name: Joanna HA Dispatches 7d
|
|
unique_id: joanna_ha_dispatches_7d
|
|
icon: mdi:calendar-range
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('d7', {}).get('haAutomation', {}).get('dispatched', 0) | int(0) }}
|
|
|
|
- name: Joanna HA Dispatches 30d
|
|
unique_id: joanna_ha_dispatches_30d
|
|
icon: mdi:calendar-month
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('d30', {}).get('haAutomation', {}).get('dispatched', 0) | int(0) }}
|
|
|
|
- name: Joanna HA Completed 24h
|
|
unique_id: joanna_ha_completed_24h
|
|
icon: mdi:check-decagram
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('completed', 0) | int(0) }}
|
|
|
|
- name: Joanna HA Success Rate 24h
|
|
unique_id: joanna_ha_success_rate_24h
|
|
icon: mdi:percent-circle
|
|
unit_of_measurement: '%'
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('successRatePct', 0) | float(0) }}
|
|
|
|
- name: Joanna HA Errors 24h
|
|
unique_id: joanna_ha_errors_24h
|
|
icon: mdi:alert-circle-outline
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{% set outcomes = stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('outcomes', {}) %}
|
|
{{ (outcomes.get('error', 0) | int(0)) + (outcomes.get('notify_error', 0) | int(0)) }}
|
|
|
|
- name: Joanna All Dispatches 24h
|
|
unique_id: joanna_all_dispatches_24h
|
|
icon: mdi:calendar-clock
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('h24', {}).get('all', {}).get('dispatched', 0) | int(0) }}
|
|
|
|
- name: Joanna All Dispatches 7d
|
|
unique_id: joanna_all_dispatches_7d
|
|
icon: mdi:calendar-range
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('d7', {}).get('all', {}).get('dispatched', 0) | int(0) }}
|
|
|
|
- name: Joanna All Dispatches 30d
|
|
unique_id: joanna_all_dispatches_30d
|
|
icon: mdi:calendar-month
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{{ stats.get('windows', {}).get('d30', {}).get('all', {}).get('dispatched', 0) | int(0) }}
|
|
|
|
- name: Joanna Queue Pending
|
|
unique_id: joanna_queue_pending
|
|
icon: mdi:playlist-clock
|
|
unit_of_measurement: jobs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set queue = state_attr('sensor.bearclaw_status_telemetry', 'queue') | default({}, true) %}
|
|
{{ queue.get('pending', 0) | int(0) }}
|
|
|
|
- name: Joanna Active Job Minutes
|
|
unique_id: joanna_active_job_minutes
|
|
icon: mdi:timer-outline
|
|
unit_of_measurement: min
|
|
state_class: measurement
|
|
state: >-
|
|
{% set active = state_attr('sensor.bearclaw_status_telemetry', 'active') | default({}, true) %}
|
|
{% set elapsed_ms = active.get('elapsedMs', 0) | int(0) %}
|
|
{{ (elapsed_ms / 60000) | round(1) }}
|
|
|
|
- name: Joanna Minutes Since HA Dispatch
|
|
unique_id: joanna_minutes_since_ha_dispatch
|
|
icon: mdi:clock-fast
|
|
unit_of_measurement: min
|
|
state_class: measurement
|
|
state: >-
|
|
{% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %}
|
|
{% set dispatched_at = stats.get('latest', {}).get('haAutomation', {}).get('dispatchedAt') %}
|
|
{% set dispatched_ts = as_timestamp(dispatched_at, 0) %}
|
|
{% if dispatched_ts > 0 %}
|
|
{{ ((as_timestamp(now()) - dispatched_ts) / 60) | round(1) }}
|
|
{% else %}
|
|
0
|
|
{% endif %}
|
|
|
|
- name: Joanna Memory Docs
|
|
unique_id: joanna_memory_docs
|
|
icon: mdi:file-document-multiple-outline
|
|
unit_of_measurement: docs
|
|
state_class: measurement
|
|
state: >-
|
|
{% set memory = state_attr('sensor.bearclaw_status_telemetry', 'memoryIndex') | default({}, true) %}
|
|
{{ memory.get('docs', 0) | int(0) }}
|
|
|
|
- name: Joanna Memory Chunks
|
|
unique_id: joanna_memory_chunks
|
|
icon: mdi:text-box-search-outline
|
|
unit_of_measurement: chunks
|
|
state_class: measurement
|
|
state: >-
|
|
{% set memory = state_attr('sensor.bearclaw_status_telemetry', 'memoryIndex') | default({}, true) %}
|
|
{{ memory.get('chunks', 0) | int(0) }}
|
|
|
|
- name: Joanna Minutes Since Memory Index
|
|
unique_id: joanna_minutes_since_memory_index
|
|
icon: mdi:database-clock-outline
|
|
unit_of_measurement: min
|
|
state_class: measurement
|
|
state: >-
|
|
{% set memory = state_attr('sensor.bearclaw_status_telemetry', 'memoryIndex') | default({}, true) %}
|
|
{% set indexed_at = memory.get('indexedAt') %}
|
|
{% set indexed_ts = as_timestamp(indexed_at, 0) %}
|
|
{% if indexed_ts > 0 %}
|
|
{{ ((as_timestamp(now()) - indexed_ts) / 60) | round(1) }}
|
|
{% else %}
|
|
0
|
|
{% endif %}
|
|
|
|
- name: Joanna Carlo Steps Trend
|
|
unique_id: joanna_carlo_steps_trend
|
|
icon: mdi:walk
|
|
unit_of_measurement: steps/day
|
|
state_class: measurement
|
|
availability: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('current7DayAverage') not in [none, 'unknown', 'unavailable', ''] }}
|
|
state: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('current7DayAverage', 0) | int(0) }}
|
|
attributes:
|
|
prior_28d_average: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior28DayAverage') }}
|
|
prior_90d_average: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior90DayAverage') }}
|
|
change_vs_28_pct: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('changeVs28Percent') }}
|
|
change_vs_90_pct: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('changeVs90Percent') }}
|
|
current_7d_samples: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('current7DaySamples', 0) | int(0) }}
|
|
prior_28d_samples: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior28DaySamples', 0) | int(0) }}
|
|
prior_90d_samples: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior90DaySamples', 0) | int(0) }}
|
|
completed_through: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('completedThroughDayKey') }}
|
|
|
|
- name: Joanna Stacey Steps Trend
|
|
unique_id: joanna_stacey_steps_trend
|
|
icon: mdi:walk
|
|
unit_of_measurement: steps/day
|
|
state_class: measurement
|
|
availability: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('current7DayAverage') not in [none, 'unknown', 'unavailable', ''] }}
|
|
state: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('current7DayAverage', 0) | int(0) }}
|
|
attributes:
|
|
prior_28d_average: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior28DayAverage') }}
|
|
prior_90d_average: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior90DayAverage') }}
|
|
change_vs_28_pct: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('changeVs28Percent') }}
|
|
change_vs_90_pct: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('changeVs90Percent') }}
|
|
current_7d_samples: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('current7DaySamples', 0) | int(0) }}
|
|
prior_28d_samples: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior28DaySamples', 0) | int(0) }}
|
|
prior_90d_samples: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior90DaySamples', 0) | int(0) }}
|
|
completed_through: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('completedThroughDayKey') }}
|
|
|
|
- name: Joanna Carlo Latest Personal Record
|
|
unique_id: joanna_carlo_latest_personal_record
|
|
icon: mdi:trophy-outline
|
|
availability: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest') is mapping }}
|
|
state: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('label', 'No records') }}
|
|
attributes:
|
|
display_value: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('displayValue') }}
|
|
activity_type: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('activityType') }}
|
|
achieved_on: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('dayKey') }}
|
|
record_count: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('count', 0) | int(0) }}
|
|
|
|
- name: Joanna Stacey Latest Personal Record
|
|
unique_id: joanna_stacey_latest_personal_record
|
|
icon: mdi:trophy-outline
|
|
availability: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest') is mapping }}
|
|
state: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('label', 'No records') }}
|
|
attributes:
|
|
display_value: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('displayValue') }}
|
|
activity_type: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('activityType') }}
|
|
achieved_on: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('dayKey') }}
|
|
record_count: >-
|
|
{% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %}
|
|
{{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('count', 0) | int(0) }}
|
|
|
|
- binary_sensor:
|
|
- name: Joanna Session Source Indexed
|
|
unique_id: joanna_session_source_indexed
|
|
icon: mdi:message-text-clock-outline
|
|
state: >-
|
|
{% set memory = state_attr('sensor.bearclaw_status_telemetry', 'memoryIndex') | default({}, true) %}
|
|
{% set options = memory.get('options', {}) if memory is mapping else {} %}
|
|
{{ options.get('includeSessionSource', false) in [true, 'true', 'True', 'on', 'yes', 1, '1'] }}
|
|
|
|
- name: Joanna Memory Index Stale
|
|
unique_id: joanna_memory_index_stale
|
|
icon: mdi:database-alert-outline
|
|
state: >-
|
|
{% set memory = state_attr('sensor.bearclaw_status_telemetry', 'memoryIndex') | default({}, true) %}
|
|
{{ memory.get('stale', false) in [true, 'true', 'True', 'on', 'yes', 1, '1'] }}
|
|
|
|
automation:
|
|
- id: bearclaw_lifecycle_webhook
|
|
alias: BearClaw Lifecycle Webhook
|
|
description: Receives structured BearClaw lifecycle callbacks for HA-triggered work.
|
|
mode: queued
|
|
trigger:
|
|
- platform: webhook
|
|
webhook_id: !secret bearclaw_reply_webhook_id
|
|
allowed_methods:
|
|
- POST
|
|
local_only: true
|
|
action:
|
|
- variables:
|
|
event_type: "{{ trigger.json.event_type | default('progress') | lower }}"
|
|
status: "{{ trigger.json.status | default(event_type, true) | lower }}"
|
|
source: "{{ trigger.json.source | default('homeassistant', true) | string | lower | trim }}"
|
|
severity: "{{ trigger.json.severity | default('active', true) | string | lower | trim }}"
|
|
summary: "{{ trigger.json.summary | default('Joanna lifecycle callback', true) }}"
|
|
message: "{{ trigger.json.message | default(summary, true) }}"
|
|
job_id: "{{ trigger.json.job_id | default('', true) }}"
|
|
run_id: "{{ trigger.json.run_id | default('', true) }}"
|
|
logbook_message: >-
|
|
{% set compact = (event_type ~ ' | ' ~ summary ~ ' | ' ~ message) | replace('\r', ' ') | replace('\n', ' ') | trim %}
|
|
{% if compact | length > 240 %}
|
|
{{ compact[:237] ~ '...' }}
|
|
{% else %}
|
|
{{ compact }}
|
|
{% endif %}
|
|
- service: script.send_to_logbook
|
|
data:
|
|
topic: JOANNA
|
|
message: "{{ status | upper }}: {{ logbook_message }}"
|
|
- choose:
|
|
- conditions:
|
|
- condition: template
|
|
value_template: >-
|
|
{{ source == 'scheduler_tesla_road_trip'
|
|
and status in ['progress', 'arrival', 'completed'] }}
|
|
sequence:
|
|
- service: script.notify_engine
|
|
data:
|
|
title: >-
|
|
{% if status == 'progress' %}
|
|
Tesla road-trip update
|
|
{% elif status == 'arrival' %}
|
|
Arrived at road-trip destination
|
|
{% else %}
|
|
Tesla road trip complete
|
|
{% endif %}
|
|
value1: >-
|
|
{% set compact = (message if message | trim != '' else summary) | replace('\r', ' ') | replace('\n', ' ') | trim %}
|
|
{% if compact | length > 180 %}
|
|
{{ compact[:177] ~ '...' }}
|
|
{% else %}
|
|
{{ compact }}
|
|
{% endif %}
|
|
who: family
|
|
group: Tesla_Model_Y
|
|
level: active
|
|
- if:
|
|
- condition: template
|
|
value_template: "{{ status in ['progress', 'arrival'] }}"
|
|
then:
|
|
- event: tesla_road_trip_active
|
|
event_data:
|
|
report_id: "{{ trigger.json.report_id | default('', true) }}"
|
|
trip_name: "{{ trigger.json.trip_name | default('', true) }}"
|
|
trip_destination: "{{ trigger.json.trip_destination | default('', true) }}"
|
|
milestone_miles: "{{ trigger.json.milestone_miles | default(0, true) | int(0) }}"
|
|
- conditions:
|
|
- condition: template
|
|
value_template: >-
|
|
{{ source == 'home_assistant_automation.vacation_house_status_digest'
|
|
and status == 'completed' }}
|
|
sequence:
|
|
- service: script.notify_engine
|
|
data:
|
|
title: Vacation digest review
|
|
value1: >-
|
|
{% set compact = (message if message | trim != '' else summary) | replace('\r', ' ') | replace('\n', ' ') | trim %}
|
|
{% if compact | length > 420 %}
|
|
{{ compact[:417] ~ '...' }}
|
|
{% else %}
|
|
{{ compact }}
|
|
{% endif %}
|
|
who: carlo
|
|
group: vacation
|
|
level: active
|
|
- conditions:
|
|
- condition: template
|
|
value_template: "{{ severity in ['warning', 'error', 'critical'] or status in ['failed', 'canceled'] }}"
|
|
sequence:
|
|
- service: script.notify_engine
|
|
data:
|
|
title: Joanna Alert
|
|
value1: >-
|
|
{{ summary }}
|
|
{% if job_id | trim != '' %}
|
|
(job={{ job_id }})
|
|
{% endif %}
|
|
{% if run_id | trim != '' %}
|
|
run={{ run_id }}
|
|
{% endif %}
|
|
{{ '\n' ~ message if message | trim != '' else '' }}
|