Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 50 additions & 26 deletions LYWSD03MMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import math
import logging
import json
import requests

@dataclass
class Measurement:
Expand Down Expand Up @@ -102,26 +103,48 @@ def thread_SendingData():
print("Measurements for " + mea.sensorname + " are identical; don't send data\n")
identicalCounters[mea.sensorname]+=1
continue
fmt = "sensorname,temperature,humidity,voltage" #don't try to seperate by semicolon ';' os.system will use that as command seperator
if ' ' in mea.sensorname:
sensorname = '"' + mea.sensorname + '"'
else:
sensorname = mea.sensorname
params = sensorname + " " + str(mea.temperature) + " " + str(mea.humidity) + " " + str(mea.voltage)
if (args.TwoPointCalibration or args.offset): #would be more efficient to generate fmt only once
fmt +=",humidityCalibrated"
params += " " + str(mea.calibratedHumidity)
if (args.battery):
fmt +=",batteryLevel"
params += " " + str(mea.battery)
if (args.rssi):
fmt +=",rssi"
params += " " + str(mea.rssi)
params += " " + str(mea.timestamp)
fmt +=",timestamp"
cmd = path + "/" + args.callback + " " + fmt + " " + params
print(cmd)
ret = os.system(cmd)

if args.callback:
fmt = "sensorname,temperature,humidity,voltage" #don't try to seperate by semicolon ';' os.system will use that as command seperator
if ' ' in mea.sensorname:
sensorname = '"' + mea.sensorname + '"'
else:
sensorname = mea.sensorname
params = sensorname + " " + str(mea.temperature) + " " + str(mea.humidity) + " " + str(mea.voltage)
if (args.TwoPointCalibration or args.offset): #would be more efficient to generate fmt only once
fmt +=",humidityCalibrated"
params += " " + str(mea.calibratedHumidity)
if (args.battery):
fmt +=",batteryLevel"
params += " " + str(mea.battery)
if (args.rssi):
fmt +=",rssi"
params += " " + str(mea.rssi)
params += " " + str(mea.timestamp)
fmt +=",timestamp"
cmd = path + "/" + args.callback + " " + fmt + " " + params
print(cmd)
ret = os.system(cmd)

if args.httpcallback:
url = args.httpcallback.format(
sensorname=mea.sensorname,
temperature=mea.temperature,
humidity=mea.humidity,
voltage=mea.voltage,
humidityCalibrated=mea.calibratedHumidity,
batteryLevel=mea.battery,
rssi=mea.rssi,
timestamp=mea.timestamp,
)
print(url)
ret = 0
try:
r = requests.get(url, verify=False, timeout=1)
r.raise_for_status()
except requests.exceptions.RequestException as e:
ret = 1

if (ret != 0):
measurements.appendleft(mea) #put the measurement back
print ("Data couln't be send to Callback, retrying...")
Expand Down Expand Up @@ -241,17 +264,16 @@ def handleNotification(self, cHandle, data):
print("Calibrated humidity: " + str(humidityCalibrated))
measurement.calibratedHumidity = humidityCalibrated

if(args.callback):
if args.callback or args.httpcallback:
measurements.append(measurement)


if(args.mqttconfigfile):
if measurement.calibratedHumidity == 0:
measurement.calibratedHumidity = measurement.humidity
jsonString=buildJSONString(measurement)
myMQTTPublish(MQTTTopic,jsonString)
#MQTTClient.publish(MQTTTopic,jsonString,1)


except Exception as e:
print("Fehler")
Expand Down Expand Up @@ -311,6 +333,7 @@ def MQTTOnDisconnect(client, userdata,rc):

callbackgroup = parser.add_argument_group("Callback related arguments")
callbackgroup.add_argument("--callback","-call", help="Pass the path to a program/script that will be called on each new measurement")
callbackgroup.add_argument("--httpcallback","-http", help="Pass the URL to a program/script that will be called on each new measurement")
callbackgroup.add_argument("--name","-n", help="Give this sensor a name reported to the callback script")
callbackgroup.add_argument("--skipidentical","-skip", help="N consecutive identical measurements won't be reported to callbackfunction",metavar='N', type=int, default=0)
callbackgroup.add_argument("--influxdb","-infl", help="Optimize for writing data to influxdb,1 timestamp optimization, 2 integer optimization",metavar='N', type=int, default=0)
Expand Down Expand Up @@ -395,7 +418,7 @@ def MQTTOnDisconnect(client, userdata,rc):
if not args.name:
args.name = args.device

if args.callback:
if args.callback or args.httpcallback:
dataThread = threading.Thread(target=thread_SendingData)
dataThread.start()

Expand Down Expand Up @@ -612,9 +635,10 @@ def le_advertise_packet_handler(mac, adv_type, data, rssi):
if measurement.calibratedHumidity == 0:
measurement.calibratedHumidity = measurement.humidity

if(args.callback):
if args.callback or args.httpcallback:
measurements.append(measurement)
if(args.mqttconfigfile):

if args.mqttconfigfile:
jsonString=buildJSONString(measurement)
myMQTTPublish(currentMQTTTopic,jsonString)
#MQTTClient.publish(currentMQTTTopic,jsonString,1)
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ For example Raspbian Stretch has only Python 3.5.3. If you like to upgrade your

If you like installing/compiling Python3.7 please take a look at this tutorial https://gist.github.com/SeppPenner/6a5a30ebc8f79936fa136c524417761d However it took about 5 hours to compile/run the regressiontests on a Raspberry PI3B. I use this compiled version directly without install. If you do, too, you have to change the first line in the script, pointing to your compiled Python version. For bluepy you can copy the bluepy-folder from home/pi/.local/lib/python3.7/site-packages/bluepy to <yourPath>Python-3.7.4/Lib and do a chmod +x bluepy-helper in <yourPath>Python-3.7.4/Lib/bluepy

Prequisites: python3 bluez python3-pip bluepy
Prequisites: python3 bluez python3-pip bluepy requests
install via

`sudo apt install python3 bluez python3-pip`

`pip3 install bluepy`
`pip3 install bluepy requests`

If you use integrated MQTT client paho-mqtt is needed. Install via

Expand Down Expand Up @@ -53,7 +53,7 @@ usage: LYWSD03MMC.py [-h] [--device AA:BB:CC:DD:EE:FF] [--battery ]
[--offset OFFSET] [--TwoPointCalibration]
[--calpoint1 CALPOINT1] [--offset1 OFFSET1]
[--calpoint2 CALPOINT2] [--offset2 OFFSET2]
[--callback CALLBACK] [--name NAME] [--skipidentical N]
[--callback CALLBACK] [--httpcallback URL] [--name NAME] [--skipidentical N]
[--influxdb N] [--atc] [--watchdogtimer X]
[--devicelistfile DEVICELISTFILE] [--onlydevicelist]
[--rssi]
Expand Down Expand Up @@ -97,6 +97,9 @@ Callback related arguments:
--callback CALLBACK, -call CALLBACK
Pass the path to a program/script that will be called
on each new measurement
--httpcallback URL, -http URL
Pass the URL to a program/script that will be called
on each new measurement
--name NAME, -n NAME Give this sensor a name reported to the callback
script
--skipidentical N, -skip N
Expand Down Expand Up @@ -544,7 +547,7 @@ Calibrated humidity: 49

## Callback for processing the data

Via the --call option a script can be passed to sent the data to.
Via the --callback option a script can be passed to sent the data to.
Example
`./LYWSD03MMC.py -d AA:BB:CC:DD:EE:FF -2p -p2 75 -o2 -4 -p1 33 -o1 -6 --name MySensor --callback sendToFile.sh`
If you don't give the sensor a name, the MAC-Address is used. The callback script must be within the same folder as this script.
Expand All @@ -562,6 +565,12 @@ Gives in data.txt `sensorname,temperature,humidity,voltage,humidityCalibrated,ti

Whereas the timestamp is in the Unix timestamp format in UTC (seconds since 1.1.1970 00:00).

Via the --httpcallback option a formatted URL can be passed to sent the data to.
Example
`./LYWSD03MMC.py -d AA:BB:CC:DD:EE:FF -2p -p2 75 -o2 -4 -p1 33 -o1 -6 --name MySensor --httpcallback "http://127.0.0.1:8080/myscript?name={sensorname}&temp={temperature}&hum={humidity}&bat={batteryLevel}"`

This will call the script at the given URL and fill in the formatted values. Just like the built in MQTT support this is less expensive than executing a script via the --callback option every time a measurement is received. Supported values are: sensorname, temperature, humidity, voltage, humidityCalibrated, batteryLevel, rssi, timestamp.

There is an option not to report identical data to the callback. To distinguish between a failure and constantly the same values are read, the option takes the number after which identical measurements the data is reportet to the callback. Use the `--skipidentical N` for this. E.g. `--skipidentical 1` means 1 identical measurement is skipped, so only every second identical measurement is reportet to callback. I recommend numbers between 10 and 50, giving at least every minute respectively 5 minutes a call to the callback script (With 10 and 50 the actual time is slightly higher than 1 respectively 5 minutes). It is recommended to use the `--round` and `--debounce` option, otherwise there is a lot of noise with changing the temperature. See https://github.com/JsBergbau/MiTemperature2/issues/2

All data received from the sensor is stored in a list and transmitted sequentially. This means if your backend like influxdb is not reachable when a new measurement is received, it will be tried again later (currently waiting 5 seconds before the next try). Thus no data is lost when your storage engine has some trouble. There is no upper limit (the only limit should be the RAM). Keep this in mind when specifing a wrong backend.
Expand Down