saberone / pimatic-smartmeter

Read energy usage data from a "smartmeter" through it's P1 port
MIT License
0 stars 6 forks source link

Adapted for meter Kaifa Type: MA105 #6

Open Ruba5913 opened 8 years ago

Ruba5913 commented 8 years ago

I adapted the program a little bit for my own usage but maybe other people can use this so I put it here. These meter is wireless connected to a gasmeter Itron G4RF1e WL . Below the adapted program en screenshot. The graph is a little bit crowded because I added also tariff3 and 4 because of my solar panels and also the gasmeter.

/KFM5KAIFA-METER

1-3:0.2.8(40) 0-0:1.0.0(151013144451S) 0-0:96.1.1(4530303039303030303134373436313134) 1-0:1.8.1(000117.566_kWh) 1-0:1.8.2(000072.650_kWh) 1-0:2.8.1(000007.845_kWh) 1-0:2.8.2(000012.889_kWh) 0-0:96.14.0(0002) 1-0:1.7.0(00.369_kW) 1-0:2.7.0(00.000_kW) 0-0:17.0.0(999.9_kW) 0-0:96.3.10(1) 0-0:96.7.21(00008) 0-0:96.7.9(00006) 1-0:99.97.0(1)(0-0:96.7.19)(000101000001W)(2147483647_s) 1-0:32.32.0(00000) 1-0:32.36.0(00000) 0-0:96.13.1() 0-0:96.13.0() 1-0:31.7.0(001_A) 1-0:21.7.0(00.365_kW) 1-0:22.7.0(00.000_kW) 0-1:24.1.0(003) 0-1:96.1.0(4730303139333430323138353337363135) 0-1:24.2.1(151013140000S)(00041.604_m3)

image

Plugin pimatic-smartmeter

module.exports = (env) -> Promise = env.require 'bluebird'

assert = env.require 'cassert'

class Smartmeter extends env.plugins.Plugin

init: (app, @framework, @config) =>
  deviceConfigDef = require("./device-config-schema")

  @framework.deviceManager.registerDeviceClass("Smartmeterdevice", {
    configDef: deviceConfigDef.Smartmeterdevice,
    createCallback: (config) => new Smartmeterdevice(config)
  })

class Smartmeterdevice extends env.devices.Sensor

attributes:
  activetariff:
    description: "Active tariff"
    type: "number"
    unit: " 1/3 or 2/4"
  actualusage:
    description: "Actual usage"
    type: "number"
    unit: " Watt"
  tariff1totalusage:
    description: "Tariff 1 total usage(T1)"
    type: "number"
    unit: " kWh"
  tariff2totalusage:
    description: "Tariff 2 total usage(T2)"
    type: "number"
    unit: " kWh"
  tariff3totalredelivery:
    description: "Tariff 3 total usage(T3)"
    type: "number"
    unit: " kWh"
  tariff4totalredelivery:
    description: "Tariff 4 total usage(T4)"
    type: "number"
    unit: " kWh"
  totalgasusage:
    description: "Total gas usage"
    type: "number"
    unit: " m3"

actualusage: 0.0
activetariff: 1
tariff1totalusage: 0.0
tariff2totalusage: 0.0
tariff3totalredelivery: 0.0
tariff4totalredelivery: 0.0    
totalgasusage: 0.0 

constructor: (config) ->

  @config = config

  @id = @config.id
  @name = @config.name
  @portName = @config.serialport
  @baudRate = @config.baudRate
  @dataBits = @config.dataBits
  @parity = @config.parity
  @stopBits = @config.stopBits
  @flowControl = @config.flowControl

  super()

  if @debug
    env.logger.debug ("Smartmeter portName : \"#{@portName}\"")
    env.logger.debug ("Smartmeter baudRate : \"#{@baudRate}\"")
    env.logger.debug ("Smartmeter dataBits : \"#{@dataBits}\"")
    env.logger.debug ("Smartmeter parity : \"#{@parity}\"")
    env.logger.debug ("Smartmeter stopBits : \"#{@stopBits}\"")

  P1DataStream = require "./p1meterdata"
  p1datastream = new P1DataStream({
    portName: @portName,
    baudRate: @baudRate,
    dataBits: @dataBits,
    parity: @parity,
    stopBits: @stopBits,
    flowControl: @flowControl
  })
  p1datastream.on 'data', (data) =>
    @actualusage = Number data.currentUsage
    @emit "actualusage", Number @actualusage

    @activetariff = Number data.currentTariff
    @emit "activetariff", Number @activetariff

    @tariff1totalusage = Number data.tariffOneTotalUsage
    @emit "tariff1totalusage", Number @tariff1totalusage

    @tariff2totalusage = Number data.tariffTwoTotalUsage
    @emit "tariff2totalusage", Number @tariff2totalusage

    @tariff3totalredelivery = Number data.tariffThreeTotalRedelivery
    @emit "tariff3totalredelivery", Number @tariff3totalredelivery

    @tariff4totalredelivery = Number data.tariffFourTotalRedelivery
    @emit "tariff4totalredelivery", Number @tariff4totalredelivery

    @totalgasusage = Number data.totalGasUsage
    @emit "totalgasusage", Number @totalgasusage

getActualusage: -> Promise.resolve @actualusage
getActivetariff: -> Promise.resolve @activetariff
getTariff1totalusage: -> Promise.resolve @tariff1totalusage
getTariff2totalusage: -> Promise.resolve @tariff2totalusage
getTariff3totalredelivery: -> Promise.resolve @tariff3totalredelivery
getTariff4totalredelivery: -> Promise.resolve @tariff4totalredelivery 
getTotalgasusage: -> Promise.resolve @totalgasusage

plugin = new Smartmeter return plugin

p1meterdata.js =>

var serialport = require("serialport"); var SerialPort = serialport.SerialPort; var util = require("util"); var events = require("events");

var openSerialPort = function (opts, callback) { console.log('serialport options '); console.log(opts);

// Setup a SerialPort instance
var sp = new SerialPort(opts.portName, {
    baudRate: opts.baudRate,
    dataBits: opts.dataBits,
    parity: opts.parity,
    stopBits: opts.stopBits,
    flowControl: opts.flowControl,
    parser: serialport.parsers.readline("!")
}, false);

sp.open(function () {
    console.log('- Serial port is open');
    sp.on('data', callback);
});

return sp;

};

// Returns result from applying regex to data (string) var returnRegExResult = function (data, regex) { var result = data.match(regex);

if (result != undefined) {
    return result[1];
} else {
    return undefined;
}

}; // returnRegExResult

var P1DataStream = function (opts) { console.log('P1DataStream opts '); console.log(opts);

var self = this;
self.opts = opts;

var listener = function (data) {

    var tariffOneTotalUsage = returnRegExResult(data, /^1-0:1\.8\.1\(0+(\d+\.\d+)\*kWh\)/m);
    var tariffTwoTotalUsage = returnRegExResult(data, /^1-0:1\.8\.2\(0+(\d+\.\d+)\*kWh\)/m);
    var tariffThreeTotalRedelivery = returnRegExResult(data, /^1-0:2\.8\.1\(0+(\d+\.\d+)\*kWh\)/m);
    var tariffFourTotalRedelivery = returnRegExResult(data,  /^1-0:2\.8\.2\(0+(\d+\.\d+)\*kWh\)/m);
    var currentTariff = returnRegExResult(data, /^0-0:96.14.0\(0+(.*?)\)/m);
    var currentUsage = returnRegExResult(data, /^1-0:1.7.0\((.*?)\*/m);
    var totalGasUsage = returnRegExResult(data, /^0-1:24\.2\.1\(\d+S\)\(0+(\d+\.\d+)\*m3\)/m);

    var dataGram = {
        tariffOneTotalUsage: tariffOneTotalUsage * 1,
        tariffTwoTotalUsage: tariffTwoTotalUsage * 1,
        tariffThreeTotalRedelivery: tariffThreeTotalRedelivery * 1,
        tariffFourTotalRedelivery: tariffFourTotalRedelivery * 1,
        currentTariff: currentTariff * 1,
        currentUsage: currentUsage * 1000,
        totalGasUsage: totalGasUsage * 1
    };

    console.log('Raw data received: ' + data);
    console.log('Parsed data: ');
    console.log(dataGram);

    self.emit("data", dataGram);
};

openSerialPort(self.opts, listener);
events.EventEmitter.call(self);

};

util.inherits(P1DataStream, events.EventEmitter);

module.exports = P1DataStream;

ghost commented 8 years ago

This is just the way i want to use the reading from my meter. Thanx

jozonja commented 8 years ago

I adapted the code but it didn't have any effect, I still only see the other variables and no gas usage :-(. Can you tell me what I did wrong. ik restarted pimatic. (I'm new in this, just started using pimatic).

ghost commented 8 years ago

i'm also having the same issue and still working on it

Ruba5913 commented 8 years ago

I didn't realize that the S is for summertime. So this morning we got wintertime and I got no readings from gas usage. I adapted following: var totalGasUsage = returnRegExResult(data, /^0-1:24.2.1(\d+S)(0+(\d+.\d+)m3)/m); should be: var totalGasUsage = returnRegExResult(data, /^0-1:24.2.1(\d+[SW])(0+(\d+.\d+)m3)/m);

Ruba5913 commented 8 years ago

Sorry wrong button ;)

Ruba5913 commented 8 years ago

I am also rather new in this material. But I used this link: https://regex101.com/ to make the gas formula. It costed me also a lot of time to get it working. Most faults where mis typing and also to find the good formula for the gas usage. But now it is working very good.

jozonja commented 8 years ago

Guys, what do you do after you change the code. Do you need to compile something or just restart pimatic?

saberone commented 8 years ago

Just restart. If you make a change to config, be sure to stop pimatic first, then make the change, and restart. Otherwise your change will be overwritten.

Pimatic runs on node.js, so it's basically javascript. Pimatic uses Coffeescript heavily, which is transpiled to javascript. No need for really compiling in the traditional way.

jozonja commented 8 years ago

ok, I restarted it but still no gasusage. This is what I see schermafbeelding 2015-10-25 om 21 14 14

and your code with the added 'gas' code... (didn't use the tariff3totalredelivery and tariff4totalredelivery) is down here. I also tested the regular expression with regex101.com and it works. Maybe you can see the bug in the code.

pimatic-smartmeter.coffee

Plugin pimatic-smartmeter

module.exports = (env) -> Promise = env.require 'bluebird'

assert = env.require 'cassert'

class Smartmeter extends env.plugins.Plugin

init: (app, @framework, @config) =>
  deviceConfigDef = require("./device-config-schema")

  @framework.deviceManager.registerDeviceClass("Smartmeterdevice", {
    configDef: deviceConfigDef.Smartmeterdevice,
    createCallback: (config) => new Smartmeterdevice(config)
  })

class Smartmeterdevice extends env.devices.Sensor

attributes:
  actualusage:
    description: "Actual usage"
    type: "number"
    unit: " Watt"
  activetariff:
    description: "Active tariff"
    type: "number"
    unit: " 1 or 2"
  tariff1totalusage:
    description: "Tariff 1 total usage(T1)"
    type: "number"
    unit: " kWh"
  tariff2totalusage:
    description: "Tariff 2 total usage(T2)"
    type: "number"
    unit: " kWh"
  totalgasusage:
    description: "Total gas usage"
    type: "number"
    unit: " m3"

actualusage: 0.0
activetariff: 1
tariff1totalusage: 0.0
tariff2totalusage: 0.0
totalgasusage: 0.0 

constructor: (config) ->

  @config = config

  @id = @config.id
  @name = @config.name
  @portName = @config.serialport
  @baudRate = @config.baudRate
  @dataBits = @config.dataBits
  @parity = @config.parity
  @stopBits = @config.stopBits
  @flowControl = @config.flowControl

  super()

  if @debug
    env.logger.debug ("Smartmeter portName : \"#{@portName}\"")
    env.logger.debug ("Smartmeter baudRate : \"#{@baudRate}\"")
    env.logger.debug ("Smartmeter dataBits : \"#{@dataBits}\"")
    env.logger.debug ("Smartmeter parity : \"#{@parity}\"")
    env.logger.debug ("Smartmeter stopBits : \"#{@stopBits}\"")

  P1DataStream = require "./p1meterdata"
  p1datastream = new P1DataStream({
    portName: @portName,
    baudRate: @baudRate,
    dataBits: @dataBits,
    parity: @parity,
    stopBits: @stopBits,
    flowControl: @flowControl
  })
  p1datastream.on 'data', (data) =>
    @actualusage = Number data.currentUsage
    @emit "actualusage", Number @actualusage

    @activetariff = Number data.currentTariff
    @emit "activetariff", Number @activetariff

    @tariff1totalusage = Number data.tariffOneTotalUsage
    @emit "tariff1totalusage", Number @tariff1totalusage

    @tariff2totalusage = Number data.tariffTwoTotalUsage
    @emit "tariff2totalusage", Number @tariff2totalusage

@totalgasusage = Number data.totalGasUsage
    @emit "totalgasusage", Number @totalgasusage

getActualusage: -> Promise.resolve @actualusage
getActivetariff: -> Promise.resolve @activetariff
getTariff1totalusage: -> Promise.resolve @tariff1totalusage
getTariff2totalusage: -> Promise.resolve @tariff2totalusage
getTotalgasusage: -> Promise.resolve @totalgasusage

plugin = new Smartmeter return plugin


p1meterdata.js

var serialport = require("serialport"); var SerialPort = serialport.SerialPort; var util = require("util"); var events = require("events");

var openSerialPort = function (opts, callback) { console.log('serialport options '); console.log(opts);

// Setup a SerialPort instance
var sp = new SerialPort(opts.portName, {
    baudRate: opts.baudRate,
    dataBits: opts.dataBits,
    parity: opts.parity,
    stopBits: opts.stopBits,
    flowControl: opts.flowControl,
    parser: serialport.parsers.readline("!")
}, false);

sp.open(function () {
    console.log('- Serial port is open');
    sp.on('data', callback);
});

return sp;

};

// Returns result from applying regex to data (string) var returnRegExResult = function (data, regex) { var result = data.match(regex);

if (result != undefined) {
    return result[1];
} else {
    return undefined;
}

}; // returnRegExResult

var P1DataStream = function (opts) { console.log('P1DataStream opts '); console.log(opts);

var self = this;
self.opts = opts;

var listener = function (data) {

    var tariffOneTotalUsage = returnRegExResult(data, /^1-0:1\.8\.1\(0+(\d+\.\d+)\*kWh\)/m);
    var tariffTwoTotalUsage = returnRegExResult(data, /^1-0:1\.8\.2\(0+(\d+\.\d+)\*kWh\)/m);
    var currentTariff = returnRegExResult(data, /^0-0:96.14.0\(0+(.*?)\)/m);
    var currentUsage = returnRegExResult(data, /^1-0:1.7.0\((.*?)\*/m);
var totalGasUsage = returnRegExResult(data, /^0-1:24\.2\.1\(\d+[SW]\)\((\d+\.\d+)\*m3\)/m);

    var dataGram = {
        tariffOneTotalUsage: tariffOneTotalUsage * 1,
        tariffTwoTotalUsage: tariffTwoTotalUsage * 1,
        currentTariff: currentTariff * 1,
        currentUsage: currentUsage * 1000,
    totalGasUsage: totalGasUsage * 1
    };

    console.log('Raw data received: ' + data);
    console.log('Parsed data: ');
    console.log(dataGram);

    self.emit("data", dataGram);
};

openSerialPort(self.opts, listener);
events.EventEmitter.call(self);

};

util.inherits(P1DataStream, events.EventEmitter);

module.exports = P1DataStream;

jozonja commented 8 years ago

I know what the problem was. I had tab's in my coffee script and that doesn't work. I replaced them with white spaces.

ghost commented 8 years ago

This script is still not working for me. I installed the smartmeteplugin stopped pimatic service and replaced p1meterdata.js and pimatic-smartmeter.coffee. Then restarted pimatic and came up with an error: unexpected indentation. The standard smartmeterplugin works fine only without gas ussage. { "id": "smartmeter", "class": "Smartmeterdevice", "name": "Smartmeter", "serialport": "/dev/ttyUSB0", "baudRate": 115200, "dataBits": 8, "parity": "", "stopBits": 0, "flowControl": true }

ghost commented 8 years ago

Fixed the problem missed one tab in coffee script. But it would be nice if every meter is below each other instead of behind. Can any one tell me where the logfile from pimatic-smartmeter is located, can't find it. might be usefull for logreader

funip commented 8 years ago

I also want to know if the pimatic smartmeter really writes a logfile or just directly shows its data. A logfile might come in handy for a logreader.

saberone commented 8 years ago

I haven't added writing to a logfile. Especially since there already were solutions that log to file which could be read by pimatic. And unnecessarily writing to disk is a win when using a Raspberry Pi.

By the way. A while ago I added a simple program that logs to file. I wrote it so people could create log files which could be used to debug/test code. See the https://github.com/saberone/pimatic-smartmeter/blob/master/README.md