Friday's Party: Creating a Private, Agentic AI using Voice Assistant tools

New tool RC:
REQUIRES 2025.6.x or better (recorder.get_statistics)

History CRUD - because someone went and made recorder.get_statistics and I could not be happier…

Yep it’s exactly what it say s it is - AI access to the history stats.

NOW - WARNING - this one’s a tool that’s a LOT open ended, it’s prerelease so the docs suck and - yes there are surprises. It will not be 100% but shouldn’t be destructive. I handled attempts to create, edit or delete history.

YOU WILL have to walk the LLM through creating the report you want, but if you know what’s in the db and your stats are actually there - she can see them. :slight_smile:

*chuckle h-Back… STT HATES HVAC with my voice, fortunately Friday tolerates it…

Now if I only had a way of saving the report spec… :smiling_imp:

(But that’s another show)

and, yes the repo is almost up. But I was working on a string of tool updates that honestly you reeeeeeeally want. So let me finish that refactor first.

History CRUD (YES they’re getting a new name soon)
Now with 100% more timelord:

alias: history CRUD (1.0.2-RC)
mode: parallel
fields:
  action_type:
    name: CRUD Action
    required: true
    default: help
    selector:
      select:
        options:
          - read
          - create
          - update
          - delete
          - help
  statistic_ids:
    description: List of sensor/statistic IDs to query
    selector:
      entity:
        multiple: true
    required: true
  start_time:
    description: ISO 8601 UTC timestamp
    selector:
      datetime: {}
  end_time:
    description: ISO 8601 UTC timestamp
    selector:
      datetime: {}
  period:
    description: Time grouping (5minute, hour, day, week, month)
    default: hour
    selector:
      select:
        options:
          - 5minute
          - hour
          - day
          - week
          - month
  types:
    description: List of statistical types to return (mean, sum, etc.)
    selector:
      select:
        multiple: true
        options:
          - change
          - last_reset
          - max
          - mean
          - min
          - state
          - sum
    name: types
  units:
    description: >-
      Optional unit conversion map (e.g. {"energy": "kWh"}); use if you need an
      output conversion such as Wh to kWh...
    selector:
      object: {}
  context:
    description: Optional label for trace/logging
    selector:
      text: {}
sequence:
  - variables:
      error_msgs: >-
        {%- set msgs = [] -%}

        {%- if statistic_ids is not defined or statistic_ids is none or
        statistic_ids == '' or statistic_ids == [] -%}
          {%- set _ = msgs.append("Missing required field: statistic_ids") -%}
        {%- endif -%}

        {%- if action_type == 'read' and (types is not defined or types is none
        or types == '' or types == []) -%}
          {%- set _ = msgs.append("Missing required field: types") -%}
        {%- endif -%}

        {{ msgs }}
      error_flag: "{{ error_msgs|count > 0 }}"
      query_payload: |-
        {% set q = {"statistic_ids": statistic_ids} %} {% set q = q | combine(
          start_time is defined and start_time != None and {"start_time": start_time} or {},
          end_time is defined and end_time != None and {"end_time": end_time} or {},
          period is defined and period != None and {"period": period} or {},
          types is defined and types and {"types": types} or {},
          units is defined and units and {"units": units} or {},
          recursive=True
        ) %} {{ q | to_json }}
      help_sample_outdoor: sensor.home_thermostat_outdoor_temperature
      help_sample_main_panel_consumption: sensor.main_panel_daily_consumption
      help_text:
        help: true
        action: read
        errors: "{{ error_msgs if error_flag else none }}"
        usage_notes:
          - Use 'read' to access historical statistics.
          - >-
            Only 'read' is supported. Other actions return structured errors. 
            It's History...
          - Specify sensor or statistic IDs to analyze.
          - Provide ISO start and end timestamps for range.
          - Select period grouping (daily, hourly, monthly, etc.).
          - Pick statistics types to return (mean, max, min, sum, etc.).
          - Data is returned grouped by period for trend analysis.
          - Retry or adjust parameters if data missing.
          - Works best on continuous numeric sensors with stats enabled.
          - Timezone-aware to ensure accurate day boundaries.
          - Can query multiple sensors simultaneously.
          - Useful for spotting usage patterns and anomalies.
        fields_reference:
          required_fields:
            - statistic_ids
          optional_fields:
            - start_time
            - end_time
            - period
            - types
            - units
            - context
          statistic_ids: List of entity statistic_ids to query (sensor.*)
          start_time: Start of the query range (ISO8601 format)
          end_time: End of the query range (ISO8601 format)
          period: Aggregation interval (5minute, hour, day, week, month)
          types: Types of statistics to fetch (mean, min, max, sum, state)
        cheat_sheet:
          description: >
            This cheat sheet provides structured guidance for using the
            `recorder.get_statistics` service in Home Assistant to query
            historical statistics data from the built-in database.
          example_use_cases:
            - id: daily_temp_summary
              description: >
                Get average daily temperature from a specific sensor for a time
                period. Uses the authoritative outdoor temp sensor if availabe
                (not null).
              statistic_ids: "{{ help_sample_outdoor }}"
              start_time: "2025-06-01T00:00:00"
              end_time: "2025-06-08T00:00:00"
              period: day
              types:
                - mean
            - id: daily_energy_usage_since_date
              description: >
                Retrieve daily total energy consumption for a main panel
                consumption since 1 Apr 2025. Note this sensor is 'always
                increasing' (from the class) so we use 'change' to get accurate
                daily use. Uses the authoritative omain consumptionsensor if
                available (not null).
              statistic_ids: "{{ help_sample_main_panel_consumption }}"
              start_time: "2025-04-01 00:00:00"
              period: day
              types:
                - change
              units:
                Wh: kWh
          notes:
            - >-
              the samples were provided for 'known good' references.  We
              understand that 'no data' is generally acceptable from this tool
              The sample cases were provided to show good example cases and
              indicate  REAL entites in this installation. If you try them and
              they DO NOT return data something may be in error.
            - >-
              Only sensors with long-term statistics enabled will return data.
              Some sensors just don't have it...  If we should turn on
              statistics for a specific reason suggest so.
            - >-
              Use 'change' to get the delta between start and end - the actual
              usage for period on always increasing sensors (most energy and
              water sensors) if the numbers seem absolutely absurd (as in
              millions of kWh, for instance) you probably got hold of an always
              increasing sensor and need to get the db to do the math for you.
          source:
            - https://www.home-assistant.io/integrations/recorder/#statistics
            - https://www.home-assistant.io/integrations/statistics/
  - choose:
      - conditions:
          - alias: ERROR - Required fields missing
            condition: template
            value_template: "{{ action_type == 'read' and error_flag }}"
        sequence:
          - variables:
              history_result: "{{ help_text }}"
          - stop: "Read error: Required fields missing"
            response_variable: history_result
      - conditions:
          - condition: template
            value_template: "{{ action_type == 'read' }}"
            alias: READ
        sequence:
          - action: recorder.get_statistics
            data: "{{ query_payload }}"
            response_variable: history_result
          - stop: Read complete
            response_variable: history_result
      - conditions:
          - condition: template
            value_template: "{{ action_type == 'create' }}"
            alias: CREATE
        sequence:
          - variables:
              history_result:
                error: true
                code: H-0001
                reason: Temporal Paradox
                detail: You cannot create history. You are not the Doctor.
          - stop: Create complete
            response_variable: history_result
      - conditions:
          - condition: template
            value_template: "{{ action_type == 'delete' }}"
            alias: DELETE
        sequence:
          - variables:
              history_result:
                error: true
                code: H-0002
                reason: Redaction Denied
                detail: This isn’t a reality show. You don’t get to delete the past.
          - stop: Delete complete
            response_variable: history_result
      - conditions:
          - condition: template
            value_template: "{{ action_type == 'update' }}"
            alias: UPDATE
        sequence:
          - variables:
              history_result:
                error: true
                code: H-0003
                reason: Zen Violation
                detail: >
                  You cannot rewrite history. You can, however, learn from it.
                  See: https://www.goodreads.com/quotes/tag/change
          - stop: Update complete
            response_variable: history_result
      - conditions:
          - condition: template
            value_template: "{{ action_type == 'help' }}"
            alias: HELP
        sequence:
          - variables:
              history_result: "{{help_text}}"
          - stop: Help complete
            response_variable: history_result
description: |-
  History Stats for Homeassistant
  Uses recorder.get_statistics for current entity stats.
  Beta
  Nest update planned target by label
  Read HELP for detailed use
  See HELP - cheat_sheet for detailed usage instructions and examples.

Also this is the new default pattern if anyone sees refinement please chime in. I’m not building for pretty I’m building for

AI tool use first (document the hell out of it, self descriptive, confirm null returns json safe descriptive, context rich with pointers and help.)