Skip to the content.

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:

The first MVP does not need the full data model.

The first MVP only needs:


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:

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:

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:


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:

TimescaleDB

Use TimescaleDB or time-series optimized tables for:

Redis

Use Redis for:


Privacy principles

Elsignal should minimize personal data.

Important principles:


Security principles

Important principles:


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