Track Your Hive Witness in Home Assistant: Rank, Votes, Stake, Power Down & Portfolio Value
If you run a Hive witness and already use Home Assistant at home, you can add a dashboard card that keeps an eye on your witness and your wallet. No custom integrations, no HACS, and no API keys. It uses Home Assistant's built-in REST and template features, Hive's public API nodes, and CoinGecko for prices.
What the card shows
Witness
- Witness rank (active witnesses only, so disabled witnesses are skipped)
- Total witness votes received (in MHP)
- Missed blocks
Stake
- Your own Hive Power, and your effective HP after delegations
- HP delegated out and delegated in
- How many witness votes you've cast
Power down
- Whether a power down is running
- Weekly payout and amount remaining
- Countdown to the next payout
Wallet & portfolio
- Liquid and savings HIVE and HBD
- Live HIVE and HBD prices
- Total portfolio value (liquid HIVE + savings + own HP + HBD; HP delegated to you isn't counted)
Prices are in GBP (£) by default. Changing currency is covered further down.
How it works
There are two parts:
- A sensor package (
hive.yaml). This calls the Hive API (condenser_api.get_accounts,get_dynamic_global_properties,get_witness_by_accountandget_witnesses_by_vote) and CoinGecko, then turns VESTS into HP for you. - A dashboard card. A plain Lovelace card that displays those sensors.
How often it updates:
- Account data: every 5 minutes
- Witness data: every 10 minutes
- Rank: every 15 minutes
- Prices: every 10 minutes
- The VESTS → HP ratio: hourly
Step 1: Edit the sensor package
Copy the YAML below. Before saving it anywhere, find and replace YOUR_HIVE_ACCOUNT with your account name: lowercase, without the @. It appears 3 times.
###############################################################################
# HIVE BLOCKCHAIN PACKAGE for Home Assistant
#
# >>> FIND & REPLACE every "YOUR_HIVE_ACCOUNT" with your account name
# (lowercase, no @). It appears 3 times in this file.
# >>> Fiat currency is GBP. To change, replace "gbp" (3 places) and "£".
#
# Install: save as /config/packages/hive.yaml and make sure
# configuration.yaml contains:
#
# homeassistant:
# packages: !include_dir_named packages
#
# Then restart Home Assistant (a YAML reload is not enough for new REST sensors).
###############################################################################
rest:
# ---------------------------------------------------------------- Account
- resource: https://api.hive.blog
method: POST
headers:
Content-Type: application/json
payload: '{"jsonrpc":"2.0","method":"condenser_api.get_accounts","params":[["YOUR_HIVE_ACCOUNT"]],"id":1}'
scan_interval: 300
timeout: 20
sensor:
- name: Hive Liquid HIVE
unique_id: hive_liquid_hive
unit_of_measurement: HIVE
state_class: measurement
value_template: "{{ value_json.result[0].balance.split(' ')[0] | float(0) }}"
- name: Hive Savings HIVE
unique_id: hive_savings_hive
unit_of_measurement: HIVE
state_class: measurement
value_template: "{{ value_json.result[0].savings_balance.split(' ')[0] | float(0) }}"
- name: Hive Liquid HBD
unique_id: hive_liquid_hbd
unit_of_measurement: HBD
state_class: measurement
value_template: "{{ value_json.result[0].hbd_balance.split(' ')[0] | float(0) }}"
- name: Hive Savings HBD
unique_id: hive_savings_hbd
unit_of_measurement: HBD
state_class: measurement
value_template: "{{ value_json.result[0].savings_hbd_balance.split(' ')[0] | float(0) }}"
- name: Hive Vests Own
unique_id: hive_vests_own
unit_of_measurement: VESTS
value_template: "{{ value_json.result[0].vesting_shares.split(' ')[0] | float(0) }}"
- name: Hive Vests Delegated Out
unique_id: hive_vests_delegated_out
unit_of_measurement: VESTS
value_template: "{{ value_json.result[0].delegated_vesting_shares.split(' ')[0] | float(0) }}"
- name: Hive Vests Received
unique_id: hive_vests_received
unit_of_measurement: VESTS
value_template: "{{ value_json.result[0].received_vesting_shares.split(' ')[0] | float(0) }}"
- name: Hive Powerdown Rate Vests
unique_id: hive_powerdown_rate_vests
unit_of_measurement: VESTS
value_template: "{{ value_json.result[0].vesting_withdraw_rate.split(' ')[0] | float(0) }}"
- name: Hive Powerdown Remaining Vests
unique_id: hive_powerdown_remaining_vests
unit_of_measurement: VESTS
# to_withdraw / withdrawn are raw integers (VESTS x 1,000,000)
value_template: >
{% set a = value_json.result[0] %}
{{ ((a.to_withdraw | int(0)) - (a.withdrawn | int(0))) / 1000000 }}
- name: Hive Next Powerdown Raw
unique_id: hive_next_powerdown_raw
value_template: "{{ value_json.result[0].next_vesting_withdrawal }}"
- name: Hive Witness Votes Cast
unique_id: hive_witness_votes_cast
unit_of_measurement: votes
value_template: "{{ value_json.result[0].witness_votes | length }}"
- name: Hive Witness Proxy
unique_id: hive_witness_proxy
value_template: "{{ value_json.result[0].proxy if value_json.result[0].proxy else 'none' }}"
# ------------------------------------------------- Global props (VESTS -> HP)
- resource: https://api.hive.blog
method: POST
headers:
Content-Type: application/json
payload: '{"jsonrpc":"2.0","method":"condenser_api.get_dynamic_global_properties","params":[],"id":1}'
scan_interval: 3600
timeout: 20
sensor:
- name: Hive Per MVests
unique_id: hive_per_mvests
unit_of_measurement: HIVE
value_template: >
{% set fund = value_json.result.total_vesting_fund_hive.split(' ')[0] | float(0) %}
{% set shares = value_json.result.total_vesting_shares.split(' ')[0] | float(1) %}
{{ (fund / shares * 1000000) | round(6) }}
# ------------------------------------------------------- Witness details
- resource: https://api.hive.blog
method: POST
headers:
Content-Type: application/json
payload: '{"jsonrpc":"2.0","method":"condenser_api.get_witness_by_account","params":["YOUR_HIVE_ACCOUNT"],"id":1}'
scan_interval: 600
timeout: 20
sensor:
- name: Hive Witness Votes Vests
unique_id: hive_witness_votes_vests
unit_of_measurement: VESTS
# "votes" is raw VESTS x 1,000,000
value_template: "{{ (value_json.result.votes | int(0) / 1000000) if value_json.result else 0 }}"
json_attributes_path: "$.result"
json_attributes:
- total_missed
- last_confirmed_block_num
- running_version
- url
- signing_key
- name: Hive Witness Missed Blocks
unique_id: hive_witness_missed_blocks
unit_of_measurement: blocks
value_template: "{{ value_json.result.total_missed if value_json.result else 0 }}"
# ---------------------------------------- Witness rank (active witnesses only)
- resource: https://api.hive.blog
method: POST
headers:
Content-Type: application/json
payload: '{"jsonrpc":"2.0","method":"condenser_api.get_witnesses_by_vote","params":["",300],"id":1}'
scan_interval: 900
timeout: 30
sensor:
- name: Hive Witness Rank
unique_id: hive_witness_rank
# Disabled witnesses (null signing key) are skipped, so the rank matches
# the "active" rank shown on most Hive witness pages.
value_template: >
{% set ns = namespace(i=0, rank=0) %}
{% for w in value_json.result %}
{% if w.signing_key != 'STM1111111111111111111111111111111114T1Anm' %}
{% set ns.i = ns.i + 1 %}
{% if w.owner == 'YOUR_HIVE_ACCOUNT' %}{% set ns.rank = ns.i %}{% endif %}
{% endif %}
{% endfor %}
{{ ns.rank if ns.rank > 0 else '>' ~ ns.i }}
# ---------------------------------------------------------------- Prices
- resource: https://api.coingecko.com/api/v3/simple/price?ids=hive,hive_dollar&vs_currencies=gbp
scan_interval: 600
timeout: 20
sensor:
- name: Hive Price
unique_id: hive_price
unit_of_measurement: "£"
state_class: measurement
value_template: "{{ value_json.hive.gbp | float(0) }}"
- name: HBD Price
unique_id: hbd_price
unit_of_measurement: "£"
state_class: measurement
value_template: "{{ value_json.hive_dollar.gbp | float(0) }}"
template:
- sensor:
- name: Hive Power Own
unique_id: hive_power_own
unit_of_measurement: HP
state_class: measurement
state: >
{{ (states('sensor.hive_vests_own') | float(0)
* states('sensor.hive_per_mvests') | float(0) / 1000000) | round(3) }}
- name: Hive Power Effective
unique_id: hive_power_effective
unit_of_measurement: HP
state_class: measurement
state: >
{% set v = states('sensor.hive_vests_own') | float(0)
- states('sensor.hive_vests_delegated_out') | float(0)
+ states('sensor.hive_vests_received') | float(0) %}
{{ (v * states('sensor.hive_per_mvests') | float(0) / 1000000) | round(3) }}
- name: Hive Power Delegated Out
unique_id: hive_power_delegated_out
unit_of_measurement: HP
state: >
{{ (states('sensor.hive_vests_delegated_out') | float(0)
* states('sensor.hive_per_mvests') | float(0) / 1000000) | round(3) }}
- name: Hive Power Received
unique_id: hive_power_received
unit_of_measurement: HP
state: >
{{ (states('sensor.hive_vests_received') | float(0)
* states('sensor.hive_per_mvests') | float(0) / 1000000) | round(3) }}
- name: Hive Powerdown Weekly
unique_id: hive_powerdown_weekly
unit_of_measurement: HIVE
state: >
{{ (states('sensor.hive_powerdown_rate_vests') | float(0)
* states('sensor.hive_per_mvests') | float(0) / 1000000) | round(3) }}
- name: Hive Powerdown Remaining
unique_id: hive_powerdown_remaining
unit_of_measurement: HIVE
state: >
{{ (states('sensor.hive_powerdown_remaining_vests') | float(0)
* states('sensor.hive_per_mvests') | float(0) / 1000000) | round(3) }}
- name: Hive Powerdown Active
unique_id: hive_powerdown_active
state: "{{ 'Yes' if states('sensor.hive_powerdown_rate_vests') | float(0) > 0 else 'No' }}"
icon: mdi:arrow-down-bold-circle-outline
- name: Hive Next Powerdown
unique_id: hive_next_powerdown
device_class: timestamp
availability: >
{{ states('sensor.hive_next_powerdown_raw') not in ['unknown', 'unavailable', '']
and not states('sensor.hive_next_powerdown_raw').startswith('1969') }}
state: "{{ states('sensor.hive_next_powerdown_raw') ~ '+00:00' }}"
- name: Hive Witness Votes HP
unique_id: hive_witness_votes_hp
unit_of_measurement: HP
state_class: measurement
state: >
{{ (states('sensor.hive_witness_votes_vests') | float(0)
* states('sensor.hive_per_mvests') | float(0) / 1000000) | round(0) }}
- name: Hive Witness Votes MHP
unique_id: hive_witness_votes_mhp
unit_of_measurement: MHP
state: "{{ (states('sensor.hive_witness_votes_hp') | float(0) / 1000000) | round(2) }}"
- name: Hive Portfolio Value
unique_id: hive_portfolio_value
unit_of_measurement: "£"
device_class: monetary
state: >
{% set hive = states('sensor.hive_liquid_hive') | float(0)
+ states('sensor.hive_savings_hive') | float(0)
+ states('sensor.hive_power_own') | float(0) %}
{% set hbd = states('sensor.hive_liquid_hbd') | float(0)
+ states('sensor.hive_savings_hbd') | float(0) %}
{{ (hive * states('sensor.hive_price') | float(0)
+ hbd * states('sensor.hbd_price') | float(0)) | round(2) }}
Step 2: Add the package to Home Assistant
How you do this depends on how Home Assistant is installed. If Settings has an Add-ons or Apps section, you're on Home Assistant OS: use Option A or B. If you see "Apps require the Home Assistant Operating System", you're on Docker/Container or Core: use Option C.
Option A: Home Assistant OS with the File editor app
- Go to Settings → Apps (called Add-ons on older versions) and open the store. Search for File editor, then install and start it. Switch on Show in sidebar.
- Open File editor from the sidebar. You'll be in the
/configfolder. - Click the folder icon (top left), then the new folder icon, and create a folder called
packages. - Open
packages, click the new file icon and name the filehive.yaml. - Paste in the YAML from Step 1, with your account name in it, and click Save.
- Go back to
/configand openconfiguration.yaml(see Step 3).
Option B: Home Assistant OS with the Studio Code Server app
If you'd rather use a VS Code-style editor:
- Go to Settings → Apps → store, then install and start Studio Code Server. Switch on Show in sidebar.
- Open it from the sidebar. In the file explorer, right-click the root of
/config→ New Folder →packages. - Right-click
packages→ New File →hive.yaml. Paste in the YAML and save (Ctrl+S). - Open
configuration.yamlfrom the explorer (see Step 3).
Option C: Docker / Container (or Core) by hand
There are no apps on these installs, so you edit the files directly on the host.
1. Find your config folder. It's the host folder mapped to /config in the container, the one containing configuration.yaml. In docker-compose.yml it's the line that looks like - /some/path:/config. If you're not sure, run this on the host:
docker inspect homeassistant --format '{{ range .Mounts }}{{ .Source }} -> {{ .Destination }}{{ println }}{{ end }}'
The path on the left of -> /config is your folder. Change homeassistant if your container has a different name. On a Core install the folder is usually ~/.homeassistant.
2. Create the package file:
cd /path/to/your/config
mkdir -p packages
nano packages/hive.yaml
Paste in the YAML. To save and exit, press Ctrl+O, Enter, then Ctrl+X.
3. Open configuration.yaml:
nano configuration.yaml
Then follow Step 3 below.
If your config folder is on a NAS or shared over the network, you can also do all of this in a normal desktop text editor.
Step 3: Turn on packages in configuration.yaml
Add these two lines to configuration.yaml:
homeassistant:
packages: !include_dir_named packages
⚠️ If there's already a homeassistant: line in the file, don't add a second one. Put the packages: line, indented, underneath the existing one.
Step 4: Check and restart
- Go to Developer tools → YAML → Check configuration. It should report the configuration as valid.
- Do a full restart. A YAML reload won't load new REST sensors.
- HA OS: Settings → System → ⋮ (top right) → Restart Home Assistant
- Docker:
docker restart homeassistant
Give it a minute or two for the sensors to fill in.
Step 5: Add the dashboard card
- Open your dashboard and click the pencil icon (top right) to edit.
- Click + Add card, scroll to the bottom and pick Manual.
- Delete everything already in the box, paste the YAML below, then click Save.
type: vertical-stack
cards:
- type: glance
title: HIVE Witness
columns: 3
entities:
- entity: sensor.hive_witness_rank
name: Rank
icon: mdi:podium
- entity: sensor.hive_witness_votes_mhp
name: Votes
icon: mdi:vote
- entity: sensor.hive_witness_missed_blocks
name: Missed
icon: mdi:cube-off-outline
- type: entities
entities:
- type: section
label: Stake
- entity: sensor.hive_power_own
name: Hive Power (own)
icon: mdi:lightning-bolt
- entity: sensor.hive_power_effective
name: Effective HP
icon: mdi:lightning-bolt-circle
- entity: sensor.hive_power_delegated_out
name: Delegated out
icon: mdi:arrow-right-bold
- entity: sensor.hive_power_received
name: Delegated in
icon: mdi:arrow-left-bold
- entity: sensor.hive_witness_votes_cast
name: Witness votes cast
icon: mdi:account-check
- type: section
label: Power Down
- entity: sensor.hive_powerdown_active
name: Powering down
- entity: sensor.hive_powerdown_weekly
name: Weekly payout
icon: mdi:calendar-week
- entity: sensor.hive_powerdown_remaining
name: Remaining
icon: mdi:progress-download
- entity: sensor.hive_next_powerdown
name: Next payout
format: relative
icon: mdi:clock-outline
- type: section
label: Wallet
- entity: sensor.hive_liquid_hive
name: HIVE (liquid)
icon: mdi:wallet
- entity: sensor.hive_savings_hive
name: HIVE (savings)
icon: mdi:piggy-bank
- entity: sensor.hive_liquid_hbd
name: HBD (liquid)
icon: mdi:currency-usd
- entity: sensor.hive_savings_hbd
name: HBD (savings)
icon: mdi:piggy-bank-outline
- entity: sensor.hive_price
name: HIVE price
icon: mdi:chart-line
- entity: sensor.hbd_price
name: HBD price
icon: mdi:chart-line-variant
- type: entity
entity: sensor.hive_portfolio_value
name: Portfolio Value
icon: mdi:cash-multiple
💡 Getting a configuration error? The Manual card starts with some placeholder text, often including a type: line. If you paste underneath it instead of replacing it, you end up with two type: lines and Home Assistant rejects the card. Clear the box completely first. If you still get an error, removing the type: vertical-stack line also fixes it.
Customising
- Currency: in
hive.yaml, replacegbp(3 places) withusd,eurand so on, and£with your symbol. - API node: the package uses
api.hive.blog. If that's slow for you, replace eachresource: https://api.hive.blogwith another node, such ashttps://api.deathwing.meorhttps://api.openhive.network. Or run it against your own node, since you're a witness 😉 - Rank: this counts active witnesses only. If you're outside the top 300, it shows something like
>290. - Not a witness? The card still works for any account. The witness rows just show 0.
Troubleshooting
- Sensors show "unavailable": check Settings → System → Logs for errors mentioning
restorhive. The usual cause is a typo in the account name, orYOUR_HIVE_ACCOUNTleft in one of the 3 places. - Entities not found on the card: if you already had sensors with the same names, Home Assistant may have added
_2to the new ones. Check Settings → Devices & services → Entities, search for "hive" and update the card to match.
Ideas for next steps
- Add a history graph of witness votes or HIVE price.
- Create an automation to notify you when your rank changes or missed blocks go up.
Questions and improvements are welcome in the comments 👇


