Closed ThinkPadNL closed 7 years ago
Hi, thank you for your question.
As a matter of fact, there is API support for posting the readings remotely to the app. By coincidence, I just created #185 with an example script. The only thing is, I do not have any experience with ESP devices, but it should be possible I guess. Any program able to read the network socket, and create a HTTP POST request, should do the trick.
Can you provide me with an example of how you read the network socket with Putty? I might be able to replace the serial input, of the script in #185, with reading data from a network socket.
I already fiddled around with some code and with this piece of Python-code, i can read one telegram and then it returns back to the command prompt:
import socket
# creates socket object
s = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
host = '192.168.4.17'
port = 8088
s.connect((host, port))
telegram = s.recv(1024) # msg can only be 1024 bytes long
#s.close()
print(telegram)
But my Python skills are reeeally basic, so it is getting too complex for me to integrate this into your project.
By the way, i couldn't find it quickly, but does your code verify if the CRC that is sent along with the telegram matches with a checksum that is calculated over the telegram?
Hi, the CRC verification is lacking, but I think that's a good idea anyway, so I'll create an separate issue for it. I am planning to use another parser in #154, but CRC lacks there as well (for the moment).
As for the sockets, it's a bit hard scripting remotely, but can you try whether this works?
There are some debugging print, but when succesful, it should print ----- Read telegram -----
followed by the telegram.
from time import sleep
import socket
HOST = '192.168.4.17'
PORT = 8088
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
telegram = read_telegram(s)
print('----- Read telegram -----')
print(telegram)
def read_telegram(s):
""" Reads the socket until we can create a reading point. """
telegram_start_seen = False
telegram = ''
# Just keep fetching data until we got what we were looking for.
while True:
print('Listening at socket...')
socket_data = s.recv(1024)
print('RECV:', socket_data)
sleep(1)
for data in socket_data.split("\n"):
print("DATA LINE:", data)
try:
# Make sure weird characters are converted properly.
data = str(data, 'utf-8')
except TypeError:
pass
# This guarantees we will only parse complete telegrams. (issue #74)
if data.startswith('/'):
telegram_start_seen = True
# Delay any logging until we've seen the start of a telegram.
if telegram_start_seen:
telegram += data
# Telegrams ends with '!' AND we saw the start. We should have a complete telegram now.
if data.startswith('!') and telegram_start_seen:
return telegram
if __name__ == '__main__':
main()
Thank you, i will try that somewhere in the following days :)
Heb je dit nog kunnen testen of is dit niet meer van toepassing sinds de CRC-check?
Nee, helaas nog niet aan toe gekomen. Het staat nog wel op de planning, moet komende week vast wel ergens lukken.
Het is zeker nog van toepassing. Er ligt nu een 5m. lange kabel van mijn meterkast naar m'n server om de P1-poort uit te lezen. Als ik die kabel kan vervangen door een draadloze oplossing dan heeft dat zeker mijn voorkeur. Dit issue heeft overigens geen haast hoor.
Ik had eindelijk wat tijd om hier weer naar te kijken. Een RJ11-splitter op de P1-poort gezet zodat dsmr-reader
gewoon door kan blijven gaan met loggen en op de tweede uitgang de ESP8266 geknupt. Script wat je hierboven plaatste even geprobeerd, dit is de output:
domotica@domotica-vm:~$ python /home/domotica/p1smartmeter/esp.py
Listening at socket...
('RECV:', '/KFM5KAIFA-METER\r\n\r\n1-3:0.2.8(42)\r\n0-0:1.0.0(170104195652W)\r\n0-0:96.1.1(SERIAL_OF_MY_METER_HERE)\r\n1-0:1.8.1(002015.856*kWh)\r\n1-0:1.8.2(002048.232*kWh)\r\n1-0:2.8.1(000000.000*kWh)\r\n1-0:2.8.2(000000.000*kWh)\r\n0-0:96.14.0(0002)\r\n1-0:1.7.0(00.379*kW)\r\n1-0:2.7.0(00.000*kW)\r\n0-0:96.7.21(00005)\r\n0-0:96.7.9(00003)\r\n1-0:99.97.0(1)(0-0:96.7.19)(000101000001W)(2147483647*s)\r\n1-0:32.32.0(00000)\r\n1-0:32.36.0(00000)\r\n0-0:96.13.1()\r\n0-0:96.13.0()\r\n1-0:31.7.0(001*A)\r\n1-0:21.7.0(00.379*kW)\r\n1-0:22.7.0(00.000*kW)\r\n0-1:24.1.0(003)\r\n0-1:96.1.0(SERIAL_OF_MY_METER_HERE)\r\n0-1:24.2.1(170104190000W)(01539.233*m3)\r\n!DA5E\x00\r\n')
('DATA LINE:', '/KFM5KAIFA-METER\r')
('DATA LINE:', '\r')
('DATA LINE:', '1-3:0.2.8(42)\r')
('DATA LINE:', '0-0:1.0.0(170104195652W)\r')
('DATA LINE:', '0-0:96.1.1(SERIAL_OF_MY_METER_HERE)\r')
('DATA LINE:', '1-0:1.8.1(002015.856*kWh)\r')
('DATA LINE:', '1-0:1.8.2(002048.232*kWh)\r')
('DATA LINE:', '1-0:2.8.1(000000.000*kWh)\r')
('DATA LINE:', '1-0:2.8.2(000000.000*kWh)\r')
('DATA LINE:', '0-0:96.14.0(0002)\r')
('DATA LINE:', '1-0:1.7.0(00.379*kW)\r')
('DATA LINE:', '1-0:2.7.0(00.000*kW)\r')
('DATA LINE:', '0-0:96.7.21(00005)\r')
('DATA LINE:', '0-0:96.7.9(00003)\r')
('DATA LINE:', '1-0:99.97.0(1)(0-0:96.7.19)(000101000001W)(2147483647*s)\r')
('DATA LINE:', '1-0:32.32.0(00000)\r')
('DATA LINE:', '1-0:32.36.0(00000)\r')
('DATA LINE:', '0-0:96.13.1()\r')
('DATA LINE:', '0-0:96.13.0()\r')
('DATA LINE:', '1-0:31.7.0(001*A)\r')
('DATA LINE:', '1-0:21.7.0(00.379*kW)\r')
('DATA LINE:', '1-0:22.7.0(00.000*kW)\r')
('DATA LINE:', '0-1:24.1.0(003)\r')
('DATA LINE:', '0-1:96.1.0(SERIAL_OF_MY_METER_HERE)\r')
('DATA LINE:', '0-1:24.2.1(170104190000W)(01539.233*m3)\r')
('DATA LINE:', '!DA5E\x00\r')
----- Read telegram -----
!DA5E4.2.1(170104190000W)(01539.233*m3)383134)7483647*s)
Listening at socket...
En dan na Listening at socket
begint bovenstaande weer opnieuw. Volgens mij zijn we al een heel eind dan, of niet?
Ik hoop dat je hier iets mee kunt. Ik laat de ESP8266 eraan hangen, mocht je een ander script hebben dan kan ik dat eenvoudig opnieuw testen dan.
Mooi, ik had niet verwacht dat die meteen zo ver zou komen.
Hopelijk gaat deze dan ook in 1 x goed, let even goed op de stappen onderaan het codeblok, een variant op deze:
from time import sleep
import socket
import requests
HOST = '192.168.4.17'
PORT = 8088
API_SERVERS = (
('http://HOST-OR-IP/api/v1/datalogger/dsmrreading', 'api-key'),
)
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
telegram = read_telegram(s)
print('Read telegram:')
print(telegram)
for current_server in API_SERVERS:
api_url, api_key = current_server
send_telegram(telegram, api_url, api_key)
print('Sent telegram to:', api_url)
sleep(1)
def read_telegram(s):
""" Reads the socket until we can create a reading point. """
telegram_start_seen = False
telegram = ''
# Just keep fetching data until we got what we were looking for.
while True:
print('Listening at socket...')
socket_data = s.recv(1024)
for data in socket_data.split("\n"):
try:
# Make sure weird characters are converted properly.
data = str(data, 'utf-8')
except TypeError:
pass
# This guarantees we will only parse complete telegrams. (issue #74)
if data.startswith('/'):
telegram_start_seen = True
# Delay any logging until we've seen the start of a telegram.
if telegram_start_seen:
telegram += data
# Telegrams ends with '!' AND we saw the start. We should have a complete telegram now.
if data.startswith('!') and telegram_start_seen:
return telegram
def send_telegram(telegram, api_url, api_key):
# Register telegram by simply sending it to the application with a POST request.
response = requests.post(
api_url,
headers={'X-AUTHKEY': api_key},
data={'telegram': telegram},
)
# You will receive a status 200/201 when successful.
if response.status_code not in (200, 201):
# Or you will find the error (hint) in the response body on failure.
print('[!] Error: {}'.format(response.text))
if __name__ == '__main__':
main()
Stappen vóór uitvoer:
API_SERVERS
de host van waar je DSMR-reader draait invullen.API_SERVERS
.requests
library installeert op de plek waar je dit script uitvoert (pip install requests
)Gaat nog niet helemaal goed:
domotica@domotica-vm:~$ python /home/domotica/p1smartmeter/esp.py
Listening at socket...
Read telegram:
!4C194.2.1(170104210000W)(01540.161*m3)383134)7483647*s)
[!] Error: Failed to parse telegram
('Sent telegram to:', 'http://192.168.4.21/api/v1/datalogger/dsmrreading')
Listening at socket...
Read telegram:
!C9634.2.1(170104210000W)(01540.161*m3)383134)7483647*s)
[!] Error: Failed to parse telegram
Dit gaat de hele tijd zo door, er wordt niet één keer een telegram succesvol verzonden naar dsmr-reader
.
In het script heb ik de connectie als volgt ingevuld (api-key wat ingekort):
API_SERVERS = (
('http://192.168.4.21/api/v1/datalogger/dsmrreading', '5S7SVP.......PO6ZJB'),
)
Dat klopt? De foutmelding Failed to parse telegram
lijkt mij afkomstig van de API, omdat ik hem in het script zelf nergens terugvind.
Komt dit door dat er wellicht een carriage return / linefeed wordt meegestuurd (of juist niet)?
Ja, volgens mij zie ik het al en klopt het wat je al vermoedt.
Ik splits het bericht per linefeed, want die krijg ik normaal terug via de seriele poort, maar ik zet die niet terug bij elke regel.
Dus wellicht kun je van de regel:
telegram += data
Dit maken:
telegram += data + "\n"
Jij bent echt de held van de dag! Aanpassing gedaan en het lijkt nu perfect te werken! 👍 👍 👍
domotica@domotica-vm:~$ python /home/domotica/p1smartmeter/esp.py
Listening at socket...
Read telegram:
/KFM5KAIFA-METER
1-3:0.2.8(42)
0-0:1.0.0(170104223828W)
0-0:96.1.1(SERIALNUMBERHERE)
1-0:1.8.1(002015.856*kWh)
1-0:1.8.2(002049.345*kWh)
1-0:2.8.1(000000.000*kWh)
1-0:2.8.2(000000.000*kWh)
0-0:96.14.0(0002)
1-0:1.7.0(00.258*kW)
1-0:2.7.0(00.000*kW)
0-0:96.7.21(00005)
0-0:96.7.9(00003)
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.257*kW)
1-0:22.7.0(00.000*kW)
0-1:24.1.0(003)
0-1:96.1.0(....)
0-1:24.2.1(170104220000W)(01540.272*m3)
!671D
('Sent telegram to:', 'http://192.168.4.21/api/v1/datalogger/dsmrreading')
Listening at socket...
Het proces dsmr_datalogger
in supervisor
zorgt normaal voor het uitlezen van de ttyUSB0
toch? Als ik die stopzet en snel even een nieuwe supervisor config maak voor dit Python dingetje, dan zou alles net zo moeten werken als met een fysieke kabel toch? Dan laat ik hem vannacht namelijk even draaien zodat ik morgenochtend een update kan geven. Hoe kan ik rechtstreeks de database querien? Dat gaat wat sneller dan dat ik 100x moet klikken om te kijken of hij ook waardes heeft gemist.
Edit: Ik probeer het op de VM waarin ik dsmr-reader
heb draaien, uit te voeren maar dan krijg ik telkens deze foutmelding:
(dsmrreader) dsmr@dsmrreader-vm:~$ python /home/dsmr/dsmr-reader/esp_sockets.py
Listening at socket...
Traceback (most recent call last):
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 78, in <module>
main()
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 20, in main
telegram = read_telegram(s)
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 43, in read_telegram
for data in socket_data.split("\n"):
TypeError: a bytes-like object is required, not 'str'
(dsmrreader) dsmr@dsmrreader-vm:~$
In die andere VM waar ik het net mee probeerde werkte het wel, dus er zal iets verschillend zijn, maar wat?
Die fout komt doordat de code het wel doet in Python 2, maar de omgeving van dsmr-reader Python 3 heeft. Dit heeft te maken met een van de grootste verschillen onderling, namelijk de afhandeling van strings. Kun je kijken of dit werkt? telegram += data + "\n"
wordt telegram += data + b"\n"
(zie de extra b
die direct vóór en tegen de string moet staan)
De database kun je als sudo/root gebruiker zo makkelijk bekijken (laatste 1000 metingen aflopend):
sudo sudo -u postgres psql dsmrreader -c "select id, "timestamp" from dsmr_datalogger_dsmrreading order by id desc limit 1000;"
Je kunt het proberen te draaien via Supervisor, zie het voorbeeld onderin deze pagina.
Helaas, heb het aangepast met die b
erin (jouw code gekopieerd) maar krijg nog steeds een foutmelding:
(dsmrreader) dsmr@dsmrreader-vm:~$ python /home/dsmr/dsmr-reader/esp_sockets.py
Listening at socket...
Traceback (most recent call last):
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 78, in <module>
main()
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 20, in main
telegram = read_telegram(s)
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 43, in read_telegram
for data in socket_data.split("\n"):
TypeError: a bytes-like object is required, not 'str'
Ik heb het op die andere VM in een supervisor gegooid en laat het vannacht draaien. Het in deze VM werkend krijgen komt wel. Ik ben veel meer benieuwd naar hoe stabiel zich dit verhoud als het een aantal uren draait. Het script doet geen check op CRC zie ik, doet de API dat wel? Het voordeel in dit geval is dat de code op de ESP8266 ook al een check op de CRC doet voordat hij het het netwerk op duwt. Maar tussen ESP en Python-script kan het dus nog misgaan en wellicht zijn er ook mensen die de meter gaan uitlezen met iets wat geen CRC check doet.
Ik ga nu slapen, ik laat het morgen weten hoe het vannacht heeft gedraaid. We komen er wel samen!😎
Ik ben niet helemaal wakker zie ik. Je foutmeldingen geven een compleet ander stuk code aan als oorzaak, dòh. Alleen wellicht dat de b
op die plek uiteindelijk ook nodig was.
Kun je hetzelfde truukje halen bij for data in socket_data.split("\n"):
? Dus dat wordt for data in socket_data.split(b"\n"):
Het is jammer dat ik niet exact dezelfde setup heb hier, dan kan ik je direct werkende code geven mja.
Wat betreft de CRC-check, die wordt op het laatst moment gedaan, vlak voordat de meting wordt weggeschreven in de database. Feitelijk doet de datalogger nu ook al geen CRC-check, maar gebeurt dat indirect in de achterliggende code.
dsmr_datalogger.services.telegram_to_reading(...)
(CRC):
Helaas, ook dan weer een foutmelding:
(dsmrreader) dsmr@dsmrreader-vm:~$ python /home/dsmr/dsmr-reader/esp_sockets.py Listening at socket...
Traceback (most recent call last):
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 78, in <module>
main()
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 20, in main
telegram = read_telegram(s)
File "/home/dsmr/dsmr-reader/esp_sockets.py", line 56, in read_telegram
telegram += data + b"\n"
TypeError: Can't convert 'bytes' object to str implicitly
(dsmrreader) dsmr@dsmrreader-vm:~$
Voor de volledigheid hier nog even het gehele script waarmee ik bovenstaande error krijg:
from time import sleep
import socket
import requests
HOST = '192.168.4.17'
PORT = 8088
API_SERVERS = (
('http://192.168.4.21/api/v1/datalogger/dsmrreading', '5S7SV....JB'),
)
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
telegram = read_telegram(s)
print('Read telegram:')
print(telegram)
for current_server in API_SERVERS:
api_url, api_key = current_server
send_telegram(telegram, api_url, api_key)
print('Sent telegram to:', api_url)
sleep(1)
def read_telegram(s):
""" Reads the socket until we can create a reading point. """
telegram_start_seen = False
telegram = ''
# Just keep fetching data until we got what we were looking for.
while True:
print('Listening at socket...')
socket_data = s.recv(1024)
for data in socket_data.split(b"\n"):
try:
# Make sure weird characters are converted properly.
data = str(data, 'utf-8')
except TypeError:
pass
# This guarantees we will only parse complete telegrams. (issue #74)
if data.startswith('/'):
telegram_start_seen = True
# Delay any logging until we've seen the start of a telegram.
if telegram_start_seen:
telegram += data + b"\n"
# Telegrams ends with '!' AND we saw the start. We should have a complete telegram now.
if data.startswith('!') and telegram_start_seen:
return telegram
def send_telegram(telegram, api_url, api_key):
# Register telegram by simply sending it to the application with a POST request.
response = requests.post(
api_url,
headers={'X-AUTHKEY': api_key},
data={'telegram': telegram},
)
# You will receive a status 200/201 when successful.
if response.status_code not in (200, 201):
# Or you will find the error (hint) in the response body on failure.
print('[!] Error: {}'.format(response.text))
if __name__ == '__main__':
main()
Zoals gezegd heb ik het in die andere VM wel draaiend, kan ik daar mooi kijken of het blijft draaien en kun jij rustig kijken of je nog een idee hebt. Nu echt slapen 😉
Wellicht is dan de oplossing om de b
in de laatste telegram += data + b"\n"
weer weg te halen. En dan alleen degene te behouden die als tweede is toegevoegd in de split().
Ik kan het toch soort van lokaal reproduceren op de shell en ik krijg voor de drie varianten ook exact dezelfde foutmeldingen:
>>> [str(x, 'utf-8') + "\n" for x in b'abcde\nfghij\nklnmop'.split("\n")]
TypeError: a bytes-like object is required, not 'str'
Daarna de b
voor de + "\n"
:
>>> [str(x, 'utf-8') + b"\n" for x in b'abcde\nfghij\nklnmop'.split("\n")]
TypeError: a bytes-like object is required, not 'str'
Daarna de b
voor de split("\n")
:
>>> [str(x, 'utf-8') + b"\n" for x in b'abcde\nfghij\nklnmop'.split(b"\n")]
TypeError: Can't convert 'bytes' object to str implicitly
Uiteindelijk de eerst toegevoegde b
weghalen (gaat goed):
>>> [str(x, 'utf-8') + "\n" for x in b'abcde\nfghij\nklnmop'.split(b"\n")]
['abcde\n', 'fghij\n', 'klnmop\n']
Ben benieuwd :]
Het script is netjes blijven draaien vannacht:
supervisor> status
p1esp8266 RUNNING pid 19614, uptime 8:34:55
Daarna in de database gekeken op de dsmr-reader
VM en hij lijkt continu te hebben gelogd, nice!
Aanpassing gedaan in het script op de dsmr-reader
VM zoals jij zegt en dan knalt het script er niet meer uit, maar krijg ik wel foutmeldingen:
(dsmrreader) dsmr@dsmrreader-vm:~/dsmr-reader$ python /home/dsmr/dsmr-reader/esp_sockets.py
Listening at socket...
Read telegram:
!ECBD4.2.1(170105080000W)(01540.862*m3)383134)7483647*s)
[!] Error: Failed to parse telegram
Sent telegram to: http://192.168.4.21/api/v1/datalogger/dsmrreading
Listening at socket...
Read telegram:
!F0144.2.1(170105080000W)(01540.862*m3)383134)7483647*s)
[!] Error: Failed to parse telegram
Sent telegram to: http://192.168.4.21/api/v1/datalogger/dsmrreading
Zie je onder Read telegram:
wel een compleet telegram staan? Of slechts een deel zoals je letterlijk hierboven post?
De output hierboven is wat ik zie, heb niks eruit geknipt.
Okee, dan gaat er nu weer iets mis met het samenvoegen. Dit zie ik al terug in een eerder bericht. Ik denk dat je dan even moet kijken of het splitsen op de linefeed goed gaat.
Maar wellicht eerst even iets anders proberen. Ik zie namelijk dat hier recv()
een compleet telegram teruggeeft. Je zou kunnen proberen om die data compleet op te sturen en te hopen dat de recv()
alles in 1x opgehaald heeft.
Script is ook even ingekort zonder comments enzo. Je kunt alles onder API_SERVERS
vervangen met:
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
socket_data = s.recv(1024)
telegram = str(socket_data, 'utf-8')
print('Read telegram:')
print(telegram)
for current_server in API_SERVERS:
api_url, api_key = current_server
print('Sending telegram to:', api_url)
send_telegram(telegram, api_url, api_key)
sleep(1)
def send_telegram(telegram, api_url, api_key):
response = requests.post(
api_url,
headers={'X-AUTHKEY': api_key},
data={'telegram': telegram},
)
if response.status_code not in (200, 201):
print('[!] API error: {}'.format(response.text))
if __name__ == '__main__':
main()
Wellicht werkt dit meteen? read_telegram()
is dus flink ingekort.
Dat werkt meteen idd!
(dsmrreader) dsmr@dsmrreader-vm:~/dsmr-reader$ python /home/dsmr/dsmr-reader/esp_sockets.py
Listening at socket...
Read telegram:
/KFM5KAIFA-METER
1-3:0.2.8(42)
0-0:1.0.0(170105202122W)
...knip, rest van telegram...
0-1:96.1.0(47......34)
0-1:24.2.1(170105200000W)(01544.140*m3)
!0720
Sending telegram to: http://192.168.4.21/api/v1/datalogger/dsmrreading
Listening at socket...
👍
Trouwens, volgens mij doet deze code geen foutafhandeling toch? Stel dat m'n wifi even hikt, dan gaat het script onderuit denk ik?
Top, mooi man. Waarschijnlijk kan die while True:
er dan ook uit, want hij doet toch al meteen een return na de eerste recv()
.
Ik heb overigens in al je voorbeelden ook nog even het serienummer van je gasmeter weggehaald (47......34
), de regel met 0-1:96.1.0
. Er staan dus twee serienummers in DSMR v4 telegrammen.
Verkorte variant werkt ook! En bedankt voor het aanpassen. Zal er op letten, hoef idd niet m'n serienummers de wereld in te gooien.
Voor de volledigheid nog even weer het complete script:
from time import sleep
import socket
import requests
HOST = '192.168.4.17'
PORT = 8088
API_SERVERS = (
('http://192.168.4.21/api/v1/datalogger/dsmrreading', '5S7SVP......O6ZJB'),
)
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
socket_data = s.recv(1024)
telegram = str(socket_data, 'utf-8')
print('Read telegram:')
print(telegram)
for current_server in API_SERVERS:
api_url, api_key = current_server
print('Sending telegram to:', api_url)
send_telegram(telegram, api_url, api_key)
sleep(1)
def send_telegram(telegram, api_url, api_key):
response = requests.post(
api_url,
headers={'X-AUTHKEY': api_key},
data={'telegram': telegram},
)
if response.status_code not in (200, 201):
print('[!] API error: {}'.format(response.text))
if __name__ == '__main__':
main()
Maar wat ik in m'n vorige comment zei, hoe zit het met foutafhandeling? De kans is natuurlijk aanwezig dat de ESP een keer vastloopt/netwerk een hikje geeft. Moet daar nog iets voor ingebouwd?
Top, bedankt voor je geduld en tests.
Ik sluit deze, mocht je toch nog nazorg hebben hierover dan hoor ik het hier wel.
De P1-kabel kan ik afkoppelen, mooi :) Maar hoe integreer ik dit in dsmr-reader
?
Kan ik in supervisor het dsmr_datalogger
component uitschakelen? Dat zorgt voor het uitlezen van de ttyUSB0 toch? Of moet dat ook blijven draaien als ik het via de API erin duw?
dsmr_datalogger
uitcommenten met #
's in de Supervisor config. Je hebt die niet meer nodig als je de bovenstaande opzet gebruikt via de API.Ter referentie. Ik heb een Pi2 aan de meter hangen met kabel. De datalogger is daar uitgeschakeld en vervangen door de variant in bovenstaande docs link. In Supervisor ziet dat er zo uit:
dsmr_backend RUNNING
dsmr_client_datalogger RUNNING
dsmr_webinterface RUNNING
dsmr_client_datalogger
(of hoe je hem ook wilt noemen) stuurt dus de metingen via de API door (ondanks dat de applicatie op hetzelfde apparaat staat). Daarnaast stuurt die ook de metingen door naar een andere, Pi3, die heel ergens anders staat. (als semi-productie-testomgeving).
Bij mij doet de dsmr_client_datalogger
dus de uitlezingen via de P1-kabel, bij jou zou dat via die ESP gaan (beiden slechts transportmiddel).
Ik heb de dsmr_datalogger
uitgeschakeld in supervisor en een eigen supervisor config gemaakt voor het Python-script wat je hebt gemaakt om de ESP8266 uit te lezen. Voor iemand die dit in de toekomst leest en ook geïnteresseerd is om de P1-poort van de slimme meter draadloos uit te lezen, ik heb het hele spulletje (incl. firmware voor ESP8266) hier online gezet.
Dennis, bedankt voor je hulp om dit werkend te krijgen!
Ik was hier toevallig net naar op zoek; eens kijken of het ook bij mij lukt :-D
Als ik handmatig het script van ThinkPadNL zijn site draai, krijg ik na enkele succesvolle telegrams:
Sending telegram to: http://-ip-/api/v1/datalogger/dsmrreading [!] API error: Invalid data Read telegram:
en dit blijft dan door'loop'en in de invalid data...
Op de 'automaat' via supervisor stopt hij ook met het verzenden van data na een paar succesvolle telegrams.
@Calimerorulez welke versie van DSMR heeft je meter?
Zie je dit als een van de eerste regels:
1-3:0.2.8(50)
(v5.0)
of dit:
1-3:0.2.8(42)
(v4.2)
Hey Dennis,
Dat is 1-3:0.2.8(42)
Ik dacht eerst dat het aan de combinatie met domoticz lag, omdat domoticz de netwerkpoort van de esp ook uitleest, maar als ik domoticz stop, wijzigt er niks aan het foutgaan.
Er komt een nieuwe patch de komende dagen, waarin beter wordt gelogd wat alle telegram-input is en welke falen/goed zijn (#224). Ik denk dat het even daarop wachten is en dat debuggen makkelijker maakt.
Ok, ik wacht af. Dank voor het mooie werk.
Ik merk overigens dat als ik een putty sessie open naar mijn esp, dat putty vanzelf afsluit na enkele geldige telegrammen, dus ik denk dat er in de esp-code/een telegram een raar (of een escape) character zit of iets dergelijks... Ik zal eens proberen te debuggen.
edit: een 'nc' sessie naar mijn esp stopt er ook na enkele telegrammen mee; het lijkt erop dat de verbinding door de esp gesloten wordt o.i.d.
Ik gebruik domoticz ook. Als domoticz draait, dan stoort de boel elkaar lijkt het wel. Start ik domoticz, dan maakt domoticz een connectie en pollt goed. Zodra ik het script van ThinkPadNL ernaast start, dan logt domoticz een error, over een verloren connectie. Het script van ThankPadNL pollt wel goed, totdat domoticz een nieuwe poging waagt, na 30 seconden, om te connecten. Dan geeft dsmr-reader een 400 error (print(response.status_code)), ipv de correcte 200 status.
Stop ik domoticz helemaal, dan gaat het script van ThinkPadNL een tijdje langer goed, maar ook daarna komen er weer http 400-errors :-)
Ik heb nog even gekeken. Die Invalid data
melding komt wanneer het POST-request op de API geen 'telegram' veld bevat.
Ik heb nu #224 klaar en daar zit ook betere logging in voor de API. Zodat je straks, als het goed is, kunt zien wat er binnenkomt.
Dit draait nu een dagje bij mij in productie en wanneer dat goed gaat komt de patch uit (waarschijnlijk morgen dus), samen met wat andere kleine dingetjes.
@Calimerorulez zojuist is de v1.5.2
patch uit met verbeterde logging. Aan de hand voor je input heb ik ook betere logging toegevoegd aan de API.
Wil je updaten naar de laatste versie?
Als dat is gelukt staan er als het goed is logfiles hier:
sudo su - dsmr
tail -f logs/dsmrreader.log
(CTRL + C om het tailen af te breken)
Wil je aangeven of die nu een betere foutmelding geven? De 'Invalid data` zou in de logs nu zoiets moeten tonen:
API validation failed with POST data: X
Waarbij X de data is die meegestuurd wordt (dat zou een compleet telegram moeten zijn).
Let erop dat je hier (of uberhaupt op internet) niet je serienummers van je (2?) meters plaatst wanneer je een voorbeeld plakt. Dat zijn lange cijferreeksen van zo rond de 30 tekens aaneensluitend.
Ook de base64
data graag hier niet plaatsen. Mocht dat nodig zijn dan mag dat per e-mail.
Het uitlezen van de ESP8266 gaat hier nog steeds uitstekend:
p1esp8266 RUNNING pid 14642, uptime 4 days, 0:22:53
Nog steeds erg blij mee dat het nu werkt, geen lelijk USB-serial kabel meer in het zicht naar m'n server toe.
Als bij @Calimerorulez de boel er dus uit klapt dan ligt het waarschijnlijk aan de code op je ESP/de ESP zelf. Welke code gebruik je? Die van mijn Bitbucket ?
@dennissiemensma in de log vind ik: [2017-01-10 18:00:35,765] WARNING @ views | API validation failed with POST data: <QueryDict: {'telegram': ['']}>
iedere seconde. Draai ik het schript van ThinkPadNL, dan zie ik 3 base64 encoded messages komen (tussendoor bovenstaande fout) en daarna weer continu om de seconde de bovenstaande fout.
@ThinkPadNL Ik gebruik de code van Romix, vanaf https://github.com/ESP8266nu/ESPEasyPluginPlayground. Ik heb espeasy ingesteld zoals vermeld in de documentatie van Romix.
Als ik de komende dagen meer tijd heb, dan zal ik mijn hele esp upgraden. Ik draai nu op build 138.
Ik zal jouw code eens flashen. Ik snap alleen het nut niet van de specificatie van het te gebruiken protocol op de Config pagina van espeasy. Daar heb ik nu Domoticz HTTP staan.
edit: _P110_P1WifiGateway.ino van jouw bitbucket is aan de code te zien een html-pagina.. ;-)
@Calimerorulez Je zegt 'Iedere seconde draai ik het script van ThinkPad'. Maar hoe bedoel je dat precies? Je start het script en die blijft luisteren naar de ESP8266. Eén keer starten is dus genoeg.
Ik heb de firmware van http://www.esp8266thingies.nl/wp/software/ gepakt, R124b. Die werkt dus, zoals hierboven in de output van supervisor
te zien dus prima.
Bedankt voor de tip m.b.t. het .ino bestand. Ik heb het .ino bestand er nu op gezet ipv HTML-code.
Dank je voor je reactie. De tekst "iedere seconde", dat hoorde bij de regel ervoor. Ik start jouw script gewoon eenmalig op, en laat dat draaien.
Ik heb net de code van espeasy gepulled, P110 erin gehangen, want P1 smart meter zit er standaard niet in als device, dus heb ik de firmware via Arduino IDE zelf gecompileerd en geupload.
Ik heb een nieuwe Wemos D1 R2 genomen, geflasht, maar geen verschil. Als ik putty 'raw' connect op de esp poort, dan vliegt ie er ook na 3 telegrams uit. Ik zal R124b proberen. Draai jij ook Domoticz?
Nee, geen Domoticz. Ik lees de P1 alleen uit naar dsmr-reader
. Ik gebruik Home Assistant voor m'n domotica, maar doe daarin niks met de P1-poort.
Stappenplan:
ESPEasy_R120.zip
uit. ESPEasy.ino.d1_mini.R124b.bin
in dezelfde map als flash.cmd
en rename dat .bin bestand naar ESPEasy_R12*4*_4096.bin
**flash.cmd
en voer de instellingen in (juiste COM-poort, flashsize 4096, build 124).Als je dat allemaal hebt gedaan zou je die R124b
firmware op de Wemos D1 moeten hebben. In de webinterface kun je dan de baudrate instellen en de poort (8088 bijv., die staat in mijn script ook ingevuld).
Die hele combinatie werkt dus erg betrouwbaar en zonder nukken bij mij. Met bovenstaande stappen hoef je dus ook niks te compilen of extra plugins toe te voegen. Die R124b.bin is voldoende aangezien daar alles al in zit.
Dank je, het werkt nu een stuk stabieler denk ik, hij blijft doorlopen. Ik heb de esp maar uit Domoticz verwijderd, want die blijft de connectie van jouw script om zeep helpen. Zodra Domoticz na 30 seconden opnieuw probeert te connecten naar de ESP (want Domoticz begint te klagen over een verloren connectie, als ik jouw script opstart), dan krijg ik weer de betreffende fout. Het lijkt erop dat twee processen/connecties tegelijk naar de esp de boel ophangt.
Ik experimenteer weer verder :-)
Dank!
Goedzo.
Waarom heb je Domoticz nog nodig als je dsmr-reader
hebt draaien? Die kan hetzelfde en meer toch? ;)
Koudwatervrees ;-)
Wellicht dan een splitter ertussen zetten, dan kun je beide gebruiken ;-)
Wat betreft je foutmelding:
API validation failed with POST data: <QueryDict: {'telegram': ['']}>
Dit betekent dat er geen data binnenkomt, de telegram
string is leeg. Dus de API call wordt gedaan, maar de data mist.
Hoe draait het nu @Calimerorulez ? Bij mij nog steeds als een trein, goede combi op deze manier. Ga binnenkort verhuizen en ben erg blij dat ik het op deze manier werkend heb kunnen krijgn.
Een ESP8266 in de meterkast is genoeg. Voeding kan dan via USB-poort van m'n router en m'n server waar VM met dsmr-reader
kan bijv. op zolder komen te staan.
Hier ook als een trein, sinds ik hem uit domoticz gemikt heb 5 dagen geleden. Nogmaals dank voor de code en hulp :-)
Vandaag was de boel even stuk. Ik opende de webinterface van dsmr-reader
toevallig even en zag dat er al 2 uur geen data meer was binnengekomen ( #208 is best wel gewenst 😉 ). De ESP herstart maar toen leek er nog steeds geen data binnen te komen. In supervisor
het uitleesscript herstart en toen leek er weer data binnen te komen. Kan in de logging niet echt een duidelijke oorzaak vinden. Als #208 is geïmplementeerd dan kan ik ook sneller de logging controleren mocht het uitlezen weer stagneren.
Nog wel een verzoek, ik heb de logging in /var/log/supervisor
bekeken en zag daar veel entries staan:
Sending telegram to: http://192.168.4.21/api/v1/datalogger/dsmrreading
[!] API error: Invalid data
Read telegram:
Wat ik eigenlijk mis is een datum en timestamp in de supervisor logging. Is dat eenvoudig toe te voegen in het ESP8266 uitleesscript wat ik gebruik?
Bovenin het script:
from datetime import datetime
Op de plek waar je de logging wil hebben:
print(datetime.now())
Software looks good!
However, i would like to run it inside a VM on my homeserver, which isn't near my smartmete and thus no serial tty port. Would it be possible to include support for reading P1-data from a network socket? I already found this ESP8266 project: http://romix.macuser.nl/software.html and fiddled around with it. With Putty i can receive telegrams, so that part works.