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:
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 [#18] for detailed explanation and migration guide.
💡 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.
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
-
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
# 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)✅ 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
- All internal state writing methods
- Direct database manipulation utilities
statistic_idproperty (usesentity_idnow)patches.pymodule
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
...To be implemented: #3
- Logo by Danny Allen (Public domain license) https://publicdomainvectors.org/es/vectoriales-gratuitas/Icono-de-configuraci%C3%B3n-del-reloj/88901.html