Support for reading Dutch Smart Meter (electricity/gas) (P1 port)

Congrat, cool to hear, I’m looking forward to version .34!

After installing the patch there have been no issues (apart from home assistant that spontaneously stopping a lot)

Hmmm… That doesn’t go well. Tried the version with M3/h but homeassistant refuses to start now. Systemctl status gives:

Nov 25 05:42:39 raspberrypi hass[574]: File "<frozen importlib._bootstrap>", line 1129, in _exec
Nov 25 05:42:39 raspberrypi hass[574]: File "<frozen importlib._bootstrap>", line 1471, in exec_module
Nov 25 05:42:39 raspberrypi hass[574]: File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
Nov 25 05:42:39 raspberrypi hass[574]: File "/home/hass/.homeassistant/custom_components/sensor/dsmr.py", line 179, in <module>
Nov 25 05:42:39 raspberrypi hass[574]: class DerivativeDSMREntity(DSMREntity):
Nov 25 05:42:39 raspberrypi hass[574]: File "/home/hass/.homeassistant/custom_components/sensor/dsmr.py", line 189, in DerivativeDSMREntity
Nov 25 05:42:39 raspberrypi hass[574]: _state = STATE_UNKNOWN
Nov 25 05:42:39 raspberrypi hass[574]: NameError: name 'STATE_UNKNOWN' is not defined
Nov 25 05:42:40 raspberrypi systemd[1]: home-assistant.service: main process exited, code=exited, status=1/FAILURE
Nov 25 05:42:40 raspberrypi systemd[1]: Unit home-assistant.service entered failed state

And no, I don’t want to push values to kindergarten.nl, but to mindergas.nl (dreadful auto correct!) Mindergas is great in comparing your gas usage to the usage of others or comparing to your own historical usage as it uses the term “weighted degree day” (=gewogen graaddag)

@Atreyu most probably info on your meter is available at the Liander site. See for example https://www.liander.nl/sites/default/files/Meters-Handleidingen-elektriciteit-Kaifa-uitgebreid.pdf

Ah yes, that makes more sense :smiley:

I think it is not the responsibility of this component to push metrics to other services. This only focusses on collecting metric from DSMR smartmeters (which happen to include gas readings). A separate component could be created which can do mindergas.nl integration with not only results from the dsmr component but also other components that provide gas meter readings.

edit: As for the error, I forgot to push that fix yesterday. It should be available now.

Nope, doesn’t work either. Tried https://raw.githubusercontent.com/aequitas/home-assistant/e15e1db170bce0b603412cc838c14a6784920b97/homeassistant/components/sensor/dsmr.py again, but I still get errors while starting:

Nov 25 16:54:29 raspberrypi hass[575]: File "<frozen importlib._bootstrap>", line 1129, in _exec
Nov 25 16:54:29 raspberrypi hass[575]: File "<frozen importlib._bootstrap>", line 1471, in exec_module
Nov 25 16:54:29 raspberrypi hass[575]: File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
Nov 25 16:54:29 raspberrypi hass[575]: File "/home/hass/.homeassistant/custom_components/sensor/dsmr.py", line 179, in <module>
Nov 25 16:54:29 raspberrypi hass[575]: class DerivativeDSMREntity(DSMREntity):
Nov 25 16:54:29 raspberrypi hass[575]: File "/home/hass/.homeassistant/custom_components/sensor/dsmr.py", line 189, in DerivativeDSMREntity
Nov 25 16:54:29 raspberrypi hass[575]: _state = STATE_UNKNOWN
Nov 25 16:54:29 raspberrypi hass[575]: NameError: name 'STATE_UNKNOWN' is not defined
Nov 25 16:54:30 raspberrypi systemd[1]: home-assistant.service: main process exited, code=exited, status=1/FAILURE
Nov 25 16:54:30 raspberrypi systemd[1]: Unit home-assistant.service entered failed state.

try: https://raw.githubusercontent.com/aequitas/home-assistant/0585aa31140e86737e16313286ab6ea5a273a55c/homeassistant/components/sensor/dsmr.py

You can make a python script like this and call it every day at 00:05/00:10 or so .
fill in the variables with your stuff (my sensor is called sensor.p1_total_gas_usage’, yours might not…
mind the .state after the p1 gas usage sensor !

import requests
import json
import datetime
import homeassistant.remote as remote

mindergas = "http://www.mindergas.nl/api/gas_meter_readings"
mindergas_token = 'token'
hass_ip = 'xx.xx.xx.xx'
hass_password = 'secret!'
hass_port = 8124
gas_sensor = 'sensor.p1_total_gas_usage.state'

api = remote.API(hass_ip,hass_password, hass_port)
gas_usage = remote.get_state(api, gas_sensor)

def post():
    yesterday = datetime.date.today () - datetime.timedelta (days=1)
    data = {'date': yesterday.strftime ("%Y%m%d"), 'reading':gas_sensor}
    headers = {'Content-Type': 'application/json', 'AUTH-TOKEN': mindergas_token}
    r = requests.post(mindergas, data=json.dumps(data), headers=headers)
    print (yesterday.strftime ("%Y%m%d"))
    print (data)
    print (r)
    print ('Ready')

post()

i added an automation for this :slight_smile:

alias: 'Upload gas usage to mindergas.nl'
trigger:
  platform: time
  hours: 0
  minutes: 5
  seconds: 0
action:
    service: shell_command.mindergas
2 Likes

I installed it and had it running during the night. No errors whatsoever, but the values of the hourly M3/H are always negative. I guess it would be just flipping a value for you to fix, right?

In addition I am very curious how you managed to do this. Do you simply substract the previous value from the current one and use that as the hourly consumption, or doe you do more averaging?

looks like a simple diff

if self._previous_reading is None:
                # can't calculate rate without previous datapoint
                # just store current point
                pass
            else:
                # recalculate the rate
                diff = self._previous_reading - current_reading
                self._state = diff

But looking a the code i think the negative value is due to this:
diff = self._previous_reading - current_reading
which has to be this, i think
diff = current_reading - self._previous_reading

@koen01 @fversteegen indeed value was negative. I pushed a fix for that yesterday: https://github.com/aequitas/home-assistant/commit/59ba84ed56d858a32d5d2cb397d5f0594139c0c8

This is the logic for calculating the hourly usage: https://github.com/aequitas/home-assistant/blob/59ba84ed56d858a32d5d2cb397d5f0594139c0c8/homeassistant/components/sensor/dsmr.py#L196-L212

The DSMR telegram contains a timestamp for the gas reading. Using this you know when the value changed and you can calculate the hourly usage (assuming the value is updated every hour).

Hi @aequitas, first of all thanks for your effort for getting the dutch smart meter supported in Home Assistant.

I have installed HA in a virtual environment on a RPi using Ubuntu Jessie (and the RPi All-in-One installer), but have some issues getting your software running. It seems that HA is not able to access the GPIO pins to which the cable is connected:

16-11-27 21:49:15 homeassistant.components.sensor: Error while setting up platform dsmr
Traceback (most recent call last):
  File "/home/hass/.homeassistant/deps/serial/serialposix.py", line 265, in open
    self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
PermissionError: [Errno 13] Permission denied: '/dev/ttyAMA0'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py", line 145, in _async_setup_platform
    entity_platform.async_add_entities, discovery_info
  File "/srv/hass/hass_venv/lib/python3.4/site-packages/homeassistant/components/sensor/dsmr.py", line 109, in async_setup_platform
    transport, _ = yield from hass.loop.create_task(dsmr)
  File "/usr/lib/python3.4/asyncio/futures.py", line 388, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.4/asyncio/tasks.py", line 286, in _wakeup
    value = future.result()
  File "/usr/lib/python3.4/asyncio/futures.py", line 277, in result
    raise self._exception
  File "/usr/lib/python3.4/asyncio/tasks.py", line 237, in _step
    result = next(coro)
  File "/usr/lib/python3.4/asyncio/coroutines.py", line 141, in coro
    res = func(*args, **kw)
  File "/home/hass/.homeassistant/deps/serial_asyncio/__init__.py", line 409, in create_serial_connection
    ser = serial.serial_for_url(*args, **kwargs)
  File "/home/hass/.homeassistant/deps/serial/__init__.py", line 88, in serial_for_url
    instance.open()
  File "/home/hass/.homeassistant/deps/serial/serialposix.py", line 268, in open
    raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
serial.serialutil.SerialException: [Errno 13] could not open port /dev/ttyAMA0: [Errno 13] Permission denied: '/dev/ttyAMA0'

My vritual port is owned by the group tty:

hass@raspberrypi:/home/pi$ ls -la /dev/ttyAMA0
crw--w---- 1 root tty 204, 64 Nov 27 18:58 /dev/ttyAMA0

My virtualenv user is added to the tty group (as well as the gpio and dialout):

hass@raspberrypi:/home/pi$ groups | grep tty
hass tty dialout gpio

If I try as a different user (also memeber of tty) it seems it does work, but only when I elevate using sudo I get dsmr_console to properly access the port and report the data from the datagram(s):

pi@raspberrypi:~ $ ls -la /dev/ttyAMA0
crw--w---- 1 root tty 204, 64 Nov 27 18:58 /dev/ttyAMA0
pi@raspberrypi:~ $ groups | grep tty
pi adm tty dialout cdrom sudo audio video plugdev games users input netdev gpio i2c spi
pi@raspberrypi:~ $ dsmr_console --help
usage: dsmr_console [-h] [--device DEVICE] [--version {2.2,4}] [-v]
                    [-p {N,E,O}] [-b {7,8}]

Output DSMR data to console.

optional arguments:
  -h, --help            show this help message and exit
  --device DEVICE       port to read DSMR data from
  --version {2.2,4}     DSMR version (2.2, 4)
  -v, --verbose         increase output verbosity
  -p {N,E,O}, --parity {N,E,O}
                        Override serial parity setting.
  -b {7,8}, --bytesize {7,8}
                        Override serial bytesize setting.
pi@raspberrypi:~ $ dsmr_console --device /dev/ttyAMA0 --version 2.2
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/serial/serialposix.py", line 265, in open
    self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
PermissionError: [Errno 13] Permission denied: '/dev/ttyAMA0'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/dsmr_console", line 9, in <module>
    load_entry_point('dsmr-parser==0.3', 'console_scripts', 'dsmr_console')()
  File "/usr/local/lib/python3.4/dist-packages/dsmr_parser/__main__.py", line 63, in console
    for telegram in serial_reader.read():
  File "/usr/local/lib/python3.4/dist-packages/dsmr_parser/serial.py", line 63, in read
    with serial.Serial(**self.serial_settings) as serial_handle:
  File "/usr/local/lib/python3.4/dist-packages/serial/serialutil.py", line 236, in __init__
    self.open()
  File "/usr/local/lib/python3.4/dist-packages/serial/serialposix.py", line 268, in open
    raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
serial.serialutil.SerialException: [Errno 13] could not open port /dev/ttyAMA0: [Errno 13] Permission denied: '/dev/ttyAMA0'
pi@raspberrypi:~ $ sudo dsmr_console --device /dev/ttyAMA0 --version 2.2
Power Consumption (low) 5782.433 kWh
Power Consumption (normal) 4304.307 kWh
Power Consumption 0.18 kW
Power Tariff 0001 None
Power Production 0.00 kW
Power Production (low) 0.000 kWh
Power Production (normal) 0.000 kWh

Power Consumption (low) 5782.434 kWh
Power Consumption (normal) 4304.307 kWh
Power Consumption 0.20 kW
Power Tariff 0001 None
Power Production 0.00 kW
Power Production (low) 0.000 kWh
Power Production (normal) 0.000 kWh

^CTraceback (most recent call last):
  File "/usr/local/bin/dsmr_console", line 9, in <module>
    load_entry_point('dsmr-parser==0.3', 'console_scripts', 'dsmr_console')()
  File "/usr/local/lib/python3.4/dist-packages/dsmr_parser/__main__.py", line 63, in console
    for telegram in serial_reader.read():
  File "/usr/local/lib/python3.4/dist-packages/dsmr_parser/serial.py", line 67, in read
    line = serial_handle.readline()
  File "/usr/local/lib/python3.4/dist-packages/serial/serialposix.py", line 472, in read
    ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left())
KeyboardInterrupt
pi@raspberrypi:~ $

Do you have any clue what could by the issue and how to get HA to read the DSMR data from inside the virtualenv?

Could you try a sudo chmod 666 /dev/ttyAMA0 and see if that improves the situation? I had similar issues with my raspberry in the beginning and this seemed to solve things.

Thanks for the hint @fversteegen, but security wise that is a bad suggestion as that would mean everybody is allowed to read and write to the device.
Apart from that I already tried changing permissions but they do not stick and are reset (periodically, most likely because of udev rules).

I figured it out this night. Due to the fact that the serial console was not properly disabled at startup it kept staring, despite disabling it using ‘sudo raspy-config’.
It seems permission changes to rw--w---- and ownership to root:try by getty, which provides the console login screen.

After disabling it by masking it like this:

sudo systemctl mask [email protected]
sudo reboot

Only after above commands the service does not start anymore and because of that permissions are rw-rw---- and ownership root:dialout.
Since my user running Home Assistant was already a member of the dialout group everything worked as expected after this.

It seems smart meter data is recorded nicely during the night.

1 Like

If nobody is having any issues with it then I am considering the gas usage ready. Hope it can be included in the next release with DSMR as well.

2 Likes

All running fine and apparently stable here @aequitas! Once more: many thanks for this great piece of work!

@aequitas Hi Johan, did you have a chance to look at my Q (Support for reading Dutch Smart Meter (electricity/gas) (P1 port)) . Quite handy as it allows you to move the HASS server to a more central location in the house.

I have a P1 Wifi cable (http://romix.macuser.nl/) which sends an identical report but then via the network. Instead of a USB port the plugin would need to check a IP/port combination.

Sorry totally missed your post there.

I think its totally doable. I already have it implemented for a different component I am working for.

I still didn’t manage to get your script up and running. The problem is in getting the sensor value into the script. If I trim down your script to:

import homeassistant.remote as remote
api = remote.API('127.1.0.1', '')
print(remote.get_state(api, 'sensor.gas_consumption.state'))

I get a value “None” in return. Any clue on how to fix that?

What steps should I need to do so I can test this component.

Would like to add it ASAP.

Just add this file to custom_components/sensor/ https://github.com/aequitas/home-assistant/blob/dsmr/homeassistant/components/sensor/dsmr.py

Don’t forget to remove it when the new version of HA is released, as that includes the sensor natively.

And what do I need to put in the configuration file?