top of page

Energy monitoring system based on REST API Smart Meter.

Iot | Electrical Power Supply Parameters | RestAPI | JSON | Aggregation | SQL Database | Grafana | Real-time Dashboards

In this illustrative solution, InDriver collects and processes electricity parameters from the Shelly 3EM smart meter. Through InDriver's tasks, the data is meticulously completed, synchronized with the clock, and aggregated into 1-minute, 15-minute, 1-hour, and 1-day series, before being stored in the SQL database. Leveraging the Time Series API (TSAPI), visualizing the data in Grafana becomes seamless, allowing easy sharing on the Internet through web browsers

Data flow diagram

Task 1 - RestAPI Data Acquisition, Processing, and Logging into an SQL Table.

Upon starting InDriver's task, the REST API data request is defined.

OnStartup:

InDriver.import("RestApi");

RestApi.defineRequest('shelly','{"url":"https://shelly-56-eu.shelly.cloud/device/status","data":"id={id}&auth_key={key}","timeout":5000, "type":"post","headers":{"ContentTypeHeader":"application/json"}}');

InDriver.installHook(30000);

LOOP

InDriver sends REST API data requests at regular intervals, such as every configured period, for example, every 30 seconds, synchronized to the clock - 00:00:00, 00:00:30, 00:01:00, and so on.

OnHook:

RestApi.sendRequest('shelly');

 

if (RestApi.isSucceeded()) {

let ts= InDriver.hookTs();

const text = RestApi.getData('shelly');

const obj = JSON.parse(text);

let energy1 = Math.abs(obj.data.device_status.emeters[0].total_returned);

let energy2 = Math.abs(obj.data.device_status.emeters[1].total_returned);

let energy3 = Math.abs(obj.data.device_status.emeters[2].total_returned);

let power1 = Math.abs(obj.data.device_status.emeters[0].power);

let power2 = Math.abs(obj.data.device_status.emeters[1].power);

let power3 = Math.abs(obj.data.device_status.emeters[2].power);

let data = {

power1 : power1,

power2 : power2,

power3 : power3,

voltage1 : Math.abs(obj.data.device_status.emeters[0].voltage),

voltage2 : Math.abs(obj.data.device_status.emeters[1].voltage),

voltage3 : Math.abs(obj.data.device_status.emeters[2].voltage),

current1 : Math.abs(obj.data.device_status.emeters[0].current),

current2 : Math.abs(obj.data.device_status.emeters[1].current),

current3 : Math.abs(obj.data.device_status.emeters[2].current),

energy1 : energy1,

energy2 : energy2,

energy3 : energy3,

energy_total: energy1 + energy2 + energy3,

power_total: power1 + power2 + power3

}

let shelly = {Shelly:data};

let list = [];

list.push(shelly);

InDriver.debug(JSON.stringify(list));

 

InDriver.sendMessage(ts, 'DeviceData', JSON.stringify(list));

InDriver.sqlExecute("azureserver",

"insert into public.shelly (source, ts, data )

values ( 'Shelly','"+ts.toISOString()+"',$$"+JSON.stringify(list)+"$$);");

}

obj = {"isok":true,"data":{"online":true,"device_status":{"ram_free":32692,"mac":"34945477F146","fs_size":233681,"fs_free":154616,"getinfo":{"fw_info":{"device":"shellyem3-34945477F146","fw":"20221027-110030\/v1.12.1-ga9117d3"}}, "ram_total":49928, "emeters":[{"power":-312.8,"pf":-0.93, "current":1.42, "voltage":238.72, "is_valid":true, "total":0, "total_returned":1699355},{"power":-281.56,"pf":-0.8, "current":1.46, "voltage":240.94, "is_valid":true, "total":20.5, "total_returned":1734052.1},{"power":-10.16,"pf":-0.26, "current":0.16, "voltage":240.05, "is_valid":true,"total":0,"total_returned":542968.2}],"serial":17383,"has_update":true,"unixtime":1699182633,"cloud":{"enabled":true,"connected":true},"actions_stats":{"skipped":0},"v_data":1,"total_power":-604.52, "uptime":171876,"mqtt":{"connected":false},"_updated":"2023-11-05 13:11:49","cfg_changed_cnt":0,"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"is_valid":true,"source":"i...ip":"192.168.0.111","rssi":-74},"fs_mounted":true,"update":{"status":"pending", "has_update":true, "new_version":"20230913-114244\/v1.14.0-gcb84623","old_version":"20221027-110030\/v1.12.1-ga9117d3"}}}}

Key energy parameters, such as power, voltage, current, and energy, are extracted from the data. Additionally, specific calculations are executed, particularly in correcting the current direction, a crucial step in cases of incorrect current transformer connections.

The resulting processed data is stored in a JSON object, as depicted below:

data = {"power1": 312.8,"power2": 281.56, "power3":10.16, "voltage1":238.72, "voltage2":240.94, "voltage3":240.05, "current1":1.42, "current2":1.46, "current3":0.16, "energy1":1699355, "energy2":1734052.1, "energy3":542968.2,"energy_total":3976375.3, "power_total':604.45}

Here are the energy parameters received in JSON format

from a smart meter via a REST API:

Finally, the processed energy parameters are stored in an SQL table, illustrated below:

select * from public.shelly order by ts desc limit 10

Energy parameters in JSON format logged into SQL table

Task 2 - Data aggregation

In the second task, the Data Aggregation Algorithm from the Time Series API (TsApi) Library is executed, as presented below. The TsApi is available with InDriver.

onStartup:

InDriver.import("TsApi");

InDriver.installHook(60000);

TsApi.defineAggregator("ShellyAggregation","azureserver","shelly","Europe/Warsaw",'["Shelly"]');

In this example, aggregation is performed at a frequency of every 1 minute.

onHook:

TsApi.aggregate("ShellyAggregation");

After applying the aggregation algorithm, SQL tables are generated for specific intervals, including default options of 1 minute, 15 minutes, 1 hour, and 1 day. For the 'shelly' input table, corresponding tables emerge: 'shelly_1minute,' 'shelly_15minutes,' 'shelly_1hour,' and 'shelly_1day.

select * from public.shelly_1hour order by ts desc limit 10

Energy parameters collected from the smart meter are stored in an SQL table in JSON format

Additionally, each aggregation table incorporates an 'extras' column, capturing statistical values from the previous interval. This includes minimum (min), maximum (max), average (avg), and delta values. The 'extras' column also indicates the status, distinguishing between values derived from real data ('real') and those estimated/interpolated ('estimated').

With continually calculated complete aggregation tables for JSON input from any source, effortlessly create visualizations, and dashboards, perform additional calculations, or generate reports.

Example row from the 'shelly_1hour' aggregated table:

source

ts

data

extras

Shelly

2023-11-06 18:00:00+00

Aggregated and Interpolated Energy Parameters (power, current, voltage, energy) in JSON format in SQL table.
Aggregated Energy Meter Data in JSON format

Thanks to the described data flow implemented with InDriver, creating visualizations becomes straightforward.

 

The aggregation and delta calculations for each interval yield instant energy consumption values. InDriver transforms JSON objects from the REST API into real-time tables, creating data that's dashboard-ready.

select ts, (extras->'statistics'->0->'Shelly'->'energy_total'->>'delta')::double precision as energy_consumption

from public."shelly_1hour" where "source" = 'Shelly' and ts >=  date_trunc('month', now() ) and ts <= now()

Energy Consumption Chart from SQL Query
bottom of page