System monitorowania energii
i REST API Smart Meter.
IoT | Parametry zasilania elektrycznego | RestAPI | JSON | Agregacja | Baza danych SQL | Grafana | Dashboardy w czasie rzeczywistym
W tym przykładowym rozwiązaniu InDriver zbiera i przetwarza parametry energii elektrycznej z inteligentnego licznika Shelly 3EM. Dzięki zadaniom InDriver dane są skrupulatnie uzupełniane, synchronizowane z zegarem i agregowane w serie 1-minutowe, 15-minutowe, 1-godzinne i 1-dniowe, a następnie zapisywane w bazie danych SQL. Dzięki wykorzystaniu interfejsu API szeregów czasowych (TSAPI) wizualizacja danych w Grafanie staje się płynna, umożliwiając łatwe udostępnianie w internecie za pośrednictwem przeglądarek internetowych.
Schemat przepływu danych
Zadanie 1 — Gromadzenie, przetwarzanie i rejestrowanie danych RESTAPI w tabeli SQL.
Po uruchomieniu zadania InDriver definiowane jest żądanie danych API REST.
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);
PĘTLA
InDriver wysyła żądania danych interfejsu API REST w regularnych odstępach czasu, np. co każdy skonfigurowany okres, na przykład co 30 sekund, zsynchronizowane z zegarem — 00:00:00, 00:00:30, 00:01:00 itd.
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, „łącznie”: 0, „zwrócono łącznie”: 1699355}, {„moc”: -281,56, {„pf”: -0,8, „bieżące”: 1,46, „napięcie”: 240,94, „prawidłowe”: prawda, „łącznie”: 20,5, „zwrócono łącznie”: 1734052,1}, {„moc”: -10,16, {„pf”: -0,26, „bieżące”: 0,16, „napięcie”: 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, „nowa_wersja”: „20230913-114244\/v1.14.0-gcb84623”, „stara_wersja”: „20221027-110030\/v1.12.1-ga9117d3”}}}}
Z danych wyodrębniane są kluczowe parametry energetyczne, takie jak moc, napięcie, prąd i energia. Dodatkowo przeprowadzane są szczegółowe obliczenia, w szczególności w celu korekcji kierunku prądu, co jest kluczowym krokiem w przypadku nieprawidłowego podłączenia przekładnika prądowego.
Wynikowe przetworzone dane są przechowywane w obiekcie JSON, jak pokazano poniżej:
dane = {"moc1": 312,8,"moc2": 281,56, "moc3": 10,16, "napięcie1": 238,72, "napięcie2": 240,94, "napięcie3": 240,05, "prąd1": 1,42, "prąd2": 1,46, "prąd3": 0,16, "energia1": 1699355, "energia2": 1734052,1, "energia3": 542968,2,"suma_energii": 3976375,3, "suma_mocy': 604,45}
Oto parametry energii otrzymane w formacie JSON
z inteligentnego licznika za pośrednictwem interfejsu API REST:
Na koniec przetworzone parametry energii są zapisywane w tabeli SQL, przedstawionej poniżej:
select * from public.shelly order by ts desc limit 10

Zadanie 2 – Agregacja danych
W drugim zadaniu wykonywany jest algorytm agregacji danych z biblioteki Time Series API (TsApi), jak pokazano poniżej. TsApi jest dostępne w InDriver.
onStartup:
InDriver.import("TsApi");
InDriver.installHook(60000);
TsApi.defineAggregator("ShellyAggregation","azureserver","shelly","Europe/Warsaw",'["Shelly"]');
W tym przykładzie agregacja jest wykonywana z częstotliwością 1 minuty.
onHook:
TsApi.aggregate(" ShellyAggregation ");
Po zastosowaniu algorytmu agregacji generowane są tabele SQL dla określonych interwałów, z domyślnymi opcjami 1 minuty, 15 minut, 1 godziny i 1 dnia. Dla tabeli wejściowej „shelly” generowane są odpowiednie tabele: „shelly_1minute”, „shelly_15minute”, „shelly_1hour” i „shelly_1day”.
wybierz * z public.shelly_1hour zamów według ts desc limit 10

Dodatkowo, każda tabela agregacji zawiera kolumnę „dodatki”, która zawiera wartości statystyczne z poprzedniego interwału. Obejmuje to wartości minimalne (min.), maksymalne (maks.), średnie (śr.) i delta. Kolumna „dodatki” wskazuje również status, rozróżniając wartości pochodzące z danych rzeczywistych („rzeczywiste”) od wartości szacowanych/interpolowanych („szacowane”).
Dzięki stale obliczanym kompletnym tabelom agregacji dla danych wejściowych JSON z dowolnego źródła możesz bez wysiłku tworzyć wizualizacje i dashboardy, wykonywać dodatkowe obliczenia lub generować raporty.
Przykładowy wiersz z tabeli agregowanej „shelly_1hour”:
źródło
ts
dane
dodatki
Shelly
2023-11-06 18:00:00+00


Dzięki opisanemu przepływowi danych zaimplementowanemu w InDriver, tworzenie wizualizacji staje się proste.
Agregacja i obliczenia delty dla każdego interwału generują natychmiastowe wartości zużycia energii. InDriver przekształca obiekty JSON z interfejsu API REST w tabele w czasie rzeczywistym, tworząc dane gotowe do wyświetlenia na pulpicie nawigacyjnym.
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()


