Historical sensors for HomeAssistant
Project description
Historical sensors for Home Assistant 
Feed historical statistics into Home Assistant database.
HomeAssistant architecture is built around polling (or pushing) data from devices, or data providers, in "real-time". Some data sources (e.g, energy, water or gas providers) can't be polled in real-time or readings are not accurate. However reading historical data, like last month consumption, is possible and accurate. This module adds support to this.
This module uses the recorder component and Home Assistant's official
statistics API to import historical statistics data.
Current projects using this module:
Important: v3.0 Breaking Changes
Version 3.0 removes state writing entirely. Historical sensors now only write statistics, not individual states. This means:
✅ What still works:
- Energy dashboard integration
- Long-term statistics and trends
- Hourly/daily/monthly aggregated data
- All existing integration code (minimal changes needed)
❌ What no longer works:
- Individual state points in entity history graphs
- Granular state visualization in the UI
Why this change? Directly manipulating Home Assistant's recorder database was extremely complex, fragile, and error-prone. The code for maintaining state chains, handling schema changes, and managing database integrity was unsustainable. Statistics provide everything needed for 99% of use cases while using Home Assistant's official, stable API.
See [https://github.com/ldotlopez/ha-historical-sensor/issues/18] for detailed explanation and migration guide.
How to implement a historical sensor
💡 Check the delorian test integration in this repository
- Import home_assistant_historical_sensor and define your sensor. ⚠️ Don't set the SensorEntity.state_class property. See FAQ below
from homeassistant_historical_sensor import (
HistoricalSensor, HistoricalState, PollUpdateMixin,
)
class Sensor(PollUpdateMixin, HistoricalSensor, SensorEntity):
...
- Define the
async_update_historicalmethod and save your historical states into theHistoricalSensor._attr_historical_statesattribute.
async def async_update_historical(self):
self._attr_historical_states = [
HistoricalState(state=x.state, timestamp=x.when.timestamp())
for x in await api.fetch()
]
Note: timestamp expects a Unix timestamp (float).
- Define the
get_statistic_metadatamethod for your sensor.
def get_statistic_metadata(self) -> StatisticMetaData:
meta = super().get_statistic_metadata()
meta["has_sum"] = True # For counters (energy, water, gas)
# OR
meta["has_mean"] = True # For measurements (temperature, power)
return meta
- Define the
async_calculate_statistic_datamethod for your sensor.
This method calculates statistics from your historical states. Check the delorian integration for a full example.
async def async_calculate_statistic_data(
self,
hist_states: list[HistoricalState],
*,
latest: StatisticsRow | None = None,
) -> list[StatisticData]:
# Calculate hourly statistics from your states
# Return list of StatisticData with start, state, sum/mean
...
- Done! Besides other Home Assistant considerations, this is everything you need to implement statistics importing into Home Assistant.
Technical details
Q. How does it work?
A. The architecture is straightforward:
-
_attr_historical_statesproperty: Holds a list ofHistoricalStateobjects, each containing astatevalue and atimestamp(Unix timestamp as float). -
async_update_historicalhook: Your implementation updates_attr_historical_stateswith data from your source. This is the only method you must implement. -
async_calculate_statistic_datamethod: Calculates statistics (sum/mean/min/max) from historical states. You must implement this to generate statistics. -
async_write_ha_historical_statesmethod: Implemented byHistoricalSensor, handles writing statistics to Home Assistant using the officialasync_import_statisticsAPI.
Q. What happened to state writing?
A. Removed in v3.0. Writing states directly to the database was extremely complex and fragile. The module now only writes statistics using Home Assistant's official API, which is:
- Simpler and more maintainable
- Forward-compatible with HA updates
- Sufficient for energy dashboards and long-term trends
Individual state points no longer appear in entity history graphs, but statistics work perfectly for energy monitoring and trend analysis.
Q. What is PollUpdateMixin and why do I need to inherit from it?
A. Home Assistant sensors can use the poll or the push model to update data.
Historical sensors use a false push model: they are never updated by themselves (the state property always returns STATE_UNKNOWN).
PollUpdateMixin provides automatic periodic updates without any code. The sensor will be updated at startup and every hour. This interval can be configured via the UPDATE_INTERVAL class attribute.
Q. Why should I NOT set the state_class property?
A. Because it causes Home Assistant to calculate its own statistics from the sensor's current state, which:
- Doesn't make sense for historical data
- Creates incorrect/duplicate statistics
- Conflicts with your manual statistics calculations
Historical sensors provide statistics through async_calculate_statistic_data, not through state_class.
Q. Why is my sensor in "Unknown" state?
A. This is expected and correct. Historical sensors don't provide current state—they only import past statistics. The sensor will always show "Unknown" as its state because:
- The last historical data point (from hours/days ago) is NOT the current state
- The current state is genuinely unknown
- Only statistics are meaningful for this type of sensor
Q. Why doesn't my sensor show up in the energy panel?
A. Energy dashboard uses statistics, not sensor states. If your sensor doesn't appear:
- Make sure you've implemented
get_statistic_metadata()withhas_sum=True - Make sure you've implemented
async_calculate_statistic_data() - Trigger an update to import data and generate statistics
- Statistics will appear after the first successful import
Q. Can I provide BOTH current state AND historical statistics?
A. You need two separate sensors:
- One regular sensor for current state (with
state_class) - One HistoricalSensor for importing historical statistics
If you're using the data coordinator pattern, this should be straightforward.
Q. Can I calculate energy/water/gas costs?
A. Cost calculation must be done in Home Assistant's Energy dashboard configuration, not in the sensor itself. The Energy dashboard has built-in cost calculation features.
The energy websocket API may be useful for advanced use cases.
Q. Do I need to worry about overlapping data when re-importing?
A. No. The library handles this automatically:
- New statistics are only added if they're newer than the last imported statistic
- You can safely re-run imports without creating duplicates
Migration from v2.x to v3.x
Breaking Changes
-
HistoricalStateparameter renamed:ts→timestamp(buttsstill works for backward compatibility) -
group_by_intervalparameter fixed:granurality→granularity(typo fix) -
Minimum Home Assistant version: Now requires HA >= 2025.12.0
-
statistic_idproperty removed: Statistics now always useentity_id -
No more state writing: Only statistics are written to the database
What You Need to Update
# Before (v2.x)
HistoricalState(state=value, ts=timestamp)
# After (v3.x) - both work, but timestamp is preferred
HistoricalState(state=value, timestamp=timestamp)
HistoricalState(state=value, ts=timestamp) # Still supported
# Before (v2.x)
group_by_interval(states, granurality=3600)
# After (v3.x)
group_by_interval(states, granularity=3600)
What Stays the Same
✅ The integration API is unchanged
✅ async_update_historical() works the same
✅ async_calculate_statistic_data() works the same
✅ get_statistic_metadata() works the same
✅ Energy dashboard integration works the same
What's Removed (probably not relevant to you)
- All internal state writing methods
- Direct database manipulation utilities
statistic_idproperty (usesentity_idnow)patches.pymodule
Helper Functions
group_by_interval
Groups historical states into time intervals (typically hourly) for statistics calculation:
from homeassistant_historical_sensor import group_by_interval
for block_timestamp, states_in_hour in group_by_interval(
hist_states,
granularity=60 * 60 # 1 hour in seconds
):
states = list(states_in_hour)
# Calculate statistics for this hour
...
Importing CSV files
To be implemented: https://github.com/ldotlopez/ha-historical-sensor/issues/3
Licenses
- Logo by Danny Allen (Public domain license) https://publicdomainvectors.org/es/vectoriales-gratuitas/Icono-de-configuraci%C3%B3n-del-reloj/88901.html
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file homeassistant_historical_sensor-3.0.0a4.tar.gz.
File metadata
- Download URL: homeassistant_historical_sensor-3.0.0a4.tar.gz
- Upload date:
- Size: 24.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff1ddd94ea1c80af2240b8ae8946160cf56b709e67b0516fdfecff088f106432
|
|
| MD5 |
8c176151b9891313195351ae40c7c2ee
|
|
| BLAKE2b-256 |
f15a1dc9ffa0e1d22f1c732e0ace619088c9083431492d073cb49731405f5c36
|
Provenance
The following attestation bundles were made for homeassistant_historical_sensor-3.0.0a4.tar.gz:
Publisher:
publish.yml on ldotlopez/ha-historical-sensor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
homeassistant_historical_sensor-3.0.0a4.tar.gz -
Subject digest:
ff1ddd94ea1c80af2240b8ae8946160cf56b709e67b0516fdfecff088f106432 - Sigstore transparency entry: 1256285040
- Sigstore integration time:
-
Permalink:
ldotlopez/ha-historical-sensor@528dbefff439bef231fe37a57a17814c0a3f0322 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ldotlopez
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@528dbefff439bef231fe37a57a17814c0a3f0322 -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file homeassistant_historical_sensor-3.0.0a4-py3-none-any.whl.
File metadata
- Download URL: homeassistant_historical_sensor-3.0.0a4-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
801b61eb45e7213a2d7ca48557ebdae7bb6e5fa1862b27007a49c4546fec5049
|
|
| MD5 |
e5d15b91b7455659cc89531fe8f749b0
|
|
| BLAKE2b-256 |
92d95e15fc2582bd4d7f02b048d1edc18d64bf2da1f820e1a17330fae6f2524e
|
Provenance
The following attestation bundles were made for homeassistant_historical_sensor-3.0.0a4-py3-none-any.whl:
Publisher:
publish.yml on ldotlopez/ha-historical-sensor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
homeassistant_historical_sensor-3.0.0a4-py3-none-any.whl -
Subject digest:
801b61eb45e7213a2d7ca48557ebdae7bb6e5fa1862b27007a49c4546fec5049 - Sigstore transparency entry: 1256285207
- Sigstore integration time:
-
Permalink:
ldotlopez/ha-historical-sensor@528dbefff439bef231fe37a57a17814c0a3f0322 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ldotlopez
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@528dbefff439bef231fe37a57a17814c0a3f0322 -
Trigger Event:
workflow_run
-
Statement type: