diff --git a/lib/parsePacket.js b/lib/parsePacket.js index f0b4867..ced69ee 100644 --- a/lib/parsePacket.js +++ b/lib/parsePacket.js @@ -119,6 +119,34 @@ function parsePacket(packet) { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, gas: { @@ -394,6 +422,54 @@ function parsePacket(packet) { parsedPacket.electricity.fuseThreshold.unit = 'A'; break; + /* + * Demands mappings, seen in Belgian peak tariff with meterType FLU5\253770234_A v50217. + */ + case "1-0:1.4.0": // Positive active demand in a current demand period (A+) [kW] + parsedPacket.electricity.demand = parsedPacket.electricity.demand || {}; + parsedPacket.electricity.demand.positiveActiveDemand = parsedPacket.electricity.demand.positiveActiveDemand || {}; + parsedPacket.electricity.demand.positiveActiveDemand.currentDemandPeriod = {}; + parsedPacket.electricity.demand.positiveActiveDemand.currentDemandPeriod.reading = parseFloat(line.value); + parsedPacket.electricity.demand.positiveActiveDemand.currentDemandPeriod.unit = line.unit; + break; + + case "1-0:2.4.0": // Negative active demand in a current demand period (A-) [kW] + parsedPacket.electricity.demand = parsedPacket.electricity.demand || {}; + parsedPacket.electricity.demand.negativeActiveDemand = parsedPacket.electricity.demand.negativeActiveDemand || {}; + parsedPacket.electricity.demand.negativeActiveDemand.currentDemandPeriod = {}; + parsedPacket.electricity.demand.negativeActiveDemand.currentDemandPeriod.reading = parseFloat(line.value); + parsedPacket.electricity.demand.negativeActiveDemand.currentDemandPeriod.unit = line.unit; + break; + + case "1-0:1.6.0": // Positive active maximum demand (A+) total [kW] + parsedPacket.electricity.demand = parsedPacket.electricity.demand || {}; + parsedPacket.electricity.demand.positiveActiveDemand = parsedPacket.electricity.demand.positiveActiveDemand || {}; + parsedPacket.electricity.demand.positiveActiveDemand.maximumDemand = {}; + parsedPacket.electricity.demand.positiveActiveDemand.maximumDemand.timestamp = _parseTimestamp(line.value); + const padmdVal = _parseLine(line.obisCode + lines[i].substring(24)); + parsedPacket.electricity.demand.positiveActiveDemand.maximumDemand.reading = parseFloat(padmdVal.value); + parsedPacket.electricity.demand.positiveActiveDemand.maximumDemand.unit = padmdVal.unit; + break; + + case "1-0:2.6.0": // Negative active maximum demand (A-) total [kW] + parsedPacket.electricity.demand = parsedPacket.electricity.demand || {}; + parsedPacket.electricity.demand.negativeActiveDemand = parsedPacket.electricity.demand.negativeActiveDemand || {}; + parsedPacket.electricity.demand.negativeActiveDemand.maximumDemand = {}; + parsedPacket.electricity.demand.negativeActiveDemand.maximumDemand.timestamp = _parseTimestamp(line.value); + const nadmdVal = _parseLine(line.obisCode + lines[i].substring(24)); + parsedPacket.electricity.demand.negativeActiveDemand.maximumDemand.reading = parseFloat(nadmdVal.value); + parsedPacket.electricity.demand.negativeActiveDemand.maximumDemand.unit = nadmdVal.unit; + break; + + case "0-0:98.1.0": // Maximum demand - Active energy import of the last 13 months + const mdaeil13m = _parseMaximumDemandLastMonths(lines[i]); + if(mdaeil13m != -1){ + parsedPacket.electricity.demand = parsedPacket.electricity.demand || {}; + parsedPacket.electricity.demand.maximumDemandLastMonths = parsedPacket.electricity.demand.maximumDemandLastMonths || {}; + parsedPacket.electricity.demand.maximumDemandLastMonths = mdaeil13m; + } + break; + /* * Handling anything not matched so far */ @@ -505,6 +581,52 @@ function _parsePowerFailureEventLog(value) { return output; } +/** + * + * @param value : the capture buffer. Example: + * 0-0:98.1.0(10)(1-0:1.6.0)(1-0:1.6.0)(230201000000W)(230129181500W)(05.380*kW)(230301000000W)(230210183000W)(05.769*kW)(230401000000S)(230312180000W)(06.309*kW)(230501000000S)(230410180000S)(05.310*kW)(230601000000S)(230506184500S)(04.369*kW)(230701000000S)(230610071500S)(05.253*kW)(230801000000S)(230727003000S)(04.364*kW)(230901000000S)(230824173000S)(04.689*kW)(231001000000S)(230923121500S)(04.278*kW)(231101000000W)(231014121500S)(05.416*kW) + */ +function _parseMaximumDemandLastMonths(value) { + const split = value.substring(0, value.length-1).split(")("); + if (split[0] && split[1]) { + let output = { + count: parseInt(split[0].substring(11)), + months: [] + }; + //console.log("_parseMaximumDemandLastMonths", split, "output", output); + + // Loop the buffer structure: timestamp)(timestamp)(value*unit)... + for (let i = 0; i < split.length;) { + if(split[i] == "0-0:98.1.0(10" || split[i] == "1-0:1.6.0"){ + i++; + continue; + } + else { + try{ + + const monthEntry = { + month: _parseTimestamp(split[i]), + timestamp: _parseTimestamp(split[i+1]), + value: parseFloat(split[i+2].split('*')[0]), + unit: split[i+2].split('*')[1], + } + //console.log("monthEntry", split[i], monthEntry); + output.months.push(monthEntry); + } catch (ex){ + // something is wrong here + } + i+=3; + } + } + return output; + } + else + { + return -1; + } + +} + /** * Parse hourly readings, which is used for gas, water, heat, cold and slave electricity meters * diff --git a/package.json b/package.json index cd12ae1..817aa54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "p1-reader", - "version": "2.0.4", + "version": "2.0.5", "description": "Node.js package for reading and parsing data from the P1 port of a Smart Meter", "main": "main.js", "author": "Ruud Verheijden", diff --git a/spec/parsePacketSpec.js b/spec/parsePacketSpec.js index b823093..d747166 100644 --- a/spec/parsePacketSpec.js +++ b/spec/parsePacketSpec.js @@ -127,6 +127,34 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, gas: { @@ -320,6 +348,34 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, "gas": { @@ -497,6 +553,34 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, "gas": { @@ -649,6 +733,34 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, "gas": { @@ -802,6 +914,34 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, "gas": { @@ -991,6 +1131,34 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, "gas": { @@ -1177,6 +1345,34 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } } }, "gas": { @@ -1363,6 +1559,313 @@ describe("parsePacket", function() { } } } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: null, + months: null + } + } + }, + "gas": { + "deviceType": "003", + "equipmentId": "3232323241424344313233343536373839", + "timestamp": "2010-12-09T11:25:00.000Z", + "reading": 12785.123, + "unit": "m3", + "valvePosition": null + } + }; + + expect(parsedPacket).toEqual(expectedOutputObject); + }); + + // Test demands code seen on Belgian meters (peak tariff) + it("should be able to parse a complete DSMR5.0 packet with demands", function() { + const packet = "/FLU5\253770234_A\r\n" + + "\r\n" + + "1-3:0.2.8(50217)\r\n" + + "0-0:1.0.0(101209113020W)\r\n" + + "0-0:96.1.1(4B384547303034303436333935353037)\r\n" + + "1-0:1.8.1(123456.789*kWh)\r\n" + + "1-0:1.8.2(123456.789*kWh)\r\n" + + "1-0:2.8.1(123456.789*kWh)\r\n" + + "1-0:2.8.2(123456.789*kWh)\r\n" + + "0-0:96.14.0(0002)\r\n" + + "1-0:1.7.0(01.193*kW)\r\n" + + "1-0:2.7.0(00.000*kW)\r\n" + + "0-0:96.7.21(00004)\r\n" + + "0-0:96.7.9(00002)\r\n" + + "1-0:99.97.0(2)(0-0:96.7.19)(101208152415W)(0000000240*s)(101208151004W)(0000000301*s)\r\n" + + "1-0:32.32.0(00002)\r\n" + + "1-0:52.32.0(00001)\r\n" + + "1-0:72.32.0(00000)\r\n" + + "1-0:32.36.0(00000)\r\n" + + "1-0:52.36.0(00003)\r\n" + + "1-0:72.36.0(00000)\r\n" + + "0-0:96.13.0(303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F)\r\n" + + "1-0:32.7.0(220.1*V)\r\n" + + "1-0:52.7.0(220.2*V)\r\n" + + "1-0:72.7.0(220.3*V)\r\n" + + "1-0:31.7.0(001*A)\r\n" + + "1-0:51.7.0(002*A)\r\n" + + "1-0:71.7.0(003*A)\r\n" + + "1-0:21.7.0(01.111*kW)\r\n" + + "1-0:41.7.0(02.222*kW)\r\n" + + "1-0:61.7.0(03.333*kW)\r\n" + + "1-0:22.7.0(04.444*kW)\r\n" + + "1-0:42.7.0(05.555*kW)\r\n" + + "1-0:62.7.0(06.666*kW)\r\n" + + "0-1:24.1.0(003)\r\n" + + "0-1:96.1.0(3232323241424344313233343536373839)\r\n" + + "0-1:24.2.1(101209112500W)(12785.123*m3)\r\n" + + "1-0:1.4.0(00.242*kW)\r\n" + + "1-0:1.6.0(231107174500W)(07.677*kW)\r\n" + + "0-0:98.1.0(10)(1-0:1.6.0)(1-0:1.6.0)(230201000000W)(230129181500W)(05.380*kW)(230301000000W)(230210183000W)(05.769*kW)(230401000000S)(230312180000W)(06.309*kW)(230501000000S)(230410180000S)(05.310*kW)(230601000000S)(230506184500S)(04.369*kW)(230701000000S)(230610071500S)(05.253*kW)(230801000000S)(230727003000S)(04.364*kW)(230901000000S)(230824173000S)(04.689*kW)(231001000000S)(230923121500S)(04.278*kW)(231101000000W)(231014121500S)(05.416*kW)"; + + const parsedPacket = parsePacket(packet); + + const expectedOutputObject = { + "meterType": "FLU5\253770234_A", + "version": "50217", + "timestamp": "2010-12-09T11:30:20.000Z", + "equipmentId": "4B384547303034303436333935353037", + "textMessage": { + "codes": null, + "message": "0123456789:;<=>?0123456789:;<=>?0123456789:;<=>?0123456789:;<=>?0123456789:;<=>?" + }, + "electricity": { + "received": { + "tariff1": { + "reading": 123456.789, + "unit": "kWh" + }, + "tariff2": { + "reading": 123456.789, + "unit": "kWh" + }, + "actual": { + "reading": 1.193, + "unit": "kW" + } + }, + "delivered": { + "tariff1": { + "reading": 123456.789, + "unit": "kWh" + }, + "tariff2": { + "reading": 123456.789, + "unit": "kWh" + }, + "actual": { + "reading": 0, + "unit": "kW" + } + }, + "tariffIndicator": 2, + "threshold": null, + "fuseThreshold": null, + "switchPosition": null, + "numberOfPowerFailures": 4, + "numberOfLongPowerFailures": 2, + "longPowerFailureLog": { + "count": 2, + "log": [ + { + "startOfFailure": "2010-12-08T15:20:15.000Z", + "endOfFailure": "2010-12-08T15:24:15.000Z", + "duration": 240, + "unit": "s" + }, + { + "startOfFailure": "2010-12-08T15:05:03.000Z", + "endOfFailure": "2010-12-08T15:10:04.000Z", + "duration": 301, + "unit": "s" + } + ] + }, + "voltageSags": { + "L1": 2, + "L2": 1, + "L3": 0 + }, + "voltageSwell": { + "L1": 0, + "L2": 3, + "L3": 0 + }, + "instantaneous": { + "current": { + "L1": { + "reading": 1, + "unit": "A" + }, + "L2": { + "reading": 2, + "unit": "A" + }, + "L3": { + "reading": 3, + "unit": "A" + } + }, + "voltage": { + "L1": { + "reading": 220, + "unit": "V" + }, + "L2": { + "reading": 220, + "unit": "V" + }, + "L3": { + "reading": 220, + "unit": "V" + } + }, + "power": { + "positive": { + "L1": { + "reading": 1.111, + "unit": "kW" + }, + "L2": { + "reading": 2.222, + "unit": "kW" + }, + "L3": { + "reading": 3.333, + "unit": "kW" + } + }, + "negative": { + "L1": { + "reading": 4.444, + "unit": "kW" + }, + "L2": { + "reading": 5.555, + "unit": "kW" + }, + "L3": { + "reading": 6.666, + "unit": "kW" + } + } + } + }, + demand: { + positiveActiveDemand: { + currentDemandPeriod: { + reading: 0.242, + unit: "kW" + }, + maximumDemand: { + timestamp: '2023-11-07T17:45:00.000Z', + reading: 7.677, + unit: "kW" + } + }, + negativeActiveDemand: { + currentDemandPeriod: { + reading: null, + unit: null + }, + maximumDemand: { + timestamp: null, + reading: null, + unit: null + } + }, + maximumDemandLastMonths: { + count: 10, + months: [ + { + "month": "2023-02-01T00:00:00.000Z", + "timestamp": "2023-01-29T18:15:00.000Z", + "value": 5.38, + "unit": "kW" + }, + { + "month": "2023-03-01T00:00:00.000Z", + "timestamp": "2023-02-10T18:30:00.000Z", + "value": 5.769, + "unit": "kW" + }, + { + "month": "2023-04-01T00:00:00.000Z", + "timestamp": "2023-03-12T18:00:00.000Z", + "value": 6.309, + "unit": "kW" + }, + { + "month": "2023-05-01T00:00:00.000Z", + "timestamp": "2023-04-10T18:00:00.000Z", + "value": 5.31, + "unit": "kW" + }, + { + "month": "2023-06-01T00:00:00.000Z", + "timestamp": "2023-05-06T18:45:00.000Z", + "value": 4.369, + "unit": "kW" + }, + { + "month": "2023-07-01T00:00:00.000Z", + "timestamp": "2023-06-10T07:15:00.000Z", + "value": 5.253, + "unit": "kW" + }, + { + "month": "2023-08-01T00:00:00.000Z", + "timestamp": "2023-07-27T00:30:00.000Z", + "value": 4.364, + "unit": "kW" + }, + { + "month": "2023-09-01T00:00:00.000Z", + "timestamp": "2023-08-24T17:30:00.000Z", + "value": 4.689, + "unit": "kW" + }, + { + "month": "2023-10-01T00:00:00.000Z", + "timestamp": "2023-09-23T12:15:00.000Z", + "value": 4.278, + "unit": "kW" + }, + { + "month": "2023-11-01T00:00:00.000Z", + "timestamp": "2023-10-14T12:15:00.000Z", + "value": 5.416, + "unit": "kW" + } + ] + } } }, "gas": {