From 7c3e2381cb677722645c3d068d181462bdd8d8e2 Mon Sep 17 00:00:00 2001 From: Martin Sebald Date: Tue, 14 Nov 2017 11:27:18 +0100 Subject: [PATCH] added sml --- README.md | 5 +- sml/README.md | 12 +++++ sml/sml.items | 2 + sml/sml.rules | 14 +++++ sml/sml_server.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 sml/README.md create mode 100644 sml/sml.items create mode 100644 sml/sml.rules create mode 100644 sml/sml_server.c diff --git a/README.md b/README.md index 1fd147c..1c716f4 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,7 @@ If you find problems or errors, please [let me know](https://www.sebald.com/kont The stuff I provide is based on [openHABian](http://docs.openhab.org/installation/openhabian.html) on a Raspberry Pi 3 using openHAB 2.1 and sometimes might reflect on my own (sometimes special) hardware and environment in and around my house. ## presence -Wifi Presence - Detect a person with his/her smartphone entering or leaving the wireless home network (based on a Fritzbox and two DD-WRT based access points). \ No newline at end of file +Wifi Presence - Detect a person with his/her smartphone entering or leaving the wireless home network (based on a Fritzbox and two DD-WRT based access points). + +## sml +Read data from a power meter (eHZ) using Smart Message Language (SML). \ No newline at end of file diff --git a/sml/README.md b/sml/README.md new file mode 100644 index 0000000..f0ad8b0 --- /dev/null +++ b/sml/README.md @@ -0,0 +1,12 @@ +# Power Meter with SML +Read data from a power meter (eHZ) using Smart Message Language (SML). + +First of all you need a IrDA USB dongle like described [here](https://wiki.volkszaehler.org/hardware/controllers/ir-schreib-lesekopf). I got mine from Udo ([Volkszaehler](http://volkszaehler.org)) but it looks like he is not selling these anymore. What I found is [this link](https://shop.weidmann-elektronik.de/index.php?page=product&info=24) to a shop selling these dongles. + +I used several information from the openHAB community, mainly from [this topic](https://community.openhab.org/t/using-a-power-meter-sml-with-openhab/21923/1). + +You need to get, modify and compile [libsml](https://github.com/dailab/libsml) from Github liked described [here](https://community.openhab.org/t/using-a-power-meter-sml-with-openhab/21923/1). I included my version of sml_server.c here as it differs a bit. + +* **sml.items**: Put this file into your */etc/openhab2/items*. +* **sml.rules**: Put this file into your */etc/openhab2/rules* directory and modify it for your specific setup. In my case the compiled sml_server is in */home/openhabian/src/libsml/examples*. +* **sml_server.c**: Put this file into the *libsml* subdirectory *src/libsml/examples* directory and compile *libsml*. \ No newline at end of file diff --git a/sml/sml.items b/sml/sml.items new file mode 100644 index 0000000..da40741 --- /dev/null +++ b/sml/sml.items @@ -0,0 +1,2 @@ +Number EHZ_consumption_power "power consumption [%.1f kwh]" +Number EHZ_status_power "power status [%.1f kwh]" diff --git a/sml/sml.rules b/sml/sml.rules new file mode 100644 index 0000000..dc5ac34 --- /dev/null +++ b/sml/sml.rules @@ -0,0 +1,14 @@ +rule "EHZ" + when + Time cron "0,30 */1 * * * ?" // this one cycles every 30 seconds. depends on your needs + then + // getting the payload of the meter + var String meter_payload = executeCommandLine("/home/openhabian/src/libsml/examples/sml_server /dev/ttyUSB0", 5000) // note the argument /dev/ttyUSB0 - your meter-device-id goes here + // splitting the payload - first in lines, then getting counterStr and consumptionStr with delimiter "#" + val lines = meter_payload.split('\n') + val counterStr = lines.get(0).split('#').get(1) + val consumptionStr = lines.get(2).split('#').get(1) + // Updating the items + if (EHZ_status_power.state != counterStr) EHZ_status_power.sendCommand(counterStr) + EHZ_consumption_power.postUpdate(consumptionStr) +end diff --git a/sml/sml_server.c b/sml/sml_server.c new file mode 100644 index 0000000..f2e15c9 --- /dev/null +++ b/sml/sml_server.c @@ -0,0 +1,135 @@ +// Copyright 2011 Juri Glass, Mathias Runge, Nadim El Sayed +// DAI-Labor, TU-Berlin +// +// This file is part of libSML. +// +// libSML is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// libSML is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with libSML. If not, see . + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int serial_port_open(const char* device) { + int bits; + struct termios config; + memset(&config, 0, sizeof(config)); + + int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd < 0) { + printf("error: open(%s): %s\n", device, strerror(errno)); + return -1; + } + + // set RTS + ioctl(fd, TIOCMGET, &bits); + bits |= TIOCM_RTS; + ioctl(fd, TIOCMSET, &bits); + + tcgetattr( fd, &config ) ; + + // set 8-N-1 + config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + config.c_oflag &= ~OPOST; + config.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + config.c_cflag &= ~(CSIZE | PARENB | PARODD | CSTOPB); + config.c_cflag |= CS8; + + // set speed to 9600 baud + cfsetispeed( &config, B9600); + cfsetospeed( &config, B9600); + + tcsetattr(fd, TCSANOW, &config); + return fd; +} + +//void transport_receiver(unsigned char *buffer, size_t buffer_len) { +// // the buffer contains the whole message, with transport escape sequences. +// // these escape sequences are stripped here. +// sml_file *file = sml_file_parse(buffer + 8, buffer_len - 16); +// // the sml file is parsed now + +// // read here some values .. + +// // this prints some information about the file +// sml_file_print(file); + +// // free the malloc'd memory +// sml_file_free(file); +//} + +void transport_receiver(unsigned char *buffer, size_t buffer_len) { + // Danke an Axel (tuxedo) für seinen Beispielcode + int i; + double value; + sml_file *file = sml_file_parse(buffer + 8, buffer_len - 16); + // the sml file is parsed now + for (i = 0; i < file->messages_len; i++) { + sml_message *message = file->messages[i]; + if (*message->message_body->tag == SML_MESSAGE_GET_LIST_RESPONSE) { + sml_list *entry; + sml_get_list_response *body; + body = (sml_get_list_response *) message->message_body->data; + for (entry = body->val_list; entry != NULL; entry = entry->next) { + switch (entry->value->type) { + case 0x51: value= *entry->value->data.int8; break; + case 0x52: value= *entry->value->data.int16; break; + case 0x54: value= *entry->value->data.int32; break; + case 0x58: value= *entry->value->data.int64; break; + case 0x61: value= *entry->value->data.uint8; break; + case 0x62: value= *entry->value->data.uint16; break; + case 0x64: value= *entry->value->data.uint32; break; + case 0x68: value= *entry->value->data.uint64; break; + default: + value = 0; + } + int scaler = (entry->scaler) ? *entry->scaler : 1; + if (scaler==-1) + value *= 0.0001; + if (value) { + printf("%d-%d:%d.%d.%d*%d#%.3f#\n", + entry->obj_name->str[0], entry->obj_name->str[1], + entry->obj_name->str[2], entry->obj_name->str[3], + entry->obj_name->str[4], entry->obj_name->str[5], value); + } + } + sml_file_free(file); + exit(0); // processed first message - exit + } + } +} + +int main(int argc, char **argv) { + // this example assumes that a EDL21 meter sending SML messages via a + // serial device. Adjust as needed. + // char *device = "/dev/cu.usbserial"; + char *device=argv[1]; + int fd = serial_port_open(device); + + if (fd > 0) { + // listen on the serial device, this call is blocking. + sml_transport_listen(fd, &transport_receiver); + close(fd); + } + + return 0; +} +