NEWS
[gelöst] regex
-
-
der wird zwischen >< hinterlegt von diesem code:
#!/usr/bin/python # coding=utf-8 # "DATASHEET": http://cl.ly/ekot # https://gist.github.com/kadamski/92653913a53baf9dd1a8 from __future__ import print_function import serial, struct, sys, time, json DEBUG = 0 CMD_MODE = 2 CMD_QUERY_DATA = 4 CMD_DEVICE_ID = 5 CMD_SLEEP = 6 CMD_FIRMWARE = 7 CMD_WORKING_PERIOD = 8 MODE_ACTIVE = 0 MODE_QUERY = 1 ser = serial.Serial() ser.port = "/dev/ttyUSB0" ser.baudrate = 9600 ser.open() ser.flushInput() byte, data = 0, "" def dump(d, prefix=''): print(prefix + ' '.join(x.encode('hex') for x in d)) def construct_command(cmd, data=[]): assert len(data) <= 12 data += [0,]*(12-len(data)) checksum = (sum(data)+cmd-2)%256 ret = "\xaa\xb4" + chr(cmd) ret += ''.join(chr(x) for x in data) ret += "\xff\xff" + chr(checksum) + "\xab" if DEBUG: dump(ret, '> ') return ret def process_data(d): r = struct.unpack('<HHxxBB', d[2:]) pm25 = r[0]/10.0 pm10 = r[1]/10.0 checksum = sum(ord(v) for v in d[2:8])%256 return [pm25, pm10] #print("PM 2.5: {} μg/m^3 PM 10: {} μg/m^3 CRC={}".format(pm25, pm10, "OK" if (checksum==r[2] and r[3]==0xab) else "NOK")) def process_version(d): r = struct.unpack('<BBBHBB', d[3:]) checksum = sum(ord(v) for v in d[2:8])%256 print("Y: {}, M: {}, D: {}, ID: {}, CRC={}".format(r[0], r[1], r[2], hex(r[3]), "OK" if (checksum==r[4] and r[5]==0xab) else "NOK")) def read_response(): byte = 0 while byte != "\xaa": byte = ser.read(size=1) d = ser.read(size=9) if DEBUG: dump(d, '< ') return byte + d def cmd_set_mode(mode=MODE_QUERY): ser.write(construct_command(CMD_MODE, [0x1, mode])) read_response() def cmd_query_data(): ser.write(construct_command(CMD_QUERY_DATA)) d = read_response() values = [] if d[1] == "\xc0": values = process_data(d) return values def cmd_set_sleep(sleep=1): mode = 0 if sleep else 1 ser.write(construct_command(CMD_SLEEP, [0x1, mode])) read_response() def cmd_set_working_period(period): ser.write(construct_command(CMD_WORKING_PERIOD, [0x1, period])) read_response() def cmd_firmware_ver(): ser.write(construct_command(CMD_FIRMWARE)) d = read_response() process_version(d) def cmd_set_id(id): id_h = (id>>8) % 256 id_l = id % 256 ser.write(construct_command(CMD_DEVICE_ID, [0]*10+[id_l, id_h])) read_response() if __name__ == "__main__": while True: cmd_set_sleep(0) cmd_set_mode(1); for t in range(15): values = cmd_query_data(); if values is not None: print("PM2.5: ", values[0], ", PM10: ", values[1]) time.sleep(2) # open stored data with open('/var/www/html/aqi.json') as json_data: data = json.load(json_data) # check if length is more than 100 and delete first element if len(data) > 100: data.pop(0) # append new values data.append({'pm25': values[0], 'pm10': values[1], 'time': time.strftime("%d.%m.%Y %H:%M:%S")}) # save it with open('/var/www/html/aqi.json', 'w') as outfile: json.dump(data, outfile) print("Going to sleep for 5min...") cmd_set_mode(0); cmd_set_sleep() time.sleep(300)
es geht um einen sds011 sensor, habe das script direkt am pi hinterlegt.
sicherlich würde es leichter gehen wenn ich den wert bei node-red einlesen würde.
Einlesen klappt ja eigentlich auch,
Bin aber leider zu blöd das ich den Sensor in sleep modus bekomme.
Deswegen eigentlich der umweg über die web seite.
Ausser du kannst mir veraten wie ich den sleep modus aktiviere und deaktiviere über node red.Hier die Node-red variate, wie gesagt der sensor ist verbunden und liefert auch die benötigeten werte, doch leider
bekomme ich den befehl nicht hin das er für eine zeit in sleep modus geht
oder hast du eine idee wie der befehl aussehen müsste?
-
@mike1976
Wie @hans_999 schon geschrieben hat sieht der HTML Code jetzt anders aus, als in deinem ersten Post.Wenn die Zahl dann an der entsprechenden Stelle steht solle dazu dann dieser RegEx passen:
/<[^>]*id=['"]aqiPm10['"][^>]*>(\d+)<\/\w+>/
Siehe auch https://regex101.com/r/a41Fp3/2
Der findet eine Zahl innerhalb eines beliebigen Tags (in deinem Code das
div
) mit der IDaqiPm10
, unabhängig davon, ob der Tag noch andere Attribute hat oder ob"
oder'
für die Attribute verwendet wird.Dein Screenshot sieht nach dem den Parser-Adapter aus. Ich bin mir nicht sicher, aber kann sein, dass du da den RegEx ohne die
/
am Anfang und Ende eingeben musst.Bezüglich Node-red kann ich dir leider nicht weiterhelfen.
-
@crycode
Okay auf https://regex101.com/r/a41Fp3/2 geht es,
doch leider will der Adapter nicht.
habe es mit
/<[^>]*id=['"]aqiPm10['"][^>]*>(\d+)<\/\w+>/
und ohne
<[^>]*id=['"]aqiPm10['"][^>]*>(\d+)<\/\w+>
getestet, immer das gleiche. noch eine idee?
-
doch leider will der Adapter nicht.
Dann brauchen wir den "echten" quellcode.
mit dem geposteten kann ich das nicht nachvollziehenBeim Parser gibt es den ein oder anderen feinen Unterschied im Vergleich zu regex101
Wahrscheinlich liegt es nur am Format.
Ich habe mal den folgenden Wert (10.50) genommen:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>AQI</title> <script src="aqi.js"></script> <link rel='stylesheet' href='style.css'> </head> <body> <div class="container"> <h1>AQI</h1> <p class='time' id="time"></p> <div class='aqi-container' id="containerPm25"> <div class='aqi-label'>AQI (PM2.5)</div> <div class='aqi' id="aqiPm25"></div> <div class='pm-label' id="pm25"></div> </div> <div class='aqi-container' id="containerPm10"> <div class='aqi-label'>AQI (PM10)</div> <div class='aqi' id="aqiPm10">10.50</div> <div class='pm-label' id="pm10"></div> </div> </div> </body> <script type="text/javascript"> getData(); setInterval(getData, 60000); </script> </html>
Und mit dem regex
<[^>]*id="aqiPm10">(\d+\.\d+)<\/div>
ausgelesenAuch die genauen Einstellungen im Adapter wären interessant
-
testpi.dyndns-at-home.com:56000
habe ich mal freigeschalten. kannst direkt auf die webseite
Quellcode:
aqi.pyeinstellungen von adapter habe ich oben einen screen gemacht. gibt ja nicht viele punkte
-
und das ist die export file des adapters:
-
@mike1976
Das Problem an der Stelle ist, dass der eigentliche Wert gar nichtdirekt im HTML Code steht, sondern per JavaScript aus der Datei api.json geladen wird. Versuch mal als URLhttp://192.168.1.241/api.json
und als RegEx den:{"pm10":\s+(\d+(\.\d+)?)[^}]*}]
Das sollte dann jeweils den letzten (aktuellsten) Wert auslesen, egal ob es nun z.B.9
oder9.5
ist.Beispiel: https://regex101.com/r/a41Fp3/3
Alternativ könnte man sicherlich auch über den JavaScript Adapter direkt die JSON-Datei laden und daraus dann den gewünschten Wert extrahieren.
-
Leider nein der aufruf geht auch nicht
wenn ich im den link so teste bzw. öffne bekomme ich nur 404 not found
okay muss einsehen das es für mich leider nicht möglich sein wird den wert in iobroker zu bekommen.
Danke an euch allen für euer rasches bemühen.lg mike
-
@mike1976 ups, mein Fehler... das sollte
aqi.json
heißen. Dann müsste er die Datei auch finden -
okay damit liest der adapter jetzt die seite ein, aber er will mir einfach keinen wert geben.
Hast recht auf https://regex101.com/r/a41Fp3/3 geht es einwandfrei.
Habe jetzt alle varianten gesteset im adapter:
boolean
number(.)
number(,)
string
JSONeigentlich müsste der type ja nummer(.) sein, aber nicht um die bohne das er mir einen wert gibt
-
update bekomme jetzt einen wert
habe bei den einstellungen noch Num 4 drinnen stehen
geändert auf 0 und siehe da es kommt was rein.ich weis das kling jetzt sicherlich frech aber wie könnte ich jetzt immer die letzten werte von
pm25 und seperat pm10 haben.im moment ist es ja immer der von pm10.
Man Tausend dank erstmal
-
Denke habe es hinbekommen
geändert auf"pm25":\s+(\d+(\.\d+)?)[^}]*}]
-
Danke das auslesen der "µg/m³" geht nun einwandfrei.
Ist es eigentlich möglich den "AQI (PM2.5)" und den "AQI (PM15)" auch auszulesen?
in http://192.168.1.241/aqi.json wird der ja nicht augelistet.Das sind die Webseiten auf den das script aufbaut:
$ wget -O /var/www/html/index.html https://raw.githubusercontent.com/zefanja/aqi/master/html/index.html
$ wget -O /var/www/html/aqi.js https://raw.githubusercontent.com/zefanja/aqi/master/html/aqi.js
$ wget -O /var/www/html/style.css https://raw.githubusercontent.com/zefanja/aqi/master/html/style.css -
@mike1976 Das sieht doch schon mal gut aus
Ist es eigentlich möglich den "AQI (PM2.5)" und den "AQI (PM15)" auch auszulesen?
Direkt auslesen kannst du die beiden Werte nicht, da die ja nicht so in den Daten enthalten sind.
Du kannst die Berechnung aber in ioBroker über ein Script nachbilden und die Ergebnisse dann eigene Datenpunkte schreiben.Dann wäre aber wieder der oben schon mal erwähnte Weg direkt die JSON-Datei zu laden die schönere und zuverlässigere Variante.
Das Script dazu sieht dann so aussehen:// URL von der die JSON-Daten geladen werden const url = 'http://192.168.1.241/aqi.json'; // Datenpunkte (javascript.0.XX) const objPm10 = 'aqi.pm10'; const objPm25 = 'aqi.pm25'; const objAqiPm10 = 'aqi.aqiPm10'; const objAqiPm25 = 'aqi.aqiPm25'; // Objekete anlegen, falls nicht vorhanden createState(objPm10, { name: 'PM10', unit: 'µg/m³', type: 'number', role: 'value' }); createState(objPm25, { name: 'PM2.5', unit: 'µg/m³', type: 'number', role: 'value' }); createState(objAqiPm10, { name: 'AQI (PM10)', type: 'number', role: 'value' }); createState(objAqiPm25, { name: 'AQI (PM2.5)', type: 'number', role: 'value' }); // alle 5 Sekunden die Daten laden schedule('*/5 * * * * *', loadData); /** * Funktion zum Laden der Daten. */ function loadData () { request(url, (err, res, body) => { if (err) { log(err.toString(), 'error'); return; } const data = JSON.parse(body); const pm10 = data[data.length -1].pm10; const pm25 = data[data.length -1].pm25; const aqiPm10 = calcAQIpm10(pm10); const aqiPm25 = calcAQIpm25(pm25); // Log-Ausgabe zum Testen // log(`pm10: ${pm10}, pm25: ${pm25}, aqiPm10: ${aqiPm10}, aqiPm25: ${aqiPm25}`); setState(objPm10, pm10, true); setState(objPm25, pm25, true); setState(objAqiPm10, aqiPm10, true); setState(objAqiPm25, aqiPm25, true); }); } /** * Berechnung von AQI PM 2.5. * Quelle: https://github.com/zefanja/aqi/blob/master/html/aqi.js#L55 */ function calcAQIpm25(pm25) { let pm1 = 0; let pm2 = 12; let pm3 = 35.4; let pm4 = 55.4; let pm5 = 150.4; let pm6 = 250.4; let pm7 = 350.4; let pm8 = 500.4; let aqi1 = 0; let aqi2 = 50; let aqi3 = 100; let aqi4 = 150; let aqi5 = 200; let aqi6 = 300; let aqi7 = 400; let aqi8 = 500; let aqipm25 = 0; if (pm25 >= pm1 && pm25 <= pm2) { aqipm25 = ((aqi2 - aqi1) / (pm2 - pm1)) * (pm25 - pm1) + aqi1; } else if (pm25 >= pm2 && pm25 <= pm3) { aqipm25 = ((aqi3 - aqi2) / (pm3 - pm2)) * (pm25 - pm2) + aqi2; } else if (pm25 >= pm3 && pm25 <= pm4) { aqipm25 = ((aqi4 - aqi3) / (pm4 - pm3)) * (pm25 - pm3) + aqi3; } else if (pm25 >= pm4 && pm25 <= pm5) { aqipm25 = ((aqi5 - aqi4) / (pm5 - pm4)) * (pm25 - pm4) + aqi4; } else if (pm25 >= pm5 && pm25 <= pm6) { aqipm25 = ((aqi6 - aqi5) / (pm6 - pm5)) * (pm25 - pm5) + aqi5; } else if (pm25 >= pm6 && pm25 <= pm7) { aqipm25 = ((aqi7 - aqi6) / (pm7 - pm6)) * (pm25 - pm6) + aqi6; } else if (pm25 >= pm7 && pm25 <= pm8) { aqipm25 = ((aqi8 - aqi7) / (pm8 - pm7)) * (pm25 - pm7) + aqi7; } return Math.round(aqipm25); } /** * Berechnung von AQI PM 10. * Quelle: https://github.com/zefanja/aqi/blob/master/html/aqi.js#L94 */ function calcAQIpm10(pm10) { let pm1 = 0; let pm2 = 54; let pm3 = 154; let pm4 = 254; let pm5 = 354; let pm6 = 424; let pm7 = 504; let pm8 = 604; let aqi1 = 0; let aqi2 = 50; let aqi3 = 100; let aqi4 = 150; let aqi5 = 200; let aqi6 = 300; let aqi7 = 400; let aqi8 = 500; let aqipm10 = 0; if (pm10 >= pm1 && pm10 <= pm2) { aqipm10 = ((aqi2 - aqi1) / (pm2 - pm1)) * (pm10 - pm1) + aqi1; } else if (pm10 >= pm2 && pm10 <= pm3) { aqipm10 = ((aqi3 - aqi2) / (pm3 - pm2)) * (pm10 - pm2) + aqi2; } else if (pm10 >= pm3 && pm10 <= pm4) { aqipm10 = ((aqi4 - aqi3) / (pm4 - pm3)) * (pm10 - pm3) + aqi3; } else if (pm10 >= pm4 && pm10 <= pm5) { aqipm10 = ((aqi5 - aqi4) / (pm5 - pm4)) * (pm10 - pm4) + aqi4; } else if (pm10 >= pm5 && pm10 <= pm6) { aqipm10 = ((aqi6 - aqi5) / (pm6 - pm5)) * (pm10 - pm5) + aqi5; } else if (pm10 >= pm6 && pm10 <= pm7) { aqipm10 = ((aqi7 - aqi6) / (pm7 - pm6)) * (pm10 - pm6) + aqi6; } else if (pm10 >= pm7 && pm10 <= pm8) { aqipm10 = ((aqi8 - aqi7) / (pm8 - pm7)) * (pm10 - pm7) + aqi7; } return Math.round(aqipm10); }
Die beiden Funktionen
calcAQIpm25
undcalcAQIpm10
sind direkt aus deraqi.js
übernommen. Ich habe dabei lediglich den Rückgabewert angepasst, sodass immer eine Zahl ohne Nachkommastellen zurückgegeben wird.Die Daten sind dann unter
javascript.0.aqi.*
verfügbar. -
Ähhh... Was soll ich sagen ausser 1000 mal Danke.
Das funktioniert einwandfrei
Hatte die hoffnung schon aufgegeben.
Kommer leider aus der SPS welt und script mässig ein kompleter noob.Danke nochmal hast mir einen riesen schritt weitergeholfen, jetzt muss ich nur noch eine vernünftige Wochenzeitschaltuhr für die Vis finden.
GLG Mike
PS: Danke für deine Zeit und Hilfe