Skip to content

Profile Format

A profile is a JSON document that maps Modbus registers to Home Assistant entities. Profiles are loaded from the built-in profiles/ directory and from <HA config>/sg_energy_hub/profiles/.

Top-level fields

Field Required Meaning
id yes Stable identifier, lowercase a-z0-9_
name yes Model name shown in the UI
vendor yes Manufacturer
device_type yes inverter, battery, heat_pump, energy_meter, system
model_pattern no Regex used for display and future discovery
description no Free text
version no Profile version, default 1.0.0
verified no true only after testing against real hardware
demo no Marks the simulated profile; exactly one is allowed
modbus_config no Connection defaults
registers yes Non-empty list of register entries

modbus_config

Field Default Meaning
default_port 502 TCP port
unit_id 1 Modbus unit id
byte_order big Byte order inside each 16-bit word
word_order big Word order for 32/64-bit values
timeout 5 Seconds

Register fields

Field Required Meaning
address yes 0–65535
name yes Stable key, lowercase a-z0-9_
label no Display name; defaults to the title-cased key
type no uint16, int16, uint32, int32, uint64, int64, float32, float64, string
length for string Number of characters
unit no Unit such as W, kWh, %, °C
scale no Divisor, default 1
offset no Added after scaling, default 0
precision no Decimal places for display
access no ro (default) or rw
function no holding (default) or input
device_class no Home Assistant device class, e.g. power, energy, battery, temperature
state_class no measurement, total, total_increasing, or null
icon no Material icon
entity_category no diagnostic or config
enabled_default no Default true
basic no Default true; false requires Pro
tier no "pro" is shorthand for basic: false
entity no Default true; false reads the register but creates no entity
enum no Map of integer values to labels

Scaling

scale is a divisor:

{"address": 500, "name": "pv1_power", "type": "uint16", "unit": "W", "scale": 10}

Raw 5000 becomes 500.0 W. Writing reverses the calculation.

Multi-register values

uint32, int32, float32 occupy two registers, uint64, int64, float64 occupy four. word_order controls which word comes first:

  • big: high word first,
  • little: low word first (common for many inverters).

byte_order controls the two bytes inside each word.

Enums

{
  "address": 540,
  "name": "heat_pump_status",
  "type": "uint16",
  "state_class": null,
  "enum": {"0": "off", "1": "heating", "2": "cooling", "5": "fault"}
}

The sensor state is the mapped label. Unknown values are shown as the raw number.

Example

{
  "id": "example_inverter",
  "name": "Example Inverter",
  "vendor": "Example",
  "device_type": "inverter",
  "model_pattern": "EX-.*",
  "version": "0.1.0",
  "verified": false,
  "modbus_config": {
    "default_port": 502,
    "byte_order": "big",
    "word_order": "little"
  },
  "registers": [
    {
      "address": 500,
      "name": "pv_power",
      "type": "uint16",
      "unit": "W",
      "scale": 10,
      "device_class": "power",
      "state_class": "measurement"
    },
    {
      "address": 514,
      "name": "battery_soc",
      "type": "uint16",
      "unit": "%",
      "device_class": "battery",
      "state_class": "measurement"
    }
  ]
}

Validation rules

  • Profile ids and register names must be unique.
  • Register address ranges may not overlap within the same function code.
  • scale may not be zero.
  • length is only valid for string registers.
  • enum is only valid for integer types.
  • The demo profile must be unique.
  • scripts/check_repository.py enforces these rules in CI.