Data & sensors
Where EOS Connect reads your house from — the home-automation connection, the sensors it measures consumption with, and the plain-array format for anything it has no integration for.
What this is for
- I want EOS Connect to read my house — what it draws, what the battery is doing — from Home Assistant or openHAB.
- I want to hand it numbers it has no integration for, as a plain array.
- My history has gaps and I want to know what it does with them.
Two settings groups live here: data_source.*, the connection to your
home automation, and load.*, the sensors it reads your household
consumption from.
Where the measurements come from
One connection, used for every sensor you name anywhere in EOS Connect. Home Assistant and openHAB are read over their REST APIs; see Home Assistant and openHAB for what each needs at its end, and Testing one sensor for checking an entity before you save it.
Household consumption
The load forecast is built from what the same weekday drew 7 and 14 days ago, so these sensors are what every plan is measured against. Appliances that forecast cannot follow belong under Managed loads instead — including the two settings that bound them all together, which are in this group.
Data quality and gaps
Optimization needs a complete array of values for every slot in the horizon. Real sensor history has holes — a restart, a network blip, an integration that stopped recording.
EOS Connect detects incomplete data and forward-fills it, carrying the last known value across the gap rather than passing a null through to the optimizer. The log names what it filled, so a persistent gap is visible instead of silently degrading your plan.
If a sensor is missing entirely, EOS Connect falls back to a built-in default and says so in the log — it never quietly polls an entity you did not configure. Use the Test button beside any sensor field to read it and see the current value.
Unified Timeseries Data Source Guide
EOS Connect supports a unified timeseries source for both electricity prices and PV forecasts. It fetches data from Home Assistant, custom HTTP APIs, or any endpoint that returns timeseries data in the expected format.
The expected format is EVCC's format. That is deliberate: it is the format
you most likely already have. An EVCC endpoint such as
/api/tariff/grid can be used as-is, and a Home Assistant template sensor
written to feed EVCC works unchanged. Home Assistant is the transport here
(state API, entity attributes, bearer token) — the format comes from EVCC.
EOS Connect accepts exactly this one format and does not try to guess foreign attribute names. Where an integration uses different names, you shape it once with a Home Assistant template sensor — see Home Assistant Template Snippets for ready-made examples.
value now matches what EVCC and the common Home Assistant
integrations actually deliver: EUR/kWh for prices and W
for PV. Previously EOS Connect expected EUR/Wh and Wh-per-slot — units no real source
provides.
If you already run a
timeseries source, check it after updating: set
price.value_unit / pv_forecast_source.value_unit to whatever your
source actually delivers. Selecting EUR/Wh resp. Wh restores the
previous behaviour exactly. Use
Test connection to see the resulting values before
saving.
Timeseries Data Format
The timeseries source expects JSON data in this format:
{
"data": [
{
"start": "2026-08-23T00:00:00+02:00",
"end": "2026-08-23T00:15:00+02:00",
"value": 0.3811
},
{
"start": "2026-08-23T00:15:00+02:00",
"end": "2026-08-23T00:30:00+02:00",
"value": 0.3727
}
]
}
| Field | Description | Format |
|---|---|---|
| start | Required. Start timestamp of the timeslot. Always include the UTC offset — a timestamp without one is read as local time, which silently shifts every slot. | ISO8601 (e.g., "2026-08-23T00:00:00+02:00" or "…Z"), or Unix timestamp |
| end | Optional. If absent, EOS Connect derives it from the next entry's
start (and the last entry from the preceding interval), so a source
that only publishes start times is accepted. |
ISO8601 or Unix timestamp |
| value | Required. For Prices: price per slot, by default in EUR/kWh (e.g. 0.3811 for 38.11 ct/kWh) — as EVCC's rates deliver it.For PV: generated power per slot, by default in W — as EVCC's solar forecast delivers it. Use value_unit if your source uses a different unit.
|
Number (float) |
Value Unit
If your source does not use the default unit, select the matching one instead of building a conversion into your template:
| Setting | Options | Default |
|---|---|---|
price.value_unit |
EUR/kWh, ct/kWh, EUR/Wh |
EUR/kWh |
pv_forecast_source.value_unit |
W, kW, Wh, kWh |
W |
W and kW are power readings and are integrated over the slot
length — 4000 W across a 15-minute slot becomes 1000 Wh. Wh and
kWh are taken as the energy of that slot directly. Picking the wrong one is a
factor-of-four error on 15-minute data, so use
Test connection if you are unsure.
null value, an unparseable timestamp, a missing field —
the whole payload is rejected and EOS Connect keeps using the last good data. Skipping just
the bad entry would shift every following hourly price by one slot, which is not visible
anywhere in the UI. The log names the offending entry by index.
Home Assistant Integration
Home Assistant entity attributes can be accessed using the dot notation in data_path.
Price Example (Home Assistant)
| Configuration | Value |
|---|---|
price.source |
timeseries |
price.data_url |
http://homeassistant.local:8123/api/states/sensor.grid_prices |
price.data_path |
attributes.data |
price.data_token |
Your HA Long-Lived Access Token (optional if local) |
HA Entity Response Example:
{
"entity_id": "sensor.grid_prices",
"attributes": {
"data": [
{"start": "2024-06-12T14:00:00Z", "end": "2024-06-12T15:00:00Z", "value": 0.25},
{"start": "2024-06-12T15:00:00Z", "end": "2024-06-12T16:00:00Z", "value": 0.28}
]
}
}
PV Forecast Example (Home Assistant)
| Configuration | Value |
|---|---|
pv_forecast_source.source |
timeseries |
pv_forecast_source.data_url |
http://homeassistant.local:8123/api/states/sensor.solar_forecast |
pv_forecast_source.data_path |
attributes.forecast |
pv_forecast_source.data_token |
Your HA Long-Lived Access Token (optional if local) |
Custom HTTP API Integration
Connect to custom HTTP endpoints that return timeseries data. Supports authentication via Bearer tokens.
Example: Custom REST API
| Configuration | Value |
|---|---|
price.source |
timeseries |
price.data_url |
https://api.example.com/v1/prices/current |
price.data_path |
prices (or nested path like result.data) |
price.data_token |
Your API token (if required) |
API Response Example:
{
"prices": [
{"start": "2024-06-12T14:00:00Z", "end": "2024-06-12T15:00:00Z", "value": 0.25},
{"start": "2024-06-12T15:00:00Z", "end": "2024-06-12T16:00:00Z", "value": 0.28}
]
}
Home Assistant Template Snippets
Home Assistant integrations name their attributes differently — Tibber and EPEX use
start_time and price_per_kwh, Solcast uses
period_start and pv_estimate. EOS Connect does not guess these
names. Instead you publish one template sensor in the expected format; adapting to a
different provider then means editing the template, not reconfiguring EOS Connect.
Because end is derived automatically, the templates only need
start and value.
Prices: Tibber Price Information / EPEX Spot (HACS)
Both provide a data attribute with start_time and
price_per_kwh (EUR/kWh):
template:
- trigger:
- platform: time_pattern
minutes: "/5"
sensor:
- name: eos_grid_prices
state: "ok"
attributes:
data: >
{% set src = state_attr('sensor.YOUR_PRICE_SENSOR', 'data') %}
[
{%- for e in src %}
{"start": "{{ e.start_time }}", "value": {{ e.price_per_kwh }}}
{%- if not loop.last %},{% endif %}
{%- endfor %}
]
Then set price.data_url to
http://homeassistant.local:8123/api/states/sensor.eos_grid_prices,
price.data_path to attributes.data and
price.value_unit to EUR/kWh.
Prices: Tibber core integration (via action)
The built-in Tibber integration no longer exposes the price list as a sensor attribute. Its
action response is keyed by address and uses start_time and
price, so fetch it in an automation and feed the result into a trigger-based
template sensor built the same way as above:
{% set src = prices['YOUR_ADDRESS'] %}
[
{%- for e in src %}
{"start": "{{ e.start_time }}", "value": {{ e.price }}}
{%- if not loop.last %},{% endif %}
{%- endfor %}
]
PV forecast: Solcast
Based on the template shared by @bennobiber in
discussion #214, which already feeds EVCC. value stays
in W, matching the default pv_forecast_source.value_unit.
template:
- trigger:
- platform: time_pattern
minutes: "/5"
sensor:
- name: eos_pv_forecast
state: "ok"
attributes:
data: >
{% set today = state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedForecast') %}
{% set tomorrow = state_attr('sensor.solcast_pv_forecast_forecast_tomorrow', 'detailedForecast') %}
{% set all = (today if today else []) + (tomorrow if tomorrow else []) %}
[
{%- for e in all %}
{"start": "{{ e.period_start.isoformat() }}", "value": {{ (e.pv_estimate * 1000) | round(0) }}}
{%- if not loop.last %},{% endif %}
{%- endfor %}
]
.isoformat(), not | timestamp_utc:
timestamp_utc renders UTC wall-clock time without an offset. EOS
Connect then reads it as local time and every slot is shifted by your UTC offset.
period_start.isoformat() keeps the offset. If you already use such a template
for EVCC, adjust this one line.
Testing Your Timeseries Configuration
Use Test connection in the configuration UI, next to the timeseries
settings. It fetches the endpoint, resolves data_path, parses the payload and
shows the first slots converted into the unit the schedule uses — so a wrong
value_unit shows up as an absurd number before you save, rather than hours
later in the schedule. It tests the values currently in the form, including unsaved ones.
The same check runs automatically when you save a timeseries setting; a hard failure blocks the save and is reported on the field that caused it, while a plausibility warning does not block.
HTTP Request:
POST /api/config/test-timeseries
Content-Type: application/json
{
"domain": "price",
"price.data_url": "http://homeassistant.local:8123/api/states/sensor.eos_grid_prices",
"price.data_path": "attributes.data",
"price.value_unit": "EUR/kWh"
}
All keys are optional — anything omitted falls back to the stored configuration.
domain is price (default) or pv.
Response (Success):
{
"ok": true,
"entry_count": 192,
"resolution_seconds": 900,
"value_unit": "EUR/kWh",
"slots": [
{"start": "2026-08-23T00:00:00+02:00", "end": "2026-08-23T00:15:00+02:00",
"value": 38.11, "unit": "ct/kWh"}
],
"warnings": []
}
Response (Format mismatch): the message names the fields the payload actually contained, so you can see what to map in your template:
{
"ok": false,
"field": "price.data_path",
"error": "missing required field 'start' in the first entry (available fields:
'start_time', 'price_per_kwh'). The expected format is {start, end, value}
- shape the source with a Home Assistant template sensor, see
configuration.html#timeseries-templates"
}
Error Handling & Retries
EOS Connect automatically handles transient API failures:
- Automatic Retries: Up to 3 attempts with exponential backoff (0.5s → 2s → 4s)
- Cache Fallback: If API fails, uses the last successfully fetched data
- Graceful Degradation: Continues running with cached data if available
- Error Logging: Issues are logged with timestamps for troubleshooting
Hot-Reload Support
All timeseries configuration fields support hot-reload — changes take effect immediately without restarting the application:
price.data_url,price.data_path,price.data_token,price.value_unitpv_forecast_source.data_url,pv_forecast_source.data_path,pv_forecast_source.data_token,pv_forecast_source.value_unit
Changes are applied within 1-2 seconds on the next update cycle.
JSON Path Reference
Examples of how to specify data_path for different response structures:
| Response Structure | data_path | Notes |
|---|---|---|
{"data": [...]} |
data |
Top-level array |
{"attributes": {"data": [...]}} |
attributes.data |
Nested object (Home Assistant pattern) |
{"result": {"prices": [...]}} |
result.prices |
Multiple levels of nesting |
{"forecast": [{"data": [...]}]} |
forecast[0].data |
Array with indexed access |