ELV is selling affordable weather sensors http://www.elv.de/ip-wettersensoren-system.html, which can be monitored via an iOS application. To allow that all data is transmitted via a gateway to a server, which the application can access. Sadly the protocol is not documented. I don’t think anybody has documented the way the URL has been constructed, especially the MD5 hash over the first part of it.
This sample code integrates sensors into fhem and I wrote it for Mac OS X, but it should work on Linux, too.
All information (and more) is available on GitHub as well.
To integrate the sensors into fhem, add the sensors to the iOS application and check if they show up. You need to note all sensor IDs. The ID is 6 bytes long and represented as a hexadecimal number. This same code supports sensors of type 2 (temperature only) and type 3 (temperature and humidity), which can be recognized via the first two characters of the ID.
You have to adjust the following things in the code:
- sensors = … Here you have to add all sensors IDs as strings
- downloadPath This needs to be adjusted to point to the log folder of fhem.
- vendorid You might want to change the UUID to a new one (just run uuidgen from the terminal)
Python Code
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import json
import pprint
import datetime
import hashlib
import time
downloadPath = os.path.expanduser('~/fhem-5.7/log')
pp = pprint.PrettyPrinter()
sensors = set(['021122334455',...,'031122334455'])
def parseJSON(jj):
if not jj['success']:
print '#%d %s' % (jj['errorcode'],jj['errormessage'])
return
for device in jj['result']['devices']:
#pp.pprint(device)
deviceTypeId = device['devicetypeid']
deviceName = device['name']
print '%s %s [%d] #%d %s' % (device['deviceid'], deviceName, device['lowbattery'],len(device['measurements']),datetime.datetime.fromtimestamp(device['lastseen']).strftime('%Y-%m-%d %H:%M:%S'))
year = -1
month = -1
logPath = ''
for measurement in device['measurements']:
idx = measurement['idx']
transmitTime = datetime.datetime.fromtimestamp(measurement['c'])
measurementTime = datetime.datetime.fromtimestamp(measurement['ts'])
tx = measurement['tx']
mm = measurement
del mm['idx']
del mm['c']
del mm['ts']
del mm['tx']
if year != measurementTime.year or month != measurementTime.month:
if len(logPath):
f = open(logPath, "w")
for d in sorted(data):
f.write('%s %s\n' % (d,data[d]))
year = measurementTime.year
month = measurementTime.month
logPath = downloadPath + '/S_SENSOR_%s-%d-%02d.log' % (deviceName.upper().replace(' ','_'),year,month)
data = {}
try:
for line in open(logPath).readlines():
vals = line[:-1].split(' ')
data[' '.join(vals[:-1])] = vals[-1]
except:
pass
dataKey = measurementTime.strftime('%Y-%m-%d_%H:%M:%S') + ' SENSOR_' + deviceName.upper().replace(' ','_') + ' '
if deviceTypeId == 2:
data[dataKey + 'TEMP:'] = '%.1f' % (measurement['t1'])
elif deviceTypeId == 3:
data[dataKey + 'TEMP:'] = '%.1f' % (measurement['t1'])
data[dataKey + 'FEUCHTIGKEIT:'] = '%.1f' % (measurement['h'])
else:
pp.pprint(mm)
if len(logPath):
f = open(logPath, "w")
for d in sorted(data):
f.write('%s %s\n' % (d,data[d]))
import urllib
import urllib2
devicetoken = 'empty' # defaults to "empty"
vendorid = '1FB220C4-CC15-4195-97CF-8BE4FD3DAE72' # iOS vendor UUID (returned by iOS, any UUID will do). Launch uuidgen from the terminal to generate a fresh one.
phoneid = 'Unknown' # Phone ID - probably generated by the server based on the vendorid (this string can be "Unknown" and it still works)
version = '1.21' # Info.plist CFBundleShortVersionString
build = '248' # Info.plist CFBundleVersion
executable = 'Mobile Alerts' # Info.plist CFBundleExecutable
bundle = 'de.synertronixx.remotemonitor' # [[NSBundle mainBundle] bundleIdentifier]
lang = 'en' # preferred language
request = "devicetoken=%s&vendorid=%s&phoneid=%s&version=%s&build=%s&executable=%s&bundle=%s&lang=%s" % (devicetoken,vendorid,phoneid,version,build,executable,bundle,lang)
request += '&timezoneoffset=%d' % 60 # local offset to UTC time
request += '&timeampm=%s' % ('true') # 12h vs 24h clock
request += '&usecelsius=%s' % ('true') # Celcius vs Fahrenheit
request += '&usemm=%s' % ('true') # mm va in
request += '&speedunit=%d' % 0 # wind speed (0: m/s, 1: km/h, 2: mph, 3: kn)
request += '×tamp=%s' % datetime.datetime.utcnow().strftime("%s") # current UTC timestamp
requestMD5 = request + 'asdfaldfjadflxgeteeiorut0ß8vfdft34503580' # SALT for the MD5
requestMD5 = requestMD5.replace('-','')
requestMD5 = requestMD5.replace(',','')
requestMD5 = requestMD5.replace('.','')
requestMD5 = requestMD5.lower()
m = hashlib.md5()
m.update(requestMD5)
hexdig = m.hexdigest()
request += '&requesttoken=%s' % hexdig
request += '&deviceids=%s' % ','.join(sensors)
#request += '&measurementfroms=%s' % ('0,' * len(sensors))
#request += '&measurementcounts=%s' % ('50,' * len(sensors))
http_header = {
"User-Agent" : "remotemonitor/248 CFNetwork/758.2.8 Darwin/15.0.0",
"Accept-Language" : "en-us",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Host" : "www.data199.com:8080",
}
# create an urllib2 opener()
opener = urllib2.build_opener()
# create your HTTP request
req = urllib2.Request('http://www.data199.com:8080/api/v1/dashboard', request, http_header)
# submit your request
while True:
res = opener.open(req)
parseJSON(json.loads(res.read()))
print '-' * 40
time.sleep(10*60) # wait for 10 minutes