Using a Static JSON File in Home Assistant

Recently I found myself needing to bring some JSON from a file into a Home Assistant sensor. Specifically, the electricity rates for my power company are woefully out of date on OpenEI so I decided I could just maintain the data myself.

Home Assistant doesn't have a direct way to read JSON data from a file into a sensor. There's the File platform which has a promising name but is actually a trap. File is meant for use cases where something writes to, say, a CSV file continuously and you just want to read the most recent line. It specifically does not read the whole file.

After a lot of searching I came across the Command Line platform. The integration does a number of things, but for our purposes it lets you periodically run a command within the context of the Home Assistant container and bring the output back into Home Assistant as a sensor.

Let's say you have a JSON file named rate.json in your Home Assistant configuration directory:

{
  "name": "Base Rate",
  "rate": 0.15
}

You can bring that into a sensor with the following snippet in your configuration.yaml file:

command_line:
  - sensor:
    name: "Electrity Rate"
    command: 'cat rate.json',
    value_template: "{{ value_json['rate'] }}"
    unit_of_measurement: "USD/kWh",
    json_attributes:
      - name
      - rate

This config does a couple things. The command key specifies what command HA should run, in this case cat to read the file to stdout. value_template extracts the rate key from the file into the sensor's value. The json_attributes list pulls the list of keys into attributes in the sensor, which you can later access from a template using state_attr(). I have also specfied unit_of_measurement here just because the Energy reporting system needs that if you want to use this as an input.

So, the above is great if you have one static set of attributes to bring in, but sensor values can be at most 255 characters. What if you have a bigger file that you need to pull just a little data out of?

Let's say we have this slightly bigger file rates.json:

[
  {
    "id": "d1-11_summer_on_peak",
    "name": "Summer Peak",
    "months": [6, 7, 8, 9],
    "days": [1, 2, 3, 4, 5],
    "hours": [15, 16, 17, 18],
    "rate": 0.23525,
    "peak": true
  },
  {
    "id": "d1-11_summer_off_peak",
    "name": "Summer Off-Peak",
    "months": [6, 7, 8, 9],
    "days": [0, 1, 2, 3, 4, 5, 6],
    "hours": [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
    "rate": 0.17859,
    "peak": false
  },
  {
    "id": "d1-11_winter_on_peak",
    "name": "Winter Peak",
    "months": [1, 2, 3, 4, 5, 10, 11, 12],
    "days": [1, 2, 3, 4, 5],
    "hours": [15, 16, 17, 18],
    "rate": 0.17879,
    "peak": true
  },
  {
    "id": "d1-11_winter_off_peak",
    "name": "Winter Off-Peak",
    "months": [1, 2, 3, 4, 5, 10, 11, 12],
    "days": [0, 1, 2, 3, 4, 5, 6],
    "hours": [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
    "rate": 0.1658,
    "peak": false
  }
]

This file describes the rate structure that's in effect at my house, DTE rate D1.11. Each entry in the array is a rate, and the first matching rate based on month, weekday, and hour is the effective rate.

Ordinarily one might reach for a HACS integration or something, but the Home Assistant container has another trick up it's sleeve: it bundles jq.

jq is a tool for querying and manipulating JSON streams. This essay isn't meant to be a jq tutorial so we're not going to go in depth into what this query in rate_filter.jq does, but in broad strokes it picks the first matching rate from the input file and extracts just the name, rate, and peak keys.

map(select(
    (.months[] | contains($ARGS.positional[0] | tonumber))
    and (.days[] | contains($ARGS.positional[1] | tonumber))
    and (.hours[] | contains($ARGS.positional[2] | tonumber))
))[0] | {name, rate, peak}

Here's a modified command line sensor that runs jq appropriately:

command_line:
  - sensor:
      name: "DTE Rate"
      command: 'jq -f rate_filter.jq rates.json --args {{ now().month }} {{ now().isoweekday() % 7 }} {{ now().hour }}'
      value_template: "{{ value_json['rate'] }}"
      unit_of_measurement: USD/kWh
      json_attributes:
        - name
        - rate
        - peak

The relevant change here is to the command key, which now invokes jq with the -f argument to pass the filter as a file rather than trying to quote everything properly within HA, then passes the actual rates.json file, then treats the rest of the arguments as positional args. These are accessed within rate_filter.jq as $ARGS.positional[0] etc.

With this set up I can access the current electric rate within my Home Assistant in a way that is compatible with the Energy dashboard, which is completely local, and which should be easy to maintain in the future.

The above isn't at all specific to electric rates, by the way. This technique should work for any data that you need in HA but is more complicated than a plain input can work with.