User-defined Devices
When creating a charger, heater, grid meter, battery, solar system, other consumer, vehicle, tariff or notification service, you can pick the User-defined device option from the device type list and describe your own logic based on the plugin system.
That way you can integrate devices that aren’t covered by a built-in template, e.g. read the current power from an HTTP endpoint, or write a target current via Modbus.
How to define a device
Section titled “How to define a device”In the UI, every device type lists a User-defined device entry in the template selector.
Picking it opens the editor for your own configuration.
A device with type: custom is created by default. The type can be overridden with another value when needed (e.g. type: heatpump).
power: source: mqtt topic: home/current/imsys/chn2/rawAlternatively, write the full configuration directly in evcc.yaml with name, type: custom and the attribute block underneath:
meters: - name: imsys type: custom power: source: mqtt topic: home/current/imsys/chn2/rawAll other examples on this page use the flat attribute-block form you enter in the UI.
For the list of available plugin sources (http, mqtt, modbus, …) and helpers (calc, map, watchdog, …) see the plugins reference.
Attributes and features
Section titled “Attributes and features”Every user-defined device is described by three kinds of fields:
- Read attributes — plugins evcc polls to obtain a value (e.g.
power,soc,status). - Write attributes — plugins evcc invokes to set a value or trigger an action (e.g.
enable,maxcurrent,wakeup). The value to be written is available in the plugin. - Features —
featureslist toggling non-default behaviour. Available flags depend on the device type.
Generic shape:
# read attributespower: source: http uri: http://meter.local/powersoc: source: mqtt topic: battery/soc
# write attributesenable: source: http uri: "http://charger/relay?turn={{if .enable}}on{{else}}off{{end}}"maxcurrent: source: mqtt topic: charger/maxcurrent payload: ${maxcurrent:%d}
# featuresfeatures: - heating - integrateddeviceThe available attribute names and feature flags differ per device type and are listed in the sections below.
Power meters are configured in the meters section.
Meters defined under meters: can be referenced from site:
grid: Grid meterpv: PV meterbattery: Home battery metercharge: Meter for the charging power of the wallboxaux: Consumption meter for intelligent consumersconsumer: Meter for a regular consumer, recorded for consumption statisticsext: Additional meter not used for regulation or shown in the UI, only logged and exported
power is the only required attribute.
Not all meter roles support all attributes:
limitsocandbatterymodeare used exclusively for battery meters (referenced fromsite.battery).currents,voltagesandpowersare phase attributes that must be configured with exactly three plugin entries each (as a YAML array) and apply to grid meters (grid) and wallboxes (charge).
Return the correct data type from each plugin. To convert, use the reading pipelines.
Read attributes
Section titled “Read attributes”| Attribute | Type | Required | Context | Description |
|---|---|---|---|---|
power | float | yes | all | Current power in W |
energy | float | no | all | Meter reading in kWh |
returnenergy | float | no | all | Meter reading in reverse direction in kWh (see below) |
maxpower | int | no | pv (hybrid) | Maximum AC power in W |
soc | int | no | battery | State of charge in % |
capacity | float | no | battery | Capacity in kWh |
powers | [float,float,float] | no | all | Phase powers in W. For sign detection of unsigned currents. |
currents | [float,float,float] | no | all | Phase currents in A. For detecting active phases. |
voltages | [float,float,float] | no | all | Phase voltages in V. For connection detection (1p/3p). |
Signs and Directions
Section titled “Signs and Directions”What a positive power value and the two energy directions mean depends on the meter role:
| Role | power positive | energy | returnenergy |
|---|---|---|---|
grid | Grid import | Imported | Exported (feed-in) |
pv | Production | Produced | Consumed (rare) |
battery | Discharging (negative: charging) | Discharged | Charged |
charge | Charging | Charged | Discharged (V2X) |
aux, ext, consumer | Consumption | Consumed | Produced (rare) |
energy and returnenergy are increasing meter readings in kWh, ideally lifetime totals like the import and export registers of a grid meter.
The differences between readings are calculated automatically, so the plugin must return a meter reading and not consumption values per interval.
A counter that resets periodically (e.g. daily) also works, as the reset is detected automatically.
A reading of 0 is treated as not available.
Both attributes are optional.
Without them, the energy history is derived from power over time.
Real meter readings give more accurate long-term statistics.
Write attributes
Section titled “Write attributes”| Attribute | Type | Required | Context | Description |
|---|---|---|---|---|
limitsoc | int | no | battery | Set charging target for battery in %. The charging target is calculated from the configured MinSoc, MaxSoc and the current state of charge (attribute soc). |
batterymode | int | no | battery | Set charging mode directly (1: normal, 2: hold, 3: charge) |
Example
Section titled “Example”Read the current grid power from an HTTP endpoint.
power: source: http uri: http://zaehler.network.local:8080/api/data.json?from=now jq: .data.tuples[0][1]Charger
Section titled “Charger”The default type: custom covers wallboxes with continuous current control.
For other devices, specialised charger types are described under Switchsocket and Heat pumps.
Read attributes
Section titled “Read attributes”| Attribute | Type | Required | Description |
|---|---|---|---|
status | string | yes | Status (A..F) |
enabled | bool | yes | Is charging enabled? |
power | float | no | Charging power in W |
energy | float | no | Meter reading in kWh |
returnenergy | float | no | Meter reading in reverse direction in kWh (discharged energy, V2X) |
identify | string | no | Current RFID identifier |
soc | int | no | State of charge in % |
limitsoc | int | no | Charge limit in % |
temp | float | no | Current temperature in °C (heating, alias for soc) |
limittemp | int | no | Temperature limit in °C (heating, alias for limitsoc) |
finishtime | string | no | Estimated charging finish time (RFC3339, Go duration, Unix timestamp, or remaining seconds) |
phases | int | no | Number of physical phases (1..3) |
powers | [float,float,float] | no | Phase powers in W. For sign detection of unsigned currents. |
currents | [float,float,float] | no | Phase currents in A. For detecting active phases. |
voltages | [float,float,float] | no | Phase voltages in V. For connection detection (1p/3p). |
Write attributes
Section titled “Write attributes”| Attribute | Type | Required | Description |
|---|---|---|---|
enable | bool | yes | Enable / disable charging |
maxcurrent | int | yes | Set maximum charging current in A |
maxcurrentmillis | float | no | Set maximum charging current in A (with decimals) |
phases1p3p | int | no | Perform phase switching (requires tos: true) |
wakeup | bool | no | Wake up vehicle |
Features
Section titled “Features”| Feature | Description |
|---|---|
integrateddevice | No vehicle selection. Device runs without a connected vehicle and without charging sessions (e.g. heat pump, heating rod, permanently installed consumer). |
heating | Treat device as heating: SOC and limits are displayed in °C instead of %. |
continuous | Device keeps running in its own normal operation when “disabled”. The UI shows “Normal operation” instead of “Standby”. A request to increase power (e.g. on PV surplus or cheap grid power) is labelled “Boost”. |
switchdevice | Device can only be switched on/off (no continuous current control). Max current and the Min+PV mode are not shown. Min current and phases set the PV enable power. |
Common feature combinations seen in built-in templates:
Electric heater:
features: - integrateddevice - heatingSocket:
features: - switchdevice - integrateddevice # optional, when the socket drives a fixed load - heating # optional, when it controls a heating deviceHeat pump:
features: - integrateddevice - heating - continuous - switchdevice # optional, when no current control is available (SG Ready)Examples
Section titled “Examples”Query the charging status of a charger via Modbus.
features: - integrateddeviceenabled: source: modbus id: 4711 uri: modbus.local:502 rtu: false register: address: 100 type: holding decode: uint16Switch a Tasmota socket via an MQTT message.
enable: source: mqtt broker: mosquitto.local:883 topic: cmd/unu-switch/Power payload: ONSwitchsocket
Section titled “Switchsocket”type: switchsocket
For smart sockets and similar relay devices that can only be switched on/off without continuous current control.
The charging status is derived from the current power (above standbypower means charging).
Full setup details are under Smart switches.
| Attribute | Type | Required | Description |
|---|---|---|---|
enabled | bool | yes | Read socket state (on/off) |
power | float | yes | Current power in W |
energy | float | no | Meter reading in kWh |
soc | float | no | State of charge in % |
enable | bool | yes | Switch socket on/off |
standbypower | float | no | Power threshold in W. Above it: charging; below: standby. Negative: static (no metering) |
Heat pumps
Section titled “Heat pumps”Heat pumps and similar heating devices use dedicated charger types, each with its own attributes. The full setup is described under Heat pumps, electric heaters.
type: heatpump
For inverter-controlled heat pumps that accept a continuous power setpoint over Modbus, HTTP or similar.
The target heating power is written directly via setmaxpower.
| Attribute | Type | Required | Description |
|---|---|---|---|
power | float | no | Current power in W |
energy | float | no | Meter reading in kWh |
temp | float | no | Current temperature in °C |
limittemp | int | no | Device-internal temperature limit in °C |
setmaxpower | int | yes | Set maximum heating power in W |
getmaxpower | float | no | Current maximum heating power in W |
type: sgready
For heat pumps that expose the standard SG-Ready interface as a single mode value.
Three modes are supported: 1 reduced, 2 normal, 3 boost.
| Attribute | Type | Required | Description |
|---|---|---|---|
power | float | no | Current power in W |
energy | float | no | Meter reading in kWh |
temp | float | no | Current temperature in °C |
limittemp | int | no | Device-internal temperature limit in °C |
setmode | int | yes | Change SG-Ready mode (1: reduced, 2: normal, 3: boost) |
getmode | int | no | Current SG-Ready mode (1, 2, 3) |
setmaxpower | int | no | Set maximum heating power in W |
type: sgready-relay
For heat pumps whose SG-Ready input is wired as two dry relay contacts (boost + dim). Each contact is driven by a separate sub-charger referenced by type, not via plugins.
| Attribute | Type | Required | Description |
|---|---|---|---|
power | float | no | Current power in W |
energy | float | no | Meter reading in kWh |
temp | float | no | Current temperature in °C |
limittemp | int | no | Device-internal temperature limit in °C |
boost | charger-typed | yes | Relay for the SG-Ready boost contact |
dim | charger-typed | no | Relay for the SG-Ready dim contact |
Vehicle
Section titled “Vehicle”Vehicle parameters can also be read via plugins.
Read attributes
Section titled “Read attributes”| Attribute | Type | Required | Description |
|---|---|---|---|
soc | int | yes | State of charge in % |
limitsoc | int | no | Charge limit in % |
status | string | no | Status (A..F) |
range | int | no | Range in km |
odometer | int | no | Odometer reading in km |
climater | bool | no | Climate control active? |
getmaxcurrent | float | no | Maximum charging current in A |
finishtime | string | no | Estimated charging finish time (RFC3339, Go duration, Unix timestamp, or remaining seconds) |
Write attributes
Section titled “Write attributes”| Attribute | Type | Required | Description |
|---|---|---|---|
wakeup | bool | no | Wake up vehicle |
chargeenable | bool | no | Start/stop charging process |
maxcurrent | int | no | Set maximum charging current in A |
Configuration
Section titled “Configuration”| Attribute | Type | Required | Description |
|---|---|---|---|
title | string | no | Display name of the vehicle |
capacity | float | no | Battery capacity in kWh |
icon | string | no | Icon shown in the user interface |
Features
Section titled “Features”| Feature | Description |
|---|---|
coarsecurrent | Vehicle accepts charging current only in whole 1 A steps. The control logic is constrained to coarse 1 A steps even when the charger could regulate more finely. |
streaming | Vehicle pushes data instead of being polled (e.g. BMW Cardata). SOC updates outside active charging are treated as reliable. |
welcomecharge | Vehicle expects the wallbox to be active on connect to register the link as working. Otherwise it reports an error. |
Examples
Section titled “Examples”Read the current range from MQTT messages.
title: Green Mazda # display name (optional)capacity: 50 # battery capacity in kWh (optional)features: - coarsecurrentrange: source: mqtt topic: mazda2mqtt/c53/chargeInfo/drivingRangeKmWake a car via an HTTP ping before sending further queries.
wakeup: source: http uri: http://teslalogger.local:5000/command/08154711/wake_uponIdentify sets the charge mode automatically when the vehicle is detected.
soc: source: mqtt topic: car/soconIdentify: mode: pvAvailable modes: off, now, minpv, pv.
Tariff and forecast
Section titled “Tariff and forecast”A user-defined tariff connects evcc to a custom value source via the plugin mechanism.
The tariff attribute selects what the source represents and which unit the values use.
Read attributes
Section titled “Read attributes”Exactly one of price or forecast must be configured.
| Attribute | Type | Description |
|---|---|---|
price | float | Current value. Float returned by the plugin. |
forecast | string | Forecast as JSON string with a list of time periods and values (see schema below). Polled hourly. |
Configuration
Section titled “Configuration”| Attribute | Type | Required | Description |
|---|---|---|---|
tariff | string | no | price (default), co2 or solar. Sets the unit of the returned values: price in configured currency per kWh, CO₂ intensity in g/kWh, solar forecast in W. |
charges | float | no | Fixed additional charge per kWh added to every value. Default 0. |
chargesZones | list | no | Time-based charges (e.g. grid fees) that override charges for specific periods. See time-based grid fees. |
tax | float | no | Percentage tax applied to the result, e.g. 0.2 for 20%. Default 0. |
formula | string | no | Go expression for a custom calculation, with price, charges and tax in scope. See examples. |
interval | duration | no | Polling interval for forecast. Default 1h. |
cache | duration | no | Cache duration for price. Default 15m. |
Features
Section titled “Features”| Feature | Description |
|---|---|
average | Smooths fine-grained price slots (e.g. 15-minute values) into hourly averages. |
cacheable | Persists fetched values. Used as fallback after a restart or provider outage (up to 24 hours). |
Examples
Section titled “Examples”Current price via HTTP:
price: source: http uri: https://example.com/api/priceForecast via HTTP:
forecast: source: http uri: https://api.allinpower.nl/troodon/api/p/spot_market/prices/?product_type=ELK jq: '[.timestamps, .prices] | transpose | map({ "start": (.[0] | strptime("%Y-%m-%dT%H:%M:%S.%f%z") | strftime("%Y-%m-%dT%H:%M:%SZ")), "end": (.[0] | strptime("%Y-%m-%dT%H:%M:%S.%f%z") | mktime + 3600 | strftime("%Y-%m-%dT%H:%M:%SZ")), "value": .[1] }) | tostring'The plugin must return a JSON structure containing a list of time periods and prices.
Date fields must be in the form YYYY-MM-DDTHH:MM:SSZ and the price in the correct currency unit (e.g. EUR).
evcc works internally with 15-minute intervals; plugins may return hourly data, which is converted automatically.
[ { "start": "2025-01-01T00:00:00Z", "end": "2025-01-01T00:15:00Z", "value": 25.0 }, { "start": "2025-01-01T00:15:00Z", "end": "2025-01-01T00:30:00Z", "value": 26.5 }, { "start": "2025-01-01T00:30:00Z", "end": "2025-01-01T00:45:00Z", "value": 24.8 }, { "start": "2025-01-01T00:45:00Z", "end": "2025-01-01T01:00:00Z", "value": 27.2 }]The formula field accepts a Go expression with price, charges, tax and the slot timestamp ts in scope.
The math library and time.Time methods on ts are available.
The formula runs for the current price and each forecast slot.
Price cap:
charges: 0.22tax: 0.19formula: math.Min(0.5, (price + charges) * (1 + tax))Caps the result at 50 ct/kWh.
No feed-in tariff on negative day-ahead prices (German PV systems commissioned after February 25, 2025):
formula: factor := 1.0; if price < 0 { factor = 0.0 }; factor * 0.07Pays a fixed feed-in tariff of 7 ct/kWh, except when the day-ahead market price is negative.
External Limit
Section titled “External Limit”A user-defined integration for the External Limit feature connects a control box or energy management system that isn’t covered by a built-in integration. In the UI, pick User-defined integration under Configuration → External Limit.
Unlike other device types, there is no type: custom here.
Instead, one of three types describes how the limit signal is received: relay (single switch contact), fnn (FNN control box with multiple switch contacts) or eebus (EEBus protocol).
Each signal is read via a plugin configuration (GPIO, MQTT, HTTP, Modbus).
See also the hems configuration reference.
The connection via a single switch contact is the simplest solution. The control box activates a contact which is evaluated by your evcc instance.
| Attribute | Type | Required | Description |
|---|---|---|---|
maxPower | int (W) | yes | Total power limit applied while the signal is active. |
limit | plugin | yes | Plugin configuration for reading the switch contact. true/1 = limited, false/0 = normal. |
passthrough | plugin | no | Passes the limitation signal through to an external system. |
interval | duration | no | Polling interval for the switch contact. Default 10s. |
The power limit is communicated to you by the grid operator. For multiple controllable consumption devices (SteuVE), the simultaneity factor is taken into account. You can also calculate the limit yourself using the formula: Total limit = Number of SteuVE × 4.2 kW × Simultaneity factor. Details on the calculation can be found here.
When using a Raspberry Pi, the GPIO pin can be read directly:
type: relaymaxPower: 8400 # Example for 2 SteuVElimit: source: gpio function: read pin: 17 # Read GPIO pin 17 # Return value: false = not limited, true = limitedFor more details on the GPIO plugin, see the plugin documentation.
If the control box or gateway sends MQTT messages:
type: relaymaxPower: 11340 # Example for 3 SteuVE with simultaneity factor 0.9limit: source: mqtt topic: hems/limit/status # Expected values: 0/false = normal, 1/true = limitedFor control boxes with REST API:
type: relaymaxPower: 13440 # Example for 4 SteuVE with simultaneity factor 0.8limit: source: http uri: http://steuerbox.local/api/limit jq: .limited # JSON path to boolean valueIf the control box or a gateway provides the power limit via Modbus:
type: relaymaxPower: 4200 # Example for 1 SteuVElimit: source: modbus uri: 192.168.179.200:4703 # Example: relay on the 2nd S0 meter of a cFos Power Brain solar wallbox id: 3 # Modbus slave ID timeout: 5s register: type: holding decode: uint16 address: 8056 # Return value: 0 = not limited, 1 = limitedFNN Control Box
Section titled “FNN Control Box”Control boxes following the FNN standard signal dimming and curtailment via separate switch contacts.
Consumption dimming (W4) and feed-in curtailment (W3, S2, S1) operate independently.
At least one of the signals w4 or w3 must be configured.
| Attribute | Type | Required | Description |
|---|---|---|---|
maxDimPower | int (W) | yes¹ | Consumption limit while the dim signal (W4) is active. |
maxCurtailPower | int (W) | yes² | Installed solar power, base value for the curtailment steps. |
w4 | plugin | no | Reads the dim signal. Limits consumption to maxDimPower. |
w3 | plugin | no | Reads the curtailment signal. Limits feed-in to 0% of maxCurtailPower. |
s2 | plugin | no | Reads the curtailment signal. Limits feed-in to 30% of maxCurtailPower. |
s1 | plugin | no | Reads the curtailment signal. Limits feed-in to 60% of maxCurtailPower. |
interval | duration | no | Polling interval for the switch contacts. Default 10s. |
¹ required when w4 is configured, ² required when w3 is configured.
type: fnnmaxDimPower: 4200 # Consumption limit while dimmed (in watts)maxCurtailPower: 10000 # Installed solar power, base for curtailment steps (in watts)w4: source: gpio function: read pin: 17 # Read GPIO pin 17 # Return value: false = normal, true = activew3: source: gpio function: read pin: 27s2: source: gpio function: read pin: 22s1: source: gpio function: read pin: 23The digital connection via the EEBus protocol. The control box communicates directly with your evcc instance and automatically transmits the power limit. Control box and evcc need to be paired once.
| Attribute | Type | Required | Description |
|---|---|---|---|
ski | string | yes | SKI (Subject Key Identifier) of the control box. |
contractualConsumptionNominalMax | int (W) | no | Contractual maximum consumption power. |
failsafeConsumptionActivePowerLimit | int (W) | no | Failsafe limit for consumption power. |
productionNominalMax | int (W) | no | Installed generator power (Wp). |
failsafeProductionActivePowerLimit | int (W) | no | Failsafe limit for feed-in power. |
failsafeDurationMinimum | duration | no | Minimum failsafe duration, e.g. 2h. |
passthrough | plugin | no | Passes the limitation signal through to an external system. |
type: eebusski: "1234-5678-90AB-CDEF" # SKI of the control boxNotification Service
Section titled “Notification Service”A user-defined notification service processes notification messages via any plugin with write access, e.g. a shell script, HTTP request, or MQTT message. In the UI, pick User-defined service under Configuration → Notifications → Services.
The message is provided to the plugin in the ${send} parameter (or as a template parameter {{.send}}).
Configuration
Section titled “Configuration”| Attribute | Type | Required | Description |
|---|---|---|---|
send | plugin | yes | Plugin invoked for each message. Must support write access. |
encoding | string | no | Format of the value provided in ${send}. See below. |
The possible encoding values are:
| Encoding | Content of ${send} |
|---|---|
json | JSON object { "msg": msg, "title": title }. The title field is only added if defined for the event. |
csv | title and msg as a comma-separated list |
tsv | Like csv, but tab-separated |
title | Only the title |
| (none) | Only the message (msg) |
Example
Section titled “Example”messaging: events: connect: title: "${vehicleTitle} connected" msg: "${vehicleTitle} was connected (charging mode: ${mode})." services: - type: custom encoding: json send: # plugin type source: script # plugin-specific configuration; # {{.send}} contains the JSON message cmd: /usr/local/bin/evcc_message "{{.send}}"In this example, a shell script (cmd) is invoked with the argument {"title": "...", "msg": "..."}.