dsmrreader / dsmr-reader

DSMR-telegram reader & data visualizer for hobbyists. Free for non-commercial use.
https://dsmr-reader.readthedocs.io
Other
464 stars 95 forks source link

Add MQTT support to publish readings #141

Closed MarsWarrior closed 7 years ago

MarsWarrior commented 8 years ago

My main Domotica backbone is build upon MQTT. Other sensors and my main system (openHAB) communicate over MQTT. I also use MQTTWarn from Jan-Piet Mens, which enables me to connect to several other services.

It would be very nice if the dsmr-reader could be extended with MQTT support:

Gasmeter example:

0-1:96.1.0(xxxx)                 -> /p1/gas/id xxxx
0-1:24.2.1(yyyy)(zzz.zzz*m3)     -> /p1/gas/timestamp_value yyyy
                                 -> /p1/gas/value zzz.zzz
0-1:24.4.0(1)                    -> /p1/gas/valve_position 1

As you can see, the 24.2.1 gasmeter value requires two topics in this case using raw values.

dennissiemensma commented 8 years ago

Hi, thank you for your request. Although you specified quite some details, I currently lack MQTT experience with the setup you are trying to describe here.

Do you simply wish to use dsmr-reader for only reading your meter and have it posted to another tool using MQTT?

If so, then this might be an excellent addition to the #96 feature (stand alone datalogger client). In #140 I've added an API for posting such data to dsmr-reader using a bare bone client. A small datalogger client will suffice either way. Using the whole dsmr-reader application for it is overkill anyway.

Besides, currently the application's datalogger is tightly coupled to the database. You can either extract the datalogger code from dsmr-reader/dsmr_datalogger/services.py or have the dsmr_datalogger process (in supervisor) push all data to a sqlite database, which is unsupported but will work anyway for just logging readings. You can then write a small script to export that data using MQTT. Or it could be a feature for #96.

Please clarify so I have a better understanding of your request.

MarsWarrior commented 8 years ago

TL;DR Yes, please add MQTT support to issue #96.

Do you simply wish to use dsmr-reader for only reading your meter and have it posted to another tool using MQTT?

Hmmm, difficult question. The dsmr-reader creates nice dashboards, but on the other hand I do prefer to have my dashboards elsewhere.

I didn't notice the #96 feature. That could indeed be a good solution for the MQTT support. A simple configuration file could then be used for instance to map the DSMR codes to MQTT topics, backed by a nice webinterface if possible.

My solution then would be:

I don't have any experience with the python library for MQTT, but the examples seem simple and straightforward to connect & publish MQTT topics.

import paho.mqtt as mqtt
import time

mqttc=mqtt.Client("dsmr-reader")
mqttc.connect("127.0.0.1" 1883, 60, True)
mqttc.loop_start()
while True:
    # do all the dsmr stuff every 10 seconds
    mqttc.publish("dsmr/x/y/z","{value: xyz}")
    time.sleep(10)# sleep for 10 seconds
dennissiemensma commented 8 years ago

Thank you for your input. Due to holiday I was unable to respond earlier. I will try to complete #96 for the current release, but I can't promise anything yet.

dennissiemensma commented 8 years ago

@MarsWarrior it has been quite some while, but I'm currently active again to perform some maintenance for this project.

Is your request here still pending or did you find an alternative in the meantime?

MarsWarrior commented 8 years ago

@dennissiemensma , yes my request is still pending. I'm not using any alternative.

But as you will understand: I'm in no hurry obviously. But if you want to add this feature now, please do!

dennissiemensma commented 8 years ago

I might give it a try. Therefor I'll need to know the exact setup you are using so I can reproduce and test it. I don't need to know every detail about the system, just the entrypoint for the MQTT and how I can see whether the readings are successful. Can you provide some info?

dennissiemensma commented 7 years ago

@MarsWarrior see my comment above. Do you have more information about how I can reproduce your setup at home? Because it' really hard to create something that I cannot test or verify that it works.

thomasvnl commented 7 years ago

I just saw that I read the title of this issue differently than what I mean, therfore this small addition:

I want to suggest to @MarsWarrior that adding publishing of MQTT messages from this dsmr-reader package feels imho outside of the scope of what this package is trying to do: logging results (but please, @dennissiemensma , correct me if I'm wrong).

You could write a tiny process with the paho.mqtt client that reads data from the serial port on your raspberry pi (see dsmr_datalogger/services.py) and publishes to whatever topic you want for further processing. That way you don't need anything from the dsmr-reader package which saves you installing dependencies and such.

My addition below could be added to a new feature request for listening on a certain topic to read in MQTT payloads from an external source.

-- New Feature --

@dennissiemensma I have a setup in which I send these kind of MQTT payloads to a topic like _home/power/dsmr/DEVICEID:

{
  "timestamp" : "170107114403W",
  "readings" : {
    "energy_delivered_tariff1" : "1414657",
    "energy_delivered_tariff2" : "1513958",
    "energy_returned_tariff1" : "331553",
    "energy_returned_tariff2" : "797366",
    "power_delivered" : "318",
    "power_returned" : "0",
    "gas_delivered" : "1702613",
    "gas_timestamp" : "170107110000W"
  }
}

MQTT requires a broker service (it's a socket with some extra stuff) like mosquitto, which runs on raspberry pi's too.

These messages come from a standalone ESP8266 with P1 Port Shield I designed, but you can fake these messages as well with a little routine that spews them out on the MQTT Broker every 10 seconds.

I was trying to create something like you have build with your dsmr-reader packge but then with MQTT + NodeJS + MongoDB but it fails miserably on the NodeJS side of things (NodeJS being Async, sometimes not so obvious). That is when I Googled and I came across this package. I have experience with Python + MQTT so I might consider adding stuff to your dsmr-reader instead to support MQTT (cannot say how long it would take), to work with your dashboarding (cause I love it!). I would appreciate some help with the Django stuff, as my Django Kung-Fu isn't as advanced as I would like it to be 😛

dennissiemensma commented 7 years ago

Hi @thomasvnl thank you for your well detailed comment and request.

I'm not sure whether I understand everything corectly, but I won't integrate any MQTT features. I do however try to help people, whenever possible, to have them run their own scripts locally. For example #187.

That's also the only reason this ticket is still open, because I closed another ticket regarding a stand-alone datalogger.

Some information about the scope of the project regarding data input:

And data output:

If I do read your request correctly and you do want dsmrreader to export readings to MQTT, then I think it would be better to use a more simple parser, such as this one.

I hope this helps you a lot in finding a suitable solution. I'll try to assist when possible.

thomasvnl commented 7 years ago

Hej @dennissiemensma,

Nice detailed response regarding the scope of the project. I don't think what I meant has reached you entirely so I will try to clarify what I meant in my previous response, because I was also replying to MarsWarrior's query.

But first:

Scopewise

Data output

Clarifying what I meant

I want to extend dsmr-reader in such a way that it supports listening on an MQTT topic (like the one mentioned before _home/power/dsmr/DEVICEID). The messages the MQTT Listener (subscriber) receives could be considered as "remote" telegrams. This looks a lot like what you did with the dsmr_api, except that the responsibilities are a little bit different.

Why I want to do this? Well, to explain that I have to explain a little bit about my intended setup (which goes beyond just the dsmr-reader part 😉).

I am trying to build a small sensor network at home, with sensors publishing their data to a central hub (MQTT broker). MQTT is a well known standard and I believe heavily used also in the Maker community for devices like the ESP8266 (sort of Arduino, but not the same and with builtin WiFi for only $2,-).

By publishing this data to this central hub, any listener can do something with the data. In programming also known as the publish-subscribe pattern. It encourages loose coupling between devices that generate data and processes that, well, process data. This loose coupling also creates space to add more/different processes to do something with the same data without a device having to submit the same data to multiple endpoints.

In my case my hub is a server running Ubuntu, NodeJS, Python, MongoDB, MariaDB, and the MQTT Broker implementation called Mosquitto but this could very well be the raspberry pi where the dsmr-reader software is running (because these brokers are primarily lightweight). My setup involves multiple ESP8266 devices, one with a specific P1 reader shield that reads in telegrams from my meter and publishes these messages to the MQTT broker. Others publish temperature, humidity, soil humidity levels, light levels, etc. Some of these devices can also be controlled by sending data to the MQTT broker on certain topics.

Supporting MQTT for reading in telegrams from an external device, in addition to supporting API calls, could help support a bigger network of "things" that all work with MQTT. I would love to help out with this if you were to add this as a feature. If not, I could always write a separate script that listens to the MQTT broker for DSMR messages and then writes them to the API on localhost. I just believe that others might want the same thing and integrating this feature would help them too.

I hope this helps in clarifying what I meant and for you to understand my vision.

Have a good weekend!

Thomas

dennissiemensma commented 7 years ago

Thanks for providing more input. I do now understand the vision you have and also the reason for it, to prevent sending the same data twice.

However, this downside is that adding such feature will add more complexity to the project. And move any of the loose couplings, you intend to have, into this project as well. That the exact reason why I do not see fit for other sources of data that are not sent via the API or builtin datalogger.

This project requires the raw telegram data for many features and integrating another source would limit this. Or even worse, break any MQTT implementation and disappoint the users of it. The data you provided in your initial comment would suffice, for some fields, but not all of it. Recently there was support added for phases, in the future I would also need the device serial numbers (#225). And at this very moment I have to build support for DSMR v5, which might introduce some new features as well.

So I hope you can understand my decision for not integrating such feature now. The project is stable for almost a year now, but far from finished. So I do think there will be a future for such integration, but that would require this project to be fully matured first. Fow now I will push this back to a later release.

I can still provide you input for the script discussed earlier. Please note that there is a raw telegram needed due to all the reasons above.

thomasvnl commented 7 years ago

I understand the downsides and respect your decision for not including it at the moment. I do want to add that the MQTT payload can contain all the fields from the DSMR message or even better, the DSMR string itself. MQTT doens't limit you in terms of contents. In my case my ESP8266 module has a parser on it (to be more precise, this one), therefore I've chosen to send a JSON string and let the parsing and CRC checking be done by the reading module.

I think I can manage with the documentation and issue #187 to create a script to parse data from MQTT into the API, but thanks anyway for being willing to help.

jeroenubbink commented 7 years ago

@dennissiemensma would you be open for a pull request that will actually implement this through an admin screen? Maybe with a message template that allows you to design a message the way you like it using variables. It would be unobtrusive and disabled by default.

I didn't go through the code myself yet but i think this would be a rather simple and elegant solution.

dennissiemensma commented 7 years ago

Thank you for providing your support. However both this ticket and #9 will be included in the v1.8 release, which I'm starting on shortly anyway. I'm currently wrapping up #230 for v1.7.

I do require some support later on here, since I'm no expert in MQTT anyway. As well as some testers.

There is a question I have for you: How do I choose the right format? Does a simply mapping of all model fields suffice? For example (from the API docs):

            {
                "id": 3593621,
                "timestamp": "2017-01-31T23:00:03Z",
                "electricity_delivered_1": "1596.234",
                "electricity_returned_1": "0.000",
                "electricity_delivered_2": "1484.761",
                "electricity_returned_2": "0.000",
                "electricity_currently_delivered": "0.075",
                "electricity_currently_returned": "0.000",
                "phase_currently_delivered_l1": "0.017",
                "phase_currently_delivered_l2": "0.058",
                "phase_currently_delivered_l3": "0.000",
                "extra_device_timestamp": "2017-01-31T22:00:00Z",
                "extra_device_delivered": "1835.904"
            }

Will posting that data to a MQTT endpoint work?

jeroenubbink commented 7 years ago

In theory you could provide that payload and yes clients will be able to receive that payload.

However i don't think you should choose the format, rather you should make it a template that people can design, maybe default to the raw dmsr string. It really all depends on how flexible the subscriber software is. Maybe it can only read the payload if it's in a certain format or maybe it will allow you to parse the payload on the subscriber side. If the software is really inflexible it might actually only read hardcoded topics and expect payloads to be in a certain format.

I would even go as far as to say, you might have multiple subscribers who are inflexible and bind to certain topics so you might even want to send out multiple messages in various formats.

I don't know if you ever used AMQP but MQTT is from what i understand somewhat of a simplification of the AMQP protocol, you publish a message with a topic and a payload to the MQTT broker and the clients who subscribed to a certain topic will receive the payload as a message.

I have whipped up an example in a few minutes using the paho-mqtt publish#single implementation, the mosquitto docker image (but you could just apt install mosquitto) and the default mosquitto_sub subscriber client (apt install mosquitto-clients) to read the message.

So given you have mosquitto running on localhost (either through docker or through a local server) you can do the following in an empty shell:

$ mosquitto_sub -t 'dsmr/raw'

This will subscribe the commandline mosquitto client to receive messages with the topic dmsr/raw

Then in another terminal i am in a virtualenv with the paho-mqtt module installed.

>>> import paho.mqtt.publish as publish >>> publish.single('dsmr/raw', payload='this can literally be any kind of string', client_id='dsmrreader')

Then when i go back to the terminal with mosquitto_sub running i see:

this can literally be any kind of string

I think in a basic scenario you would want a user through the dsmrreader admin to:

If you need any further help please let me know. I'd be happy to provide it.

dennissiemensma commented 7 years ago

Thanks a lot for the explaination! I will try mosquitto and the paho client to see how things work. Defining a template seems the way to go, as there are now multiple examples prompted by different users in this ticket. I will continue later on in the next release.

dennissiemensma commented 7 years ago

I'm currently looking for a uniform, but simple way to allow users have different kind of MQTT messages containing the data they want. I do think however that it's quite difficult to have a 1-solution-for-all.

I'm currently thinking of:

Any thought on this or what is needed to make it as configurable but simple as possible? Or any stuff that I don't need?

jeroenubbink commented 7 years ago

This looks like a very decent way of doing it. I've meanwhile looked into how openhab(2) and homeassistant read mqtt messages but they both seem to require 'custom' functionality to transform the message, be it through some configuration (openhab) or a transform function written in python (homeassistant).

I would just go with this for now as this is a great start that should cover probably 90% of all the use cases. If people start asking something that is not possible with this template i'm sure it will be trivial to add.

I'm not sure if you really need to be sending it in different formats under different topics, but it does add additional flexibility which i guess is a nice-to-have.

dennissiemensma commented 7 years ago

Okay thank you for your input. I will maintain the current draft then.

dennissiemensma commented 7 years ago

I've managed to finish the implementation for this. I've tested it all locally using the mosquitto_sub, but I rather have someone run it as well in a real MQTT setup.

Does anyone wants to give it a try?

jeroenubbink commented 7 years ago

That sounds awesome! I was planning on integrating it in my OpenHAB setup so i can give it a try but that could take a little while (2-3 weeks?). If nobody has responded in between i'll let you know the results.

dennissiemensma commented 7 years ago

Thank you, I will most likely just release it for now and add any additions required later via patches or a new version. I'm looking forward to hearing from you in a few weeks then.

dennissiemensma commented 7 years ago

I've added some documentation and screenshots as well, to give you guys an idea of how it works: