Home Assistant
REST sensors for the live wholesale price, the forecast, and tomorrow's cheap and expensive half-hours. Home Assistant does not import OpenAPI.
Put the key in secrets.yaml, never in configuration.yaml.
ecos_api_key: "Bearer ecos_live_your_key_here"
The word Bearer is part of the secret. If that secret is missing, Check Configuration fails on !secret ecos_api_key.
The URLs are real. Only the values are stubs: *poc_from_GET_me (your GXP) and *icp_from_GET_me (your ICP), from GET /me. Leave them and Home Assistant refuses to save (found undefined alias 'poc_from_GET_me'). Replace with your poc (for example HAY2201) and your quoted icp (for example "0000123456ABCDE").
Then either:
- Download home-assistant.yaml, replace those two values, and drop the file in
config/packages/if you use packages, or - Paste the snippets below into
configuration.yamlin this order — allrest:items together, thentemplate:.
Poll the live price every 5 minutes — faster than that returns the same number and spends your budget. Poll the forecast and tomorrow’s ranking every 30 minutes.
Need a key first? Overview.
Current price
rest:
- resource: https://api.ecosmart.co.nz/rest/v1/gxps/*poc_from_GET_me/spot
params:
poc: *poc_from_GET_me
scan_interval: 300
headers:
Authorization: !secret ecos_api_key
sensor:
- name: Spot price
unique_id: ecos_spot_price
value_template: "{{ value_json.priceCentsPerKwhInclGst | round(2) }}"
unit_of_measurement: "c/kWh"
state_class: measurement
availability: "{{ not value_json.isStale }}"
json_attributes:
- observedAt
- ageSeconds
- isStale
- priceDollarsPerMwh
availability uses isStale, so the sensor goes unavailable rather than quietly holding a stale price if the feed stops.
Forecast
A Home Assistant state is capped at 255 characters, so the state is the current half-hour and the rest of the curve lives in an attribute.
- resource: https://api.ecosmart.co.nz/rest/v1/gxps/*poc_from_GET_me/forecast
params:
hours: 24
poc: *poc_from_GET_me
scan_interval: 1800
headers:
Authorization: !secret ecos_api_key
sensor:
- name: Spot forecast
unique_id: ecos_spot_forecast
value_template: "{{ value_json.points[0].priceCentsPerKwhInclGst | round(2) }}"
unit_of_measurement: "c/kWh"
availability: "{{ value_json.points | length > 0 }}"
json_attributes:
- points
- coveredHours
- publishedAt
Tomorrow’s cheap / expensive half-hours
Scattered half-hours on tomorrow’s New Zealand trading date, not a consecutive block. Use this to fill a battery’s time-based schedule; use the template below if you want “the next cheap 90 minutes from now”.
This is another item in the same rest: list as the sensors above.
- resource: https://api.ecosmart.co.nz/rest/v1/windows
params:
icp: *icp_from_GET_me
n: 6
scan_interval: 1800
headers:
Authorization: !secret ecos_api_key
sensor:
- name: Tomorrow cheapest half-hour
unique_id: ecos_windows_cheap_start
value_template: >
{% if value_json.cheap is defined and value_json.cheap | length > 0 %}
{{ value_json.cheap[0]['startsAt'] }}
{% elif value_json.code is defined %}
{{ value_json.code }}
{% else %}
unknown
{% endif %}
json_attributes:
- cheap
- dear
- networkTou
- tradingDate
- indicative
- unavailableReason
- code
- message
Cheapest 90 minutes in the next 24 hours
A template over the forecast attribute. The state is the start time of the cheapest three consecutive half-hours.
template:
- sensor:
- name: Cheapest 90 minutes
unique_id: ecos_cheapest_window
state: >
{% set pts = state_attr('sensor.spot_forecast', 'points') or [] %}
{% if pts | length < 3 %}
unknown
{% else %}
{% set ns = namespace(best=None, cost=None) %}
{% for i in range(pts | length - 2) %}
{% set c = (pts[i].priceCentsPerKwhInclGst
+ pts[i+1].priceCentsPerKwhInclGst
+ pts[i+2].priceCentsPerKwhInclGst) / 3 %}
{% if ns.cost is none or c < ns.cost %}
{% set ns.cost = c %}
{% set ns.best = i %}
{% endif %}
{% endfor %}
{{ pts[ns.best].startsAt }}
{% endif %}
- binary_sensor:
- name: Spot is cheap
unique_id: ecos_spot_cheap
# Pick your own threshold — 10 c/kWh incl. GST is only an example.
state: "{{ states('sensor.spot_price') | float(999) < 10 }}"
points[0] is the half-hour in progress. If you want strictly future windows, drop the first point before ranking.
Reload Home Assistant (Developer Tools → YAML → REST entities, or a full restart) after saving.