Items marked deprecated should not be used going forward as they will be removed in a future firmware release.

Device Attributes

Most device attributes are simple name/value pairs with both the name and the value consisting of strings. Two exceptions are the path and interface attributes:

  • path: The value of this attribute is a list of strings. The list provides a unique path to the device. If present, it must be the first attribute. This is the only attribute which provides a guaranteed unique identifier for the device. The flip-side is that the path is not necessary stable. For example, if a USB device is moved from one port to another, the path would change. Thus, depending on the needs of the application, it may be more appropriate to identify a device through other means, such as the manufacturer, model, and serial-number, which, together, might provide a unique identifier for the device that remains stable regardless of how the device is connected to the meter.

  • interface: The value of this attribute is also a list of strings. Each entry is the name of an interface that is supported by the device. A description of each interface can be obtained from /ctrl/interface.

The meaning of other attributes is given below:

  • link: The physical link used by the device. If present, the value must be one of:

    • Ethernet: The device is connected via Ethernet.

    • USB: The device is connected via USB.

    • serial: The device is connected via a serial link such as RS485, RS232, or similar.

  • mfg: The name of the manufacturer of the device (e.g., eGauge).

  • model: The model name of the device (e.g., PRM3).

  • name: If present, a user-selected name of the device.

  • prot: The communication protocol used by the device. If present, it must be one of:

    • CoAP: The device uses the Constrained Application Protocol.

    • CtrlByWeb: The device uses the ControlByWeb XML protocol.

    • Modbus: The device uses the Modbus protocol.

    • RTCoA: The device uses the Radio Thermostat Co of America protocol.

    • SCPI: The device uses the SCPI protocol (pronounced "skippy".

    • SMANet: The device uses the SMAnet protocol used by older PV inverters manufactured by SMA.

  • quality: Devices that can potentially be reached through multiple paths may set this attribute to indicate the communication-quality of a particular path. The value of this attribute must be a decimal string. Paths that provide better communication-quality in some sense (e.g., higher speed or smaller loss-rate) should have a higher value. When mapping a set of attributes to a set of paths, the paths will be ordered by decreasing quality value such that higher quality paths will appear before lower quality ones.

  • sn: The serial "number" of the device. Even though called a number, the value may also contain non-digit characters (e.g., 0Y0035).


eScript is a simple scripting langage which supports basic arithmetic operations (addition, subtraction, multiplication, and division) of double-precision floating point numbers. The operations follow normal precedence rules. Parentheses can be used to force evaluation in a particular order. A C-like ternary operator is also supported for conditional evaluation. Specifically:

c ? e1 : e2

evaluates to e1 if c is non-zero and to e2 otherwise.

The latest instantaneous value of a meter register can be obtained with the $ operator which must be followed by a register name in quotes. For example:


would evaluate to the instantaneous value of register Grid.

eScript also supports various functions such as sin() to calculate the sine of an angle or THD() to calculate total-harmonic distortion in a signal. A list of functions is available at /sys/func.

An eScript expression which starts with a colon (:) is interpreted as a Lua expression.


Filter-specs can be used to return only certain members of an object or certain elements of an array.

For example, {foo,bar} would limit the output of an object to the members with names foo and bar. Similarly, [0,3:5] would limit the output of an array to the elements with indices 0, 3, 4, and 5.

If a member or array filter is empty, all members/elements of that object/array are returned. This is handy when filtering deeper levels of a response. For example [{addr}] would return only member addr from all the objects in the returned array.

For a more complex example, the filter-spec {reg[7:8{addr}]} would, for each object in the response, only return the reg member and, within each reg array, only elements with indices 7 or 8 would be returned. Within the objects of those elements, only the addr member would be returned.

Note that braces and square brackets normally need to be percent-encoded before using them in a URL (e.g., %7B for {).

Formal Definition

The formal syntax of a filter-spec (FSPEC) is given by the EBNF syntax below. No whitespace is allowed between symbols.

RANGE = UINT [ ":" UINT ] .
DIGIT = "0".."9".
NAME = ALPHA | DIGIT | "-" | "." | "_" | "~" | PCT_ENC .
ALPHA = "a".."z" | "A".."Z" .
HEX = DIGIT | "a".."f" | "A".."F" .

In words: a filter-spec can restrict the members returned from an object by listing zero or more member names, separated by commas, in curly braces. Only listed member names will be returned. As a special case, the empty object filter {} returns all members. A member name can be followed by a nested filter-spec to further filter the value the member with that name. A common nested filter-spec can also be applied to several comma-separated member names by enclosing them in parentheses and writing the common filter-spec after the closing parenthesis.

Similarly, a filter-spec can restrict the elements returned from an array by listing zero or more indices, separated by commas, in square brackets. Only listed indices will be returned. As a special case, the empty array filter [] returns all elements in an array. An index can be a single unsigned integer or a range of indices written as a starting index, a colon, and an ending index. For example, range 10:20 would corresponds to indices 10 through 20. An index can be followed by a nested filter-spec to further filter the value of the element with that index. A common nested filter-spec can also be applied to several comma-separated indices by enclosing them in parentheses and writing the common filter-spec after the closing parenthesis.

Lua Scripts

In addition to eScript, the meter firmware also supports the more powerful Lua language. All eScript functions can be called directly from Lua. Conversely, eScript may also call Lua functions as long as they use only numbers as arguments and return a single number as a result.


The max-depth parameter can be specified to limit the depth to which a response object or array is output. When the depth-limit is reached, only a list of member names is returned for objects and only the length is returned for arrays.

For example, if the full result object were:

{"obj": {"a": ..., "b": ...}, "arr": [1, 2, 3, 4]}

then this restricted to max-depth=2 would return:

{"obj": ["a", "b"], "arr": 4}

That is, the value of obj was replaced by the list of the object's member names and the value of array arr was replaced by its length.

Non-Transactional Updates

When a modification request to a resource is not executed transactionally, it means that it may be possible to observe the modification of that resource before or after modifications to the other resources being updated within the same request. It is also possible for the modification to take effect even though the overall request may end up failing with an error.

Time Point Names

Time-point names provide a way to refer to both absolute points in time as well as times relative to the current time. Specifically:

  • now: The most recent time for which the meter has collected data.

  • epoch: The time at which the meter started recording data. That is, the oldest time for which the database will return data. This time is user configurable via /config/db/epoch.

  • soy: The time at which the current year started (start-of-year).

  • som: The time at which the current month started (start-of-month).

  • sow: The time at which the current week started (start-of-week).

  • sod: The time at which the current day started (start-of-day).

  • soh: The time at which the current hour started (start-of-hour).

  • sob: The time at which the current billing period started (start-of-bill). Server-storage variable global/billing/start_day establishes the day of the month a new billing period starts. If that day is greater than the number of days in the current month, the last day of that month is taken as the start of the billing period. For simplicity, the new billing period is assumed to start at 12pm on the billing day (meter-local time).

Time Ranges

A time range is an ordered series of Unix timestamps which are spaced out at a fixed interval starting from an initial, older, point in time to a final, younger point in time. In this API, time ranges are written as three decimal numbers, separated by colons: start:step:stop, where start is the initial timestamp, step is the interval between timestamps (in seconds), and stop is the final timestamp. For example, 100:1:103 would correspond to the timestamp series [100, 101, 102, 103]. If step and the following colon are left out, the interval defaults to one second. If the timerange consists of only a single number stop, it is interpreted as a singleton consisting of only the specified stop time.

The timestamps are generated from youngest to oldest. Thus, if the oldest timestamp is not an integer-multiple of step apart from youngest, then the oldest timestamp will not be in the series of generated timestamp.

The start and stop times of an interval may also be written as one of the time-point names and, optionally, a number (in seconds) can be added or subtracted from such a name. For example, now-100 refer to the point in time that is 100 seconds before the current time of the meter.

When a time range is used to select rows from the database, the resulting timestamps may not align with the timestamps of the rows stored in the database. When this happens, the meter will, by default, round down the specified timestamp to that of the nearest older row. However, if the starting or ending timestamp starts with a plus sign (+), the meter will instead round the timestamp up to that of the nearest younger row.

Formal Definition

The full syntax for a time range is given in EBNF syntax below:

TIME_RANGE = [FROM ":" [STEP ":"]]TO .
POINT = "now"|"epoch"|"soy"|"som"|"sow"|"sod"|"soh"|"sob" .

Type Codes

Each register records values in a physical unit indicated by a type code. Apart from the physical unit, the type code also defines the quantum with which a value is recorded in the database.

To understand the role of the quantum, you need to know that the database stores all values as signed 64-bit integer numbers. For all type codes except d (discrete numbers), the meter accumulates values before storing them in the database. Let us see how this is done for a sensor that measures a voltage. If we look up type code V in the table below, we see that the quantum q for a voltage is 0.001. Now, suppose the voltage v of a sensor was measured to be 120V on average over a measurement interval dt of one second and that the previous accumulated value of that sensor was c0. The meter would then calculate the new accumulated value c1 as:

c1 = c0 + round(v / quantumdt


c1 = c0 + round(120V / 0.001)·1s = c0 + 120000 V·s

This new accumulated value is then stored in the database. In other words, for every second where the average voltage is 120V, the value stored in the database would increase by 120000. This also shows that the accumulated values stored in the database have a unit that is the rate unit multiplied by seconds. For volts, that turns into volt-seconds. Similarly, power in watts would be recorded as watt-seconds (or joules), and speed in meters-per-second would be recorded as meters.

Note that an accumulated value may eventually overflow if the measured rate has predominantly the same sign for a very long period of time. If that were to happen, the value would wrap around from a large positive value to a large negative value or vice versa. The quanta have been selected such that under normal circumstances, wrap-arounds will not occur within the lifetime of a meter. Nevertheless, when calculating how much an accumulated value changed between two points in time, we recommend calculating that difference modulo 263 since that will give the correct result provided at most one wrap-around occurred between the two points in time.

Discrete numbers (type code d) are unit-less and are used to record discrete states (such as error states or bitsets). Such quantities cannot be averaged and hence they are not accumulated. Instead, they are stored directly as signed 64-bit integers in the database.

Type code Physical quantity Rate unit Quantum
# Whole number 1
#3 Number with 3 decimal places 0.001
% Percentage % 0.001
$ Monetary accrual rate ${currency}/s 2-29
a Angle ° 0.001
aq Air quality index (0=good, 500=bad) s 0.001
d Discrete number 1
Ee Irradiance W/m2 1
F Frequency Hz 0.001
h Relative humidity % 0.001
I Electrical current A 0.001
m Mass g 0.001
P Power W 1
Pa Pressure Pa 1
ppm Parts per million ppm 0.001
var Reactive power var 1
Q Mass flow g/s 1
Qe Electric charge Ah 0.001
Qv Volumetric flow m3/s 10-9
R Electric resistance Ω 1
S Apparent power VA 1
T Temperature °C 0.001
THD Total harmonic distortion % 0.001
V Voltage V 0.001
v Speed m/s 0.001

Note For the monetary unit, ${currency} should be replaced by the the currency symbol applicable to the region the meter is installed in. The builtin user interface of the meter uses the value of server-storage variable global/default/currency_symbol for this purpose or, if undefined, a dollar sign ($).

Password Hashes

Passwords are never written directly to this API. Instead, only hashes are written which are derived from the user name, realm, and password. Specifically, the hash is calculated as the MD5 sum over the string obtained when concatenating the user name, the realm, and the password while using colons as field-separators. For example, the hash of user name jane, realm domain, and password secret, would be:

MD5("jane:domain:secret") = 251910de04f5eab86859939167d4fded

Physical Register Names

Physical register names may not be empty, contain control characters, dots (.), or commas (,). They may also not consist entirely of digits.

Register names of locally calculated registers (registers with dev set to local) attach special meaning to the last character (suffix):

  • +: Allowed only for power-registers (type code P). Only positive power will be accumulated.

  • -: Allowed only for power-registers (type code P). Only negative measured power will be accumulated.

  • |: Allowed only for power-registers (type code P). The absolute value of the measured power will be accumulated.

  • *: Allowed only and required for apparent-power registers (type code S).

These suffixes are not allowed for registers whose value is calculated by an eScript expression.

If the name of a locally calculated power register does not end with one of the above suffixes, the measured net real power is accumulated. Net real power may be positive or negative, depending on the direction of the power flow.

Serial Ports

A serial port may be specified either as a device name or as a USB path. A device name must have the format /dev/ttyUSBn, where n is a non-negative integer. A USB path must have the format USBpath where path is a sequence of one or more non-negative integers, separated by dots (.). Resource /sys/dev/serial returns a list of serial ports detected by the meter.

A serial port string may optionally also specify serial parameters. The parameters must follow the device name and have the format :b/8ps where b is the baud rate (positive integer), 8 is the number of bits per character (must be 8), p is the parity (n for none, e for even, o for odd), and s is the number of stop bits (1 or 2).

Unix Timestamp

A Unix timestamp is a number that counts the seconds since the start of January 1, 1970 UTC.

User Privileges

Users may have the one or more of the following privileges:

  • unlimited_save: The user may change (save) the meter configuration.

  • local_save: The user may change (save) the meter configuration but only when connected over a local network connection. LAN-connections are considered local if the user's browser is on the same subnet as the meter. Bluetooth connections are always considered local. All other connections (e.g., via proxy server) are considered not local.

  • view_settings: The user may view the meter configuration. Without this privilege, the user only has access to configuration settings that are directly related to the user.

  • ctrl: The user may issue control operations such as putting a device in a particular operational state (e.g., opening or closing a relay or setting the temperature on a thermostat).

  • restricted_view: The user only has restricted access to the meter data. Specifically, the user may only view registers in the view that matches the user name.

While access to the device is generally governed by these privileges, there are two exceptions:

* Users without `view_settings` privilege may still read their
  own user configuration (but not that of any other user).

* Users without `save` privilege (`unlimited_save` or
  `local_save`) may still change their own password.

View Names

Views are used to group related registers. For example, if a single meter measures multiple apartments, a separate view could be defined for each apartment. A user-interface can then offer to display all measurements for a particular apartment by selecting the desired apartment's view name. Similarly, users can be setup so that they may access only a particular view. That way, views can be used to ensure, for example, that each apartment tenant can only view their own data.

In this API, view names are defined as part of a register name. Specifically, the view name is written as prefix of the register name, followed by a dot. For example, the string apt1.cooktop defines register cooktop as being part of view apt1.

Virtual Register Formulas

The values of virtual registers are calculated based on the values of physical registers. The formulas for these calculations are limited to addition and subtraction. When expressed as a JSON string, virtual register formulas are written as a sequence of register names which are prefixed either by a plus sign (+) to indicate addition or by a minus sign (-) to indicate subtraction. The register names are enclosed in double-quotes. Within a register name, a double-quote character can be included by prefixing it with a backslash character: \". Likewise, to include a literal backslash character in the name, it must be doubled up and written as \\.

As an example, the formula:

+"Panel \"A\""+"Solar+"-"EV"

would calculate the virtual register value by adding the values of registers Panel "A" and Solar+ and then subtracting the value of register EV.


Old meters may still use deprecated operators in virtual register formulas. The syntax for these operators is op(reg,n) where op is either MIN or MAX (case-sensitive), reg is a register name, and n is an integer constant (usually 0). These operators are deprecated because they only work correctly when the formula is applied to rates, not when applied to accumulated register values. When applied to rates, these operators should work as follows:

  • MAX(reg,n): Returns the value of register reg if it is greater than n or n otherwise.

  • MIN(reg,n): Returns the value of register reg if it is less than n or n otherwise.