Slimme Meter

Slimme meters zijn uitgerust met een P1 poort. Hierop wordt elke 10 seconden een bericht verstuurd met informatie over je stroomverbruik en (indien van toepassing) levering. Voor iets van 10-15 euro kan je een kabeltje kopen dat die berichten via USB uitleesbaar maakt. Daar heeft openHAB nog niks aan, je moet het nog koppelen. Dat kan via een DSMR binding. Ik had gedoe die aan de praat te krijgen en dacht hoe moeilijk kan het zijn om zelf even wat in elkaar te flansen. Een voordeel is dat je dat via een browser ook de ruwe data kan opvragen.

Ik gebruik altijd NodeJS voor dit soort grappen omdat je alles in javascipt kan maken. Bekend terrein voor mij. Dit komt niet mee met openHAB dus zal je zelf moeten installeren, de handleiding staat ook op deze site. Daarna kan je zelf scripts schrijven. Denk je 'is allemaal bekend terrein' kan je gelijk door naar mijn github code.

var serial = require("serialport");

var serialPort = new serial("/dev/ttyUSB_POWER", {
  baudRate: 115200,
  databits: 8,
  parity: "none",
  stopbits: 1
});

serialPort.on("open", function() {
  console.log("Opened P1 port");

  serialPort.on("data", function(buffer) {
    console.log(buffer);
  }
}

Bovenstaande code opent een seriele poort en gaat de invoer uitlezen. Vrij standaard code, mits je bekend bent met nodeJS. Enige bijzondere is de naam van de seriele poort ttyUSB_POWER via een alias/snelkoppeling. Als je meer dan 1 USB device hebt, kan de raspberry bij een herstart de poorten (qua nummer) anders toewijzen. En dat is niet handig natuurlijk als je hard naar 1 van die nummers verwijst. Dit kan je oplossen door ze een naam te geven die altijd naar hetzelfde apparaat verwijst. Ook de handleiding hiervoor staat op deze site.

//webserver init
const http = require('http');
const PORT = 3001;

const server = http.createServer((req, res) => {
  let data = '';

  //read data
  req.on('data', chunk => {
    data += chunk;
  });

  //on completion
  req.on('end', () => {

    console.log('request '+data);

    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('hello world');

  });

});

server.listen(PORT, () => {
  console.log('Server running on port ${PORT}.');
});

Bovenstaande code maakt een webserver die luistert naar poort 3001 waarop je webpagina's of webservices kan laten antwoorden. Wederom een vrij standaard constructie. Meest bijzondere is dat je de vraag/invoer soms niet in één keer krijgt. Vandaar de splitsing in een deel dat elke keer die binnenkomende gegevens optelt bij het eindresultaat en een deel dat indien de gegevens compleet zijn aan de slag gaat. Combineer deze met de voorgaande seriale lezer en je kan de P1 info via die webservice teruggeven.

/Ene5\XS210 ESMR 5.0

1-3:0.2.8(50)
0-0:1.0.0(171105201324W)
0-0:96.1.1(4530303437303030303037363330383137)
1-0:1.8.1(000051.775*kWh)
1-0:1.8.2(000000.000*kWh)
1-0:2.8.1(000024.413*kWh)
1-0:2.8.2(000000.000*kWh)
0-0:96.14.0(0001)
1-0:1.7.0(00.335*kW)
1-0:2.7.0(00.000*kW)
0-0:96.7.21(00003)
0-0:96.7.9(00001)
1-0:99.97.0(0)(0-0:96.7.19)
1-0:32.32.0(00002)
1-0:32.36.0(00000)
0-0:96.13.0()
1-0:32.7.0(229.0*V)
1-0:31.7.0(001*A)
1-0:21.7.0(00.335*kW)
1-0:22.7.0(00.000*kW)
0-1:24.1.0(003)
0-1:96.1.0(4730303538353330303031313633323137)
0-1:24.2.1(171105201000W)(00016.713*m3)
!8F46

De berichten die je krijgt over de P1 poort zijn platte tekst en bestaan uit een aantal regels. De meeste regels bestaan uit een code en een waarde. Bij het inschakelen van de hardware kan je incomplete berichten krijgen. Om die uit te filtereren kan je misbruik maken van de structuur. De laatste regel is altijd iets met een "!" en de eerste regel heeft een "/".

var lines = bericht.split('\n');

lines.forEach(function(line, i) {
  if (line.length > 4 && line.charAt(3) == ':') {
    //valid value, get type and value

    var meterType = getStringBetween(line, ':', '(');
    var meterValue = convertToIntegerValue(getStringBetween(line, '(', '*'));
    switch (meterType) {
    case '1.8.1':
      json.stand.verbruikLaag = meterValue;
      read++;
      break;
    case '1.8.2':
      json.stand.verbruikHoog = meterValue;
      read++;
      break;
    case '2.8.1':
      json.stand.leveringLaag = meterValue;
      read++;
      break;

    //etc, etc, etc

    default:
      // unknown meter type
      console.log("unknown: " + line);
    }
  }
});

Het specifieke deel van de code is het regel voor regel lezen van het bericht en op basis van de codes (1.8.1 etc) de betreffende waarde in een JSON object zetten. Dit json object kan je dan op via de webservice teruggeven met response=JSON.stringify. Er valt natuurlijk veel meer te vertellen maar vermoedelijk ben je al afgehaakt of ik trap open deuren in ;-). Dus wil je meer lezen verwijs ik naar het topic over slimme meters op tweakers.

Het complete nodeJS script met bijbehorende openHAB thing en instructies zijn te vinden op github. Downloaden en op je raspberry zetten en met PM2 op de achtergrond (als service) op de raspberry kan laten draaien. Belangrijkste als je het alleen voor je slimme meter wilt gebruiken is in de configuratie 'const ENPHASE=false' te zetten. Doe je dit niet probeert hij ook de productie op je zonnepanelen (met enphase micro-omvormers) mee te nemen. Hierna zijn er twee (of drie als je de zonnepanelen service ook aanzet, lees hier meer info over deze optie).

http://localhost:3001/energy (op de raspberry zelf)
http://[naam of ip-adres]:3001/energy (elders op je netwerk)

Het antwoord zal zijn:
{"stand":{"verbruikLaag":16069589,"verbruikHoog":10341437,"leveringLaag":7262018,"leveringHoog":16930966},"verbruik":{"totaal":2036,"L1":800,"L2":570,"L3":666},"levering":{"totaal":0,"L1":0,"L2":0,"L3":0},"netto":{"totaal":2036,"L1L2L3":2036,"L1":800,"L2":570,"L3":666},"delta":{"verbruik":{"totaal":0,"L1":-3,"L2":-2,"L3":5},"levering":{"totaal":0,"L1":0,"L2":0,"L3":0},"netto":{"totaal":0,"L1":-3,"L2":94,"L3":5}},"timestamp":1699198916,"index":0,"tarief":"laag","read":13,"verbruikLaag":2036,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0,"productie":{"now":70,"today":2295}}

http://localhost:3001/history (op de raspberry zelf)
http://[naam of ip-adres]:3001/history (elders op je netwerk)

Het antwoord zal zijn:
{"dag":{"productie":2901,"dag":6,"levering":1960,"verbruik":7089,"verbruikLaag":7089,"verbruikHoog":0,"leveringLaag":1960,"leveringHoog":0},"uur":[{"dag":6,"uur":0,"productie":0,"levering":0,"verbruik":737,"verbruikLaag":737,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":1,"productie":0,"levering":0,"verbruik":852,"verbruikLaag":852,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":2,"productie":0,"levering":0,"verbruik":459,"verbruikLaag":459,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":3,"productie":0,"levering":0,"verbruik":465,"verbruikLaag":465,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":4,"productie":0,"levering":0,"verbruik":448,"verbruikLaag":448,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":5,"productie":0,"levering":0,"verbruik":451,"verbruikLaag":451,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":6,"productie":0,"levering":0,"verbruik":432,"verbruikLaag":432,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":7,"productie":0,"levering":0,"verbruik":495,"verbruikLaag":495,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":8,"productie":36,"levering":15,"verbruik":729,"verbruikLaag":729,"verbruikHoog":0,"leveringLaag":15,"leveringHoog":0},{"dag":6,"uur":9,"productie":91,"levering":31,"verbruik":232,"verbruikLaag":232,"verbruikHoog":0,"leveringLaag":31,"leveringHoog":0},{"dag":6,"uur":10,"productie":105,"levering":47,"verbruik":202,"verbruikLaag":202,"verbruikHoog":0,"leveringLaag":47,"leveringHoog":0},{"dag":6,"uur":11,"productie":209,"levering":96,"verbruik":296,"verbruikLaag":296,"verbruikHoog":0,"leveringLaag":96,"leveringHoog":0},{"dag":6,"uur":12,"productie":522,"levering":469,"verbruik":12,"verbruikLaag":12,"verbruikHoog":0,"leveringLaag":469,"leveringHoog":0},{"dag":6,"uur":13,"productie":309,"levering":133,"verbruik":297,"verbruikLaag":297,"verbruikHoog":0,"leveringLaag":133,"leveringHoog":0},{"dag":6,"uur":14,"productie":874,"levering":927,"verbruik":11,"verbruikLaag":11,"verbruikHoog":0,"leveringLaag":927,"leveringHoog":0},{"dag":6,"uur":15,"productie":600,"levering":197,"verbruik":251,"verbruikLaag":251,"verbruikHoog":0,"leveringLaag":197,"leveringHoog":0},{"dag":6,"uur":16,"productie":147,"levering":41,"verbruik":142,"verbruikLaag":142,"verbruikHoog":0,"leveringLaag":41,"leveringHoog":0},{"dag":6,"uur":17,"productie":5,"levering":0,"verbruik":558,"verbruikLaag":558,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":6,"uur":18,"productie":0,"levering":0,"verbruik":10,"verbruikLaag":10,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":5,"uur":19,"productie":0,"levering":0,"verbruik":300,"verbruikLaag":300,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":5,"uur":20,"productie":0,"levering":0,"verbruik":329,"verbruikLaag":329,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":5,"uur":21,"productie":0,"levering":0,"verbruik":227,"verbruikLaag":227,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":5,"uur":22,"productie":0,"levering":0,"verbruik":456,"verbruikLaag":456,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0},{"dag":5,"uur":23,"productie":0,"levering":0,"verbruik":166,"verbruikLaag":166,"verbruikHoog":0,"leveringLaag":0,"leveringHoog":0}]}

De services kan je aanroepen op de openHAB server zelf of vanaf een browser op een andere PC/telefoon/tablet op je lokale netwerk. Elk geven ze een JSON resultaat met informatie die je in openHAB kan gebruiken.

UID: http:url:energie_meter
label: Energie Meter
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://localhost:3001/energy
  delay: 0
  stateMethod: GET
  refresh: 10
  commandMethod: GET
  timeout: 3000
  bufferSize: 2048
channels:
  - id: netto_totaal
    channelTypeUID: http:number
    label: netto totaal
    description: null
    configuration:
      mode: READONLY
      stateTransformation: JSONPATH:$.netto.totaal
  - id: netto_L1
    channelTypeUID: http:number
    label: netto L1
    description: null
    configuration:
      mode: READONLY
      stateTransformation: JSONPATH:$.netto.L1

Je kan deze services in openHAB aanroepen met de HTTP binding. Installeer deze (als je dat nog niet gedaan hebt) en je kan met behulp van bovenstaande url's een thing maken. Daarbij zul je ook de plugin JSONPATH add-on nodig hebben, installeer die ook als je dat nog niet gedaan had. Daarmee kan je een specifieke variabele uit het JSON bericht in een channel zetten. Dit kan je met de GUI doen of met YAML op het code tabblad. Voorbeeldje hierboven. Je kan ook lui zijn en die van mij gebruiken, hij staat ook op github. Download de yaml code en plak die in het code tabblad van een nieuw thing. Kies daarna in the gemaakte en opgeslagen thing dat je equipements/items wil genereren en daarna komen de gegevens ook in openHAB door voor gebruik in pages en rules.

Ik heb hem in een tile op een dashboard. De widget hiervoor staat ook op github. Enige verplichte configuratie is het aanwijzen van het item dat je wilt weergeven. Indien nodig kan je schaal en indeling aanpassen via de instellingen van de widget. De heigth is een workaround voor als je werkt met hele kleine meters op een tile, geeft bijv. 50,100 of 200 op om hem goed weer te geven (anders 'schuift' ie uit beeld met kleine tiles). Zo kan je ook een 2e item aanwijzen die bijvoorbeeld het maximum of gemiddelde verbruik aangeeft. Of je gaat los en kan hem helemaal aanpassen naar eigen wensen, tenslotte kan je met de YAML code alle kanten op.

1