###################################################################### # @CCOSTAN - Follow Me on X # For more info visit https://www.vcloudinfo.com/click-here # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # OneNote Indexer Monitoring - Status and health sensors for Joanna # Polls codex_appliance OneNote status and exposes trigger-ready health entities. # ------------------------------------------------------------------- # Notes: Keep onenote indexer monitoring in this package (separate from bearclaw transport). # Notes: last_status='never' is treated as success only when index health is confirmed. # Notes: Only explicit last_status='error' is treated as failure; unknown/unavailable are neutral. # Notes: HA->Joanna request includes trigger context so Telegram progress messages can identify origin. # Notes: Creates/clears a Spook Repair issue and requests Joanna remediation on failures. # Notes: Index health requires pages, chunks, no pending embeddings, and a healthy embedding worker. # Notes: Recovery clear is polled so stale Repairs do not linger after the indexer recovers. # Notes: Daily Joanna recap should be plain-English; only surface detailed index metrics when something materially changes or fails. ###################################################################### sensor: - platform: rest name: OneNote Indexer Status Payload unique_id: onenote_indexer_status_payload resource: !secret bearclaw_onenote_status_url method: GET headers: x-codex-token: !secret bearclaw_token scan_interval: 300 timeout: 20 value_template: >- {% if value_json is mapping %} {{ value_json.server_time | default(now().isoformat()) }} {% else %} unknown {% endif %} json_attributes: - ok - indexer - embeddingWorker template: - sensor: - name: OneNote Indexer Last Job Status unique_id: onenote_indexer_last_job_status icon: mdi:notebook-check state: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set sync = payload.get('sync', {}) if payload is mapping else {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {% set worker = state_attr('sensor.onenote_indexer_status_payload', 'embeddingWorker') or {} %} {% set raw = (sync.get('last_status', '') | string | lower) %} {% set pages = index.get('pages') | int(0) %} {% set chunks = index.get('chunks') | int(0) %} {% set pending = index.get('pending_embeddings') | int(999999) %} {% set worker_status = worker.get('lastStatus', '') | string | lower %} {% set worker_running = worker.get('running', false) | bool %} {% set index_healthy = pages > 0 and chunks > 0 and pending == 0 and worker_status == 'ok' and not worker_running %} {% if raw in ['ok', 'success'] or (raw == 'never' and index_healthy) %} success {% elif raw == 'running' %} running {% elif raw == 'error' %} error {% elif raw == 'never' %} unknown {% else %} unknown {% endif %} attributes: running: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set sync = payload.get('sync', {}) if payload is mapping else {} %} {{ sync.get('running', false) | bool }} last_run_id: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set sync = payload.get('sync', {}) if payload is mapping else {} %} {{ sync.get('last_run_id') }} last_started_at: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set sync = payload.get('sync', {}) if payload is mapping else {} %} {{ sync.get('last_started_at') }} last_finished_at: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set sync = payload.get('sync', {}) if payload is mapping else {} %} {{ sync.get('last_finished_at') }} last_error: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set sync = payload.get('sync', {}) if payload is mapping else {} %} {{ sync.get('last_error') }} pending_embeddings: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {{ index.get('pending_embeddings') }} pages: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {{ index.get('pages') }} chunks: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {{ index.get('chunks') }} embedding_worker_status: >- {% set worker = state_attr('sensor.onenote_indexer_status_payload', 'embeddingWorker') or {} %} {{ worker.get('lastStatus') }} embedding_worker_last_run_at: >- {% set worker = state_attr('sensor.onenote_indexer_status_payload', 'embeddingWorker') or {} %} {{ worker.get('lastRunAt') }} last_metrics: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set sync = payload.get('sync', {}) if payload is mapping else {} %} {{ sync.get('last_metrics', {}) }} - binary_sensor: - name: OneNote Indexer Last Job Successful unique_id: onenote_indexer_last_job_successful state: >- {{ states('sensor.onenote_indexer_last_job_status') in ['success', 'running'] }} icon: >- {% if is_state('binary_sensor.onenote_indexer_last_job_successful', 'on') %} mdi:check-circle {% else %} mdi:alert-circle {% endif %} - name: OneNote Indexer Index Healthy unique_id: onenote_indexer_index_healthy state: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {% set worker = state_attr('sensor.onenote_indexer_status_payload', 'embeddingWorker') or {} %} {% set pages = index.get('pages') | int(0) %} {% set chunks = index.get('chunks') | int(0) %} {% set pending = index.get('pending_embeddings') | int(999999) %} {% set worker_status = worker.get('lastStatus', '') | string | lower %} {% set worker_running = worker.get('running', false) | bool %} {{ pages > 0 and chunks > 0 and pending == 0 and worker_status == 'ok' and not worker_running }} icon: >- {% if is_state('binary_sensor.onenote_indexer_index_healthy', 'on') %} mdi:notebook-check {% else %} mdi:notebook-remove {% endif %} attributes: pages: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {{ index.get('pages') }} chunks: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {{ index.get('chunks') }} pending_embeddings: >- {% set payload = state_attr('sensor.onenote_indexer_status_payload', 'indexer') or {} %} {% set index = payload.get('index', {}) if payload is mapping else {} %} {{ index.get('pending_embeddings') }} embedding_worker_status: >- {% set worker = state_attr('sensor.onenote_indexer_status_payload', 'embeddingWorker') or {} %} {{ worker.get('lastStatus') }} embedding_worker_last_run_at: >- {% set worker = state_attr('sensor.onenote_indexer_status_payload', 'embeddingWorker') or {} %} {{ worker.get('lastRunAt') }} - name: OneNote Indexer Job Failed unique_id: onenote_indexer_job_failed device_class: problem state: >- {{ is_state('sensor.onenote_indexer_last_job_status', 'error') }} automation: - id: onenote_indexer_daily_delete_maintenance alias: OneNote Indexer - Daily Delete Maintenance Request description: Ask Joanna daily to run duplicate cleanup and delete reconciliation for OneNote indexer. mode: single trigger: - platform: time at: "03:15:00" condition: - condition: template value_template: "{{ not (state_attr('sensor.onenote_indexer_last_job_status', 'running') | default(false) | bool) }}" action: - variables: trigger_context: "HA automation onenote_indexer_daily_delete_maintenance (OneNote Indexer - Daily Delete Maintenance Request)" - service: script.send_to_logbook data: topic: "ONENOTE" message: "Requesting daily duplicate cleanup + delete reconciliation run from Joanna." - service: script.joanna_dispatch data: trigger_context: "{{ trigger_context }}" source: "home_assistant_automation.onenote_indexer_daily_delete_maintenance" summary: "Run daily OneNote indexer duplicate cleanup and deletion reconciliation" entity_ids: - "sensor.onenote_indexer_status_payload" - "sensor.onenote_indexer_last_job_status" diagnostics: "scheduled_time=03:15:00" request: >- Run OneNote duplicate cleanup and apply deletions. Start with any dependency check needed before cleanup or delete reconciliation. If cleanup does not finish cleanly or index health is not confirmed, report back with: Headline: Cleanup did not finish cleanly; index health is not confirmed. Action: Retry cleanup after dependency check. Otherwise include delete reconciliation, then send a brief conversational morning summary. Tell me whether cleanup finished and whether anything actually changed. Only include detailed counts or backend metrics if pages were removed, something failed, or you need my attention. - id: onenote_indexer_failure_open_repair alias: OneNote Indexer - Open Repair On Failure description: Open a Spook Repair issue and ask Joanna to troubleshoot when indexer status is failed. mode: single trigger: - platform: state entity_id: binary_sensor.onenote_indexer_job_failed to: "on" for: "00:03:00" action: - variables: last_status: "{{ states('sensor.onenote_indexer_last_job_status') }}" run_id: "{{ state_attr('sensor.onenote_indexer_last_job_status', 'last_run_id') | default('n/a') }}" last_error: "{{ state_attr('sensor.onenote_indexer_last_job_status', 'last_error') | default('n/a') }}" last_metrics: "{{ state_attr('sensor.onenote_indexer_last_job_status', 'last_metrics') | default({}) }}" issue_id: "onenote_indexer_job_failed" trigger_context: "HA automation onenote_indexer_failure_open_repair (OneNote Indexer - Open Repair On Failure)" - service: repairs.create data: issue_id: "{{ issue_id }}" title: "OneNote indexer job failed" severity: "warning" persistent: true description: >- Home Assistant detected OneNote indexer failure. last_status: {{ last_status }} last_run_id: {{ run_id }} last_error: {{ last_error }} last_metrics: {{ last_metrics }} - service: script.send_to_logbook data: topic: "ONENOTE" message: >- OneNote indexer failed (run {{ run_id }}). Spook repair opened and Joanna remediation requested. - service: script.joanna_dispatch data: trigger_context: "{{ trigger_context }}" source: "home_assistant_automation.onenote_indexer_failure_open_repair" summary: "OneNote indexer job reported failure" entity_ids: - "binary_sensor.onenote_indexer_job_failed" - "sensor.onenote_indexer_last_job_status" diagnostics: >- issue_id={{ issue_id }}, last_status={{ last_status }}, last_run_id={{ run_id }}, last_error={{ last_error }}, last_metrics={{ last_metrics }} request: "Troubleshoot and resolve the indexer failure if possible." - id: onenote_indexer_failure_clear_repair alias: OneNote Indexer - Clear Repair On Recovery description: Clear the Spook Repair issue when OneNote indexer and index health are confirmed healthy again. mode: single trigger: - platform: state entity_id: binary_sensor.onenote_indexer_job_failed to: "off" for: "00:02:00" - platform: state entity_id: binary_sensor.onenote_indexer_index_healthy to: "on" for: "00:02:00" - platform: time_pattern minutes: "20" - platform: time_pattern minutes: "50" condition: - condition: state entity_id: binary_sensor.onenote_indexer_job_failed state: "off" - condition: state entity_id: binary_sensor.onenote_indexer_index_healthy state: "on" action: - service: repairs.remove continue_on_error: true data: issue_id: "onenote_indexer_job_failed" - service: script.send_to_logbook data: topic: "ONENOTE" message: "OneNote indexer and index health are confirmed healthy. Spook repair cleared."