Data Model
Overview
This document describes the planned data model for Elsignal.
Elsignal is an open electricity data and energy signal platform for Sweden. The platform provides electricity price data, smart time windows, energy scores, automation flags and partner-ready API responses.
The data model should support:
- public electricity price data
- optional user accounts
- saved homes
- contract types
- solar panels
- batteries
- EV chargers
- water heaters
- heat pumps
- API keys
- partners
- device profiles
- calculated signals
- future tariff models
The first MVP does not need the full data model.
The first MVP only needs:
- electricity areas
- price data
- calculated public summaries
Core concepts
Electricity area
Swedish electricity area.
Supported values:
SE1
SE2
SE3
SE4
Contract type
Describes the user’s electricity contract.
Supported values:
hourly
variable
fixed
mixed
unknown
Description:
| Value | Meaning |
|---|---|
hourly |
User has hourly spot price |
variable |
User has variable price but not necessarily hourly billing |
fixed |
User has fixed price |
mixed |
User has a mixed or hybrid contract |
unknown |
Contract type is unknown |
Optimization goal
Describes what the user wants to optimize for.
Supported values:
cost
grid_support
solar_self_consumption
balanced
Description:
| Value | Meaning |
|---|---|
cost |
Optimize mainly for lowest cost |
grid_support |
Optimize mainly for system-friendly consumption |
solar_self_consumption |
Optimize mainly for local use of solar production |
balanced |
Balance cost, grid support and solar self-consumption |
Device type
Describes the type of connected device or use case.
Supported planned values:
water_heater
ev_charger
heat_pump
battery
solar_inverter
generic_switch
energy_manager
home_automation
Description:
| Value | Meaning |
|---|---|
water_heater |
Water heater or domestic hot water load |
ev_charger |
Electric vehicle charger |
heat_pump |
Heat pump or heating system |
battery |
Home battery or energy storage |
solar_inverter |
Solar inverter or PV system |
generic_switch |
Generic controllable load |
energy_manager |
Energy management system |
home_automation |
Home automation platform |
MVP data model
The first MVP should be simple.
It does not require users, accounts, partners or device profiles.
electricity_areas
Stores supported electricity areas.
id
code
name
country
timezone
created_at
updated_at
Example:
{
"code": "SE3",
"name": "Stockholm",
"country": "SE",
"timezone": "Europe/Stockholm"
}
price_data
Stores electricity price periods.
id
price_area
start_time
end_time
spot_price
currency
unit
source
created_at
updated_at
Example:
{
"price_area": "SE3",
"start_time": "2026-05-18T14:00:00+02:00",
"end_time": "2026-05-18T15:00:00+02:00",
"spot_price": 1.24,
"currency": "SEK",
"unit": "SEK/kWh",
"source": "external_price_source"
}
Recommended constraints:
unique(price_area, start_time)
Recommended indexes:
price_area
start_time
price_area + start_time
public_price_summary
This does not necessarily need to be a database table.
It can be calculated dynamically from price_data and cached.
Fields:
area
timezone
currency
generated_at
current
thresholds
cheapest_hours
most_expensive_hours
best_window
flags
data_status
Example:
{
"area": "SE3",
"timezone": "Europe/Stockholm",
"currency": "SEK",
"generated_at": "2026-05-18T14:05:00+02:00",
"current": {
"start": "2026-05-18T14:00:00+02:00",
"end": "2026-05-18T15:00:00+02:00",
"spot_price": 1.24,
"price_level": "normal",
"rank_today": 12
},
"thresholds": {
"p30": 0.92,
"p80": 1.85
},
"flags": {
"is_cheap_now": false,
"is_expensive_now": false,
"is_in_best_window": false
}
}
User data model
This part is planned for later versions.
The platform should work without an account, but accounts allow users to save homes, preferences, API keys and integration settings.
users
Stores user accounts.
id
email
password_hash
display_name
created_at
updated_at
last_login_at
subscription_plan
is_active
Notes:
- Passwords must never be stored in plain text.
password_hashshould use a strong hashing method.- Email should be unique.
Recommended constraints:
unique(email)
homes
Stores user homes.
id
user_id
name
country
price_area
contract_type
electricity_supplier
grid_operator
timezone
created_at
updated_at
Example:
{
"name": "Villa",
"country": "SE",
"price_area": "SE3",
"contract_type": "fixed",
"electricity_supplier": "unknown",
"grid_operator": "unknown",
"timezone": "Europe/Stockholm"
}
Recommended indexes:
user_id
price_area
contract_type
home_capabilities
Stores information about what the home has.
id
home_id
has_solar
has_battery
has_ev
has_water_heater
has_heat_pump
has_flexible_loads
optimization_goal
created_at
updated_at
Example:
{
"has_solar": true,
"has_battery": false,
"has_ev": true,
"has_water_heater": true,
"has_heat_pump": true,
"has_flexible_loads": true,
"optimization_goal": "balanced"
}
tariffs
Stores tariff and cost settings for a home.
This is planned for later versions.
id
home_id
vat_percent
supplier_markup_ore_per_kwh
energy_tax_ore_per_kwh
grid_energy_fee_ore_per_kwh
fixed_monthly_fee
effect_fee_enabled
effect_fee_model
valid_from
valid_to
created_at
updated_at
Example:
{
"vat_percent": 25,
"supplier_markup_ore_per_kwh": 8,
"energy_tax_ore_per_kwh": 53.5,
"grid_energy_fee_ore_per_kwh": 32,
"fixed_monthly_fee": 49,
"effect_fee_enabled": true,
"effect_fee_model": "monthly_peak_average"
}
API key model
api_keys
Stores API keys for users and partners.
id
user_id
home_id
partner_id
name
key_prefix
key_hash
scopes
rate_limit_plan
created_at
last_used_at
expires_at
is_active
Notes:
- Store only a hash of the API key.
key_prefixcan be stored to identify the key without exposing it.- API keys should support scopes.
Example scopes:
public:read
signals:read
home:read
partner:read
partner:signals
Signal data model
Signals can either be calculated dynamically or stored for historical analysis.
For the first MVP, signals can be calculated dynamically and cached.
Later, storing signals may be useful for analysis, debugging and partner reporting.
calculated_signals
Stores calculated signal values per home or public area.
id
home_id
price_area
timestamp
price_score
grid_support_score
solar_score
combined_score
price_level
grid_signal
labels
flags
created_at
Example:
{
"price_area": "SE3",
"timestamp": "2026-05-18T13:00:00+02:00",
"price_score": 90,
"grid_support_score": 82,
"solar_score": 95,
"combined_score": 89,
"price_level": "cheap",
"grid_signal": "good_time_to_consume",
"labels": [
"cheap",
"solar_friendly",
"good_time_to_consume"
],
"flags": {
"is_cheap_now": true,
"good_time_to_consume": true,
"avoid_consumption": false
}
}
Recommended indexes:
home_id
price_area
timestamp
price_area + timestamp
signal_windows
Stores calculated recommended windows.
id
home_id
price_area
device_type
optimization_goal
start_time
end_time
duration_minutes
average_price
price_score
grid_support_score
solar_score
combined_score
rank
reason
created_at
Example:
{
"price_area": "SE3",
"device_type": "water_heater",
"optimization_goal": "balanced",
"start_time": "2026-05-18T02:00:00+02:00",
"end_time": "2026-05-18T05:00:00+02:00",
"duration_minutes": 180,
"average_price": 0.48,
"combined_score": 91,
"rank": 1,
"reason": "Low price and high combined score."
}
Partner data model
This part is planned for later versions.
partners
Stores partner companies or service providers.
id
name
company_name
contact_email
website
plan
created_at
updated_at
is_active
Example plans:
developer
partner
enterprise
partner_products
Stores products connected to a partner.
id
partner_id
name
device_type
description
website
created_at
updated_at
is_active
Example:
{
"name": "Smart EV Charger",
"device_type": "ev_charger",
"description": "EV charger using Elsignal to schedule charging."
}
device_profiles
Stores device profile templates.
id
partner_id
device_type
name
default_duration_minutes
min_duration_minutes
max_duration_minutes
supported_signals
created_at
updated_at
Example:
{
"device_type": "water_heater",
"name": "Default water heater profile",
"default_duration_minutes": 180,
"min_duration_minutes": 60,
"max_duration_minutes": 360,
"supported_signals": [
"price_score",
"grid_support_score",
"combined_score"
]
}
Integration data model
This part is planned for later versions.
integrations
Stores user integration settings.
id
home_id
integration_type
name
enabled
settings
created_at
updated_at
last_seen_at
Example integration types:
home_assistant
homey
mqtt
webhook
custom_api
Example:
{
"integration_type": "home_assistant",
"name": "Home Assistant Villa",
"enabled": true,
"settings": {
"entity_prefix": "elsignal",
"include_solar_signals": true
}
}
webhooks
Stores webhook targets.
id
home_id
name
url
events
secret_hash
enabled
created_at
updated_at
last_triggered_at
Example events:
price_became_cheap
price_became_expensive
best_window_started
avoid_consumption_started
good_time_to_consume_started
solar_window_started
mqtt_connections
Stores MQTT connection settings.
id
home_id
name
broker_url
username
password_hash
topic_prefix
enabled
created_at
updated_at
last_connected_at
Notes:
- Passwords and secrets should be encrypted or otherwise protected.
- Avoid logging secrets.
Future data model for measurements
Later versions may support user-provided measurements.
This is not needed for the first MVP.
measurements
Stores optional user energy measurements.
id
home_id
timestamp
measurement_type
value
unit
source
created_at
Example measurement types:
consumption_power
consumption_energy
solar_power
solar_energy
battery_soc
grid_import_power
grid_export_power
Example:
{
"home_id": "home_123",
"timestamp": "2026-05-18T14:00:00+02:00",
"measurement_type": "solar_power",
"value": 4200,
"unit": "W",
"source": "home_assistant"
}
Relationships
High-level relationships:
User
└── Home
├── HomeCapabilities
├── Tariff
├── ApiKeys
├── Integrations
├── Webhooks
├── MqttConnections
├── Measurements
├── CalculatedSignals
└── SignalWindows
Partner
├── PartnerProducts
├── DeviceProfiles
└── ApiKeys
ElectricityArea
└── PriceData
MVP relationships
The first MVP only needs:
ElectricityArea
└── PriceData
└── PublicPriceSummary
Where PublicPriceSummary can be calculated dynamically and cached.
Storage strategy
PostgreSQL
Use PostgreSQL for relational data:
- users
- homes
- API keys
- partners
- integrations
- tariffs
- settings
TimescaleDB
Use TimescaleDB or time-series optimized tables for:
- price data
- calculated signals
- measurements
Redis
Use Redis for:
- cached API responses
- current price data
- summary responses
- rate limiting
- short-lived calculation results
Privacy principles
Elsignal should minimize personal data.
Important principles:
- Public API should not require personal data.
- User accounts should only store necessary information.
- API keys should be hashed.
- Secrets should not be logged.
- Users should be able to delete their data.
- Partner access should not expose private user data unless explicitly authorized.
Security principles
Important principles:
- Passwords must be hashed.
- API keys must be hashed.
- Secrets should be encrypted.
- Use HTTPS in production.
- Use rate limiting.
- Use scoped API keys.
- Validate all input.
- Log errors without exposing sensitive data.
- Separate public, user and partner access.
First implementation recommendation
For the first MVP, start with simple in-memory or file/cache based data handling if needed.
Minimum useful model:
ElectricityArea
PricePoint
PriceSummary
Suggested Python models:
```python from datetime import datetime from pydantic import BaseModel
class PricePoint(BaseModel): area: str start: datetime end: datetime spot_price: float currency: str = “SEK” unit: str = “SEK/kWh”
class PriceSummary(BaseModel): area: str timezone: str = “Europe/Stockholm” current: PricePoint | None cheapest_hours: list[PricePoint] most_expensive_hours: list[PricePoint] best_window: dict | None