Walt HelpOpen a case
Getting startedUsing WaltConnectionsAccount and privacy

Connect your own hub with a webhook

Connections

If your sensors and locks live on a hub Walt has no sign-in for, such as Hubitat, Z-Wave JS, Home Assistant or a script on a home server, the hub can send their battery and connection to Walt itself. Walt never reaches into your house, and the hub can never lock, unlock or change anything. It can only send readings.

Set it up

  1. Go to House → Sensors and tap + Connect sensors.
  2. Pick Webhook, give the hub a name you'll recognise, such as "Hubitat in the closet", and tap Make webhook token.
  3. Copy the token straight away. Walt shows it once and keeps only a scrambled copy, so nobody can read it back later, including you.
  4. Have the hub send a POST to https://heywalt.app/api/ingest/readings every few minutes, with the header Authorization: Bearer followed by the token.
The new token, with a Copy button beside it

To try a token before you set up the hub, send this from any computer's terminal, with your token in place of the example:

Shell
curl -i https://heywalt.app/api/ingest/readings \
  -H "Authorization: Bearer walt_hook_paste_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"devices": [{"id": "test-1", "name": "Test lock", "kind": "lock", "powerType": "cell", "reachable": true, "batteryLevel": 64}]}'

Walt answers 202 Accepted and a "Test lock" shows on House → Sensors. The hub's first real post replaces it, because each post is the whole list.

Each hub gets its own token. To see your hubs again, when each last posted, and to add or revoke one, open + Connect sensors, then the Webhook tile's ⋯ and Manage tokens.

What the hub sends

The body is JSON with a devices list. Each device is one object with these fields:

  • id: your hub's own id for the device, such as a Z-Wave node id. Keep it the same on every post.
  • name: what to call it, such as "Front Door". Optional.
  • kind: one of lock, contact, motion, leak, smoke_co, keypad, opener, hub, bulb, vent, room_sensor or other.
  • powerType: cell, rechargeable or wired.
  • reachable: true or false.
  • batteryLevel: 0 to 100. Optional.
  • batteryStatus: full, ok, low, failed, charging or none, for devices that report a word instead of a number. Optional.
  • temperatureF or temperatureC, and humidity: for sensors that measure them. Optional.

A lock at 64% battery looks like this:

JSON
{
  "devices": [
    {
      "id": "zwave-12",
      "name": "Front Door",
      "kind": "lock",
      "powerType": "cell",
      "reachable": true,
      "batteryLevel": 64
    }
  ]
}

Send every device on each post, up to 100. A device left out of a post is treated as removed.

Meters

A post can also carry a meters list, for running totals such as a circuit's energy use or the water line's gallons. Walt keeps the difference between posts. Each meter has:

  • id: your hub's own id for the meter. Keep it the same on every post.
  • name: what to call it, such as "Dryer circuit". Optional.
  • utility: electric, gas or water.
  • unit: for electric, kWh or Wh. For gas, therm, ccf, ft3 or m3. For water, gal, L, ft3 or m3.
  • total: the running total, in that unit. Send the counter itself, not the amount since the last post.
  • rate: what it is using right now, in watts for electric. Optional.
  • wholeHouse: true for the meter on the whole house, such as the panel or the gas meter. Optional.

A plug that has used 1,250 Wh and is drawing 300 W looks like this:

JSON
{
  "meters": [
    {
      "id": "plug-dryer",
      "name": "Dryer plug",
      "utility": "electric",
      "unit": "Wh",
      "total": 1250,
      "rate": 300
    }
  ]
}

A Shelly smart plug can send this by itself, with no hub: see Send a Shelly sensor straight to Walt.

Home Assistant

Home Assistant can send its devices' batteries and connection to Walt with a command and an automation that you paste in. Walt never gets a Home Assistant token, so it can't see or change anything in your house. You need Home Assistant 2024.10 or newer.

1. Make a token in Walt

  1. Go to House → Sensors, tap + Connect sensors and pick Home Assistant.
  2. Tap Make webhook token and copy the token straight away. Walt shows it once.

2. Label the devices for Walt

  1. In Home Assistant, open Settings → Areas, labels & zones → Labels and create a label called Walt.
  2. Open Settings → Devices & services → Devices, tick each device you want Walt to watch, such as locks, door sensors and leak sensors, and add the Walt label to them.

Walt reads each labelled device's battery, whether Home Assistant can still reach it, and its temperature and humidity if it measures them. It works out what kind of device it is from its entities: a lock, a door or window sensor, a motion, leak or smoke sensor, or a room sensor. Up to 100 devices.

3. Add the token and the command

Open your secrets.yaml and add this line, with your token after the word Bearer:

YAML
walt_token: "Bearer walt_hook_paste_your_token_here"

Then open your configuration.yaml and paste this at the end. You don't need to change any of it.

YAML
rest_command:
  walt_readings:
    url: https://heywalt.app/api/ingest/readings
    method: post
    headers:
      Authorization: !secret walt_token
    content_type: application/json
    payload: >-
      {% set ns = namespace(out=[]) %}
      {% for d in label_devices('walt')[:100] %}
        {% set n = namespace(kinds=[], level=none, low=none, bat=none, up=false, temp=none, unit=none, hum=none) %}
        {% for e in device_entities(d) %}
          {% set dom = e.split('.')[0] %}
          {% set dc = state_attr(e, 'device_class') %}
          {% set s = states(e) %}
          {% set live = s not in ['unavailable', 'unknown'] %}
          {% if live %}{% set n.up = true %}{% endif %}
          {% if dc == 'battery' %}
            {% set n.bat = live if n.bat is none else (n.bat or live) %}
            {% if dom == 'sensor' and s | is_number %}{% set n.level = [0, [s | float | round | int, 100] | min] | max %}{% endif %}
            {% if dom == 'binary_sensor' and live %}{% set n.low = s == 'on' %}{% endif %}
          {% endif %}
          {% if dom == 'lock' %}{% set n.kinds = n.kinds + ['lock'] %}{% endif %}
          {% if dom == 'binary_sensor' %}
            {% if dc in ['smoke', 'carbon_monoxide', 'gas'] %}{% set n.kinds = n.kinds + ['smoke_co'] %}{% endif %}
            {% if dc == 'moisture' %}{% set n.kinds = n.kinds + ['leak'] %}{% endif %}
            {% if dc in ['door', 'window', 'opening', 'garage_door'] %}{% set n.kinds = n.kinds + ['contact'] %}{% endif %}
            {% if dc in ['motion', 'occupancy', 'presence'] %}{% set n.kinds = n.kinds + ['motion'] %}{% endif %}
          {% endif %}
          {% if dom == 'sensor' and live and s | is_number %}
            {% if dc == 'temperature' and n.temp is none %}
              {% set n.temp = s | float %}{% set n.unit = state_attr(e, 'unit_of_measurement') %}
              {% set n.kinds = n.kinds + ['room_sensor'] %}
            {% endif %}
            {% if dc == 'humidity' and n.hum is none %}{% set n.hum = s | float %}{% endif %}
          {% endif %}
        {% endfor %}
        {% set kind = (['lock', 'smoke_co', 'leak', 'contact', 'motion', 'room_sensor'] | select('in', n.kinds) | first) or 'other' %}
        {% set dev = {
          'id': 'ha:' ~ d,
          'name': ((device_attr(d, 'name_by_user') or device_attr(d, 'name') or '') ~ '')[:120],
          'kind': kind,
          'powerType': 'wired' if n.bat is none else 'cell',
          'reachable': n.up if n.bat is none else n.bat,
        } %}
        {% if n.level is not none %}{% set dev = dict(dev, batteryLevel=n.level) %}
        {% elif n.low is not none %}{% set dev = dict(dev, batteryStatus='low' if n.low else 'ok') %}{% endif %}
        {% if n.unit == '°F' and -58 <= n.temp <= 158 %}{% set dev = dict(dev, temperatureF=n.temp) %}
        {% elif n.unit == '°C' and -50 <= n.temp <= 70 %}{% set dev = dict(dev, temperatureC=n.temp) %}{% endif %}
        {% if n.hum is not none and 0 <= n.hum <= 100 %}{% set dev = dict(dev, humidity=n.hum) %}{% endif %}
        {% set ns.out = ns.out + [dev] %}
      {% endfor %}
      {"devices": {{ ns.out | to_json }}}

Restart Home Assistant from Settings → System, so it reads the new command.

4. Send it every five minutes

Open Settings → Automations & scenes, tap Create automation, then Create new automation. Open the ⋮ menu at the top, pick Edit in YAML, and replace what is there with this:

YAML
alias: Send device health to Walt
description: Batteries and connection of every device labelled Walt.
mode: single
triggers:
  - trigger: time_pattern
    minutes: /5
  - trigger: homeassistant
    event: start
conditions:
  - condition: template
    value_template: "{{ label_devices('walt') | count > 0 }}"
actions:
  - action: rest_command.walt_readings
    response_variable: walt
  - if:
      - condition: template
        value_template: "{{ walt.status != 202 }}"
    then:
      - action: system_log.write
        data:
          level: warning
          message: "Walt answered {{ walt.status }}: {{ walt.content }}"

Tap Save. Within five minutes the devices show on House → Sensors.

If nothing shows up

Open Settings → System → Logs and search for Walt.

  • Walt answered 401 means the token in secrets.yaml is wrong or was revoked, or the word Bearer and its space are missing. Make a new token and paste it in.
  • Walt answered 400 means the post was refused. The message after it says which field.
  • No line at all means the automation has not run. Check that at least one device has the Walt label.

Good to know

  • To stop Walt watching a device, take the Walt label off it. Walt treats it as removed on the next post.
  • A device shows as offline in Walt when Home Assistant marks its battery unavailable, which is how Zigbee and Z-Wave report one that has stopped answering.
  • If Home Assistant itself stops posting for a while, Walt says so on the Sensors page.
  • Walt only reads. The token can send readings and nothing else, and Home Assistant never hands Walt a way back in.

Lost a token, or replacing a hub?

Make a new token for the hub, paste it in, then revoke the old one from its ⋯. The old token stops working the moment you revoke it.

Walt is not an alarm system

Walt shows battery and connection, never alarms. Even if your hub sends one, Walt drops it. Keep relying on your detectors and your hub's own alerts for leaks, smoke and carbon monoxide.