Add sensors, /latest API route
This commit is contained in:
parent
9157ff6da1
commit
9d6997bfc7
109
main.py
109
main.py
|
@ -112,7 +112,7 @@ class Sensor():
|
||||||
}
|
}
|
||||||
sensors_client.write_points([point])
|
sensors_client.write_points([point])
|
||||||
|
|
||||||
logging.info('Wrote %s data to InfluxDB.', self)
|
logging.info('Wrote %s data to InfluxDB: %s', self, data)
|
||||||
|
|
||||||
def check_update(self):
|
def check_update(self):
|
||||||
if self.update_period:
|
if self.update_period:
|
||||||
|
@ -150,7 +150,7 @@ class ThermostatSensor(Sensor):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
async def poll(self):
|
async def poll(self):
|
||||||
data = await getter('http://{}/query/info'.format(self.ip))
|
data = await getter('http://{}/query/info'.format(self.ip)) or {}
|
||||||
self.update(data)
|
self.update(data)
|
||||||
|
|
||||||
class ERTSCMSensor(Sensor):
|
class ERTSCMSensor(Sensor):
|
||||||
|
@ -183,6 +183,18 @@ class DustSensor(Sensor):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class AirSensor(Sensor):
|
||||||
|
type_ = 'air'
|
||||||
|
update_period = 15
|
||||||
|
|
||||||
|
def transform(self, data):
|
||||||
|
for key, value in data.items():
|
||||||
|
# what happens if you do this to a timestamp?
|
||||||
|
try:
|
||||||
|
data[key] = float(round(value, 1))
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
class SleepSensor(Sensor):
|
class SleepSensor(Sensor):
|
||||||
type_ = 'sleep'
|
type_ = 'sleep'
|
||||||
update_period = 90
|
update_period = 90
|
||||||
|
@ -195,6 +207,9 @@ class SleepSensor(Sensor):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class SolarSensor(Sensor):
|
||||||
|
type_ = 'solar'
|
||||||
|
|
||||||
class Acurite606TX(Sensor):
|
class Acurite606TX(Sensor):
|
||||||
type_ = 'temperature'
|
type_ = 'temperature'
|
||||||
bad_keys = [
|
bad_keys = [
|
||||||
|
@ -203,11 +218,39 @@ class Acurite606TX(Sensor):
|
||||||
'battery_ok',
|
'battery_ok',
|
||||||
]
|
]
|
||||||
update_period = 40
|
update_period = 40
|
||||||
|
offset = 0.0
|
||||||
|
|
||||||
|
def __init__(self, id_, name, offset=0.0):
|
||||||
|
self.id_ = id_
|
||||||
|
self.name = name
|
||||||
|
self.offset = offset
|
||||||
|
|
||||||
def transform(self, data):
|
def transform(self, data):
|
||||||
if data['battery_ok'] != 1:
|
if data['battery_ok'] != 1:
|
||||||
logging.error('%s battery not ok!', self)
|
logging.error('%s battery not ok!', self)
|
||||||
data['temperature_C'] = float(data['temperature_C'])
|
data['temperature_C'] = float(data['temperature_C']) + self.offset
|
||||||
|
|
||||||
|
class AcuRite6002RM(Sensor):
|
||||||
|
type_ = 'temperature'
|
||||||
|
bad_keys = [
|
||||||
|
'model',
|
||||||
|
'mic',
|
||||||
|
'battery_ok',
|
||||||
|
'channel',
|
||||||
|
]
|
||||||
|
update_period = 40
|
||||||
|
offset = 0.0
|
||||||
|
|
||||||
|
def __init__(self, id_, name, offset=0.0):
|
||||||
|
self.id_ = id_
|
||||||
|
self.name = name
|
||||||
|
self.offset = offset
|
||||||
|
|
||||||
|
def transform(self, data):
|
||||||
|
if data['battery_ok'] != 1:
|
||||||
|
logging.error('%s battery not ok!', self)
|
||||||
|
data['temperature_C'] = float(data['temperature_C']) + self.offset
|
||||||
|
data['humidity'] = float(data['humidity'])
|
||||||
|
|
||||||
|
|
||||||
async def poll_sensors():
|
async def poll_sensors():
|
||||||
|
@ -228,7 +271,7 @@ async def process_mqtt(message):
|
||||||
topic = message.topic
|
topic = message.topic
|
||||||
logging.debug('MQTT topic: %s, message: %s', topic, text)
|
logging.debug('MQTT topic: %s, message: %s', topic, text)
|
||||||
|
|
||||||
if topic == 'test':
|
if topic.startswith('test'):
|
||||||
logging.info('MQTT test, message: %s', text)
|
logging.info('MQTT test, message: %s', text)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -251,7 +294,7 @@ async def fetch_mqtt():
|
||||||
|
|
||||||
async def owntracks(request):
|
async def owntracks(request):
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
logging.info('Web data: %s', str(data))
|
logging.debug('Web data: %s', str(data))
|
||||||
|
|
||||||
if data.get('_type', '') == 'location':
|
if data.get('_type', '') == 'location':
|
||||||
data['id'] = data['topic'].split('/')[-1]
|
data['id'] = data['topic'].split('/')[-1]
|
||||||
|
@ -268,6 +311,9 @@ async def history(request):
|
||||||
measurement = request.match_info.get('measurement')
|
measurement = request.match_info.get('measurement')
|
||||||
name = request.match_info.get('name')
|
name = request.match_info.get('name')
|
||||||
|
|
||||||
|
if name not in [x.name for x in sensors.sensors]:
|
||||||
|
raise
|
||||||
|
|
||||||
end_unix = request.rel_url.query.get('end', None)
|
end_unix = request.rel_url.query.get('end', None)
|
||||||
if end_unix:
|
if end_unix:
|
||||||
end_unix = int(end_unix)
|
end_unix = int(end_unix)
|
||||||
|
@ -293,8 +339,12 @@ async def history(request):
|
||||||
elif duration == 'year':
|
elif duration == 'year':
|
||||||
start = end - timedelta(days=365)
|
start = end - timedelta(days=365)
|
||||||
window = '1d'
|
window = '1d'
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
window = request.rel_url.query.get('window', window)
|
window = request.rel_url.query.get('window', window)
|
||||||
|
if window not in ['10m', '1h', '1d', '7d', '30d']:
|
||||||
|
raise
|
||||||
|
|
||||||
if name == 'Water':
|
if name == 'Water':
|
||||||
scale = 10
|
scale = 10
|
||||||
|
@ -308,38 +358,54 @@ async def history(request):
|
||||||
|
|
||||||
if measurement == 'temperature':
|
if measurement == 'temperature':
|
||||||
client = sensors_client
|
client = sensors_client
|
||||||
q = 'select mean("temperature_C") as temperature_C from temperature where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
q = 'select mean("temperature_C") as temperature_C, mean("humidity") as humidity from temperature where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
||||||
elif measurement == 'ertscm':
|
elif measurement == 'ertscm':
|
||||||
client = sensors_client
|
client = sensors_client
|
||||||
q = 'select derivative(max("consumption_data"))*{} as delta, max("consumption_data")*{} as max from ertscm where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(previous)'.format(scale, scale, name, start, end, window)
|
q = 'select derivative(max("consumption_data"))*{} as delta, max("consumption_data")*{} as max from ertscm where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(previous)'.format(scale, scale, name, start, end, window)
|
||||||
elif measurement == 'thermostat':
|
elif measurement == 'thermostat':
|
||||||
client = sensors_client
|
client = sensors_client
|
||||||
q = 'select first("spacetemp") as spacetemp, first("heattemp") as heattemp, first("state") as state from thermostat where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(previous)'.format(name, start, end, window)
|
q = 'select first("spacetemp") as spacetemp, first("heattemp") as heattemp, mode("state") as state from thermostat where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(previous)'.format(name, start, end, window)
|
||||||
elif measurement == 'dust':
|
elif measurement == 'dust':
|
||||||
client = sensors_client
|
client = sensors_client
|
||||||
q = 'select max("avg_p10") as max_p10, max("avg_p25") as max_p25 from dust where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
q = 'select max("avg_p10") as max_p10, max("avg_p25") as max_p25 from dust where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
||||||
|
elif measurement == 'air':
|
||||||
|
client = sensors_client
|
||||||
|
q = 'select max("pm10") as max_p10, max("pm25") as max_p25, max("co2") as max_co2, max("voc_idx") as max_voc from air where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
||||||
|
elif measurement == 'lux':
|
||||||
|
client = sensors_client
|
||||||
|
q = 'select mean("lux") as lux from air where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
||||||
elif measurement == 'sleep':
|
elif measurement == 'sleep':
|
||||||
client = sensors_client
|
client = sensors_client
|
||||||
q = 'select max("max_mag") as max_mag from sleep where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
q = 'select max("max_mag") as max_mag from sleep where "name" = \'{}\' and time >= {}s and time < {}s group by time({}) fill(linear)'.format(name, start, end, window)
|
||||||
elif measurement == 'solar':
|
elif measurement == 'solar':
|
||||||
client = solar_client
|
client = solar_client
|
||||||
q = 'select max("actual_total") as actual_total from ecu where time >= {}s and time < {}s group by time({}) fill(linear)'.format(start, end, window)
|
q = 'select max("actual_total") as actual_total, last("lifetime_energy")-first("lifetime_energy") as lifetime_energy from ecu where time >= {}s and time < {}s group by time({}) fill(linear)'.format(start, end, window)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
q += ' tz(\'America/Edmonton\')'
|
q += ' tz(\'America/Edmonton\')'
|
||||||
|
|
||||||
#if window and moving_average:
|
|
||||||
# q = 'select moving_average(mean("value"),{}) as value from {} where "name" = \'{}\' and time >= {}s and time < {}s group by time({}m) fill(none)'.format(moving_average, measurement, name, start, end, window)
|
|
||||||
#elif window:
|
|
||||||
# q = 'select mean("value") as value from {} where "name" = \'{}\' and time >= {}s and time < {}s group by time({}m) fill(none)'.format(measurement, name, start, end, window)
|
|
||||||
#elif moving_average:
|
|
||||||
# q = 'select moving_average("value", {}) as value from {} where "name" = \'{}\' and time >= {}s and time < {}s'.format(moving_average, name, start, end)
|
|
||||||
#else:
|
|
||||||
# q = 'select value from {} where "name" = \'{}\' and time >= {}s and time < {}s'.format(measurement, name, start, end)
|
|
||||||
|
|
||||||
result = list(client.query(q).get_points())
|
result = list(client.query(q).get_points())
|
||||||
|
|
||||||
return web.json_response(result)
|
return web.json_response(result)
|
||||||
|
|
||||||
|
async def latest(request):
|
||||||
|
result = dict()
|
||||||
|
|
||||||
|
for sensor in sensors:
|
||||||
|
if sensor.type_ == 'solar':
|
||||||
|
continue
|
||||||
|
|
||||||
|
q = 'select * from {} where "name" = \'{}\' order by desc limit 1'.format(sensor.type_, sensor.name)
|
||||||
|
points = sensors_client.query(q).get_points()
|
||||||
|
point = list(points)
|
||||||
|
|
||||||
|
if sensor.type_ not in result:
|
||||||
|
result[sensor.type_] = dict()
|
||||||
|
|
||||||
|
result[sensor.type_][sensor.name] = point
|
||||||
|
|
||||||
|
return web.json_response(result)
|
||||||
|
|
||||||
async def index(request):
|
async def index(request):
|
||||||
return web.Response(text='hello world', content_type='text/html')
|
return web.Response(text='hello world', content_type='text/html')
|
||||||
|
@ -348,16 +414,19 @@ if __name__ == '__main__':
|
||||||
app.router.add_get('/', index)
|
app.router.add_get('/', index)
|
||||||
app.router.add_post('/owntracks', owntracks)
|
app.router.add_post('/owntracks', owntracks)
|
||||||
app.router.add_get('/history/{measurement}/{name}', history)
|
app.router.add_get('/history/{measurement}/{name}', history)
|
||||||
|
app.router.add_get('/latest', latest)
|
||||||
|
|
||||||
sensors.add(ThermostatSensor('thermostat2', '192.168.69.152', 'Venstar'))
|
sensors.add(ThermostatSensor('thermostat2', '192.168.69.152', 'Venstar'))
|
||||||
sensors.add(ERTSCMSensor('31005493', 'Water'))
|
sensors.add(ERTSCMSensor('31005493', 'Water'))
|
||||||
sensors.add(ERTSCMSensor('41249312', 'Gas'))
|
sensors.add(ERTSCMSensor('41249312', 'Gas'))
|
||||||
sensors.add(OwnTracksSensor('owntracks1', 'OwnTracks'))
|
sensors.add(OwnTracksSensor('owntracks1', 'OwnTracks'))
|
||||||
sensors.add(DustSensor('dust1', 'Living Room'))
|
sensors.add(AirSensor('air1', 'Living Room'))
|
||||||
sensors.add(Acurite606TX('231', 'Outside'))
|
sensors.add(Acurite606TX('231', 'Outside'))
|
||||||
sensors.add(Acurite606TX('226', 'Bedroom'))
|
sensors.add(AcuRite6002RM('5613', 'Seeds', 0.0)) # A
|
||||||
sensors.add(Acurite606TX('132', 'Nook'))
|
sensors.add(AcuRite6002RM('5109', 'Nook', 0.4)) # B
|
||||||
|
sensors.add(AcuRite6002RM('11087', 'Bedroom', -0.3)) # C
|
||||||
sensors.add(SleepSensor('sleep1', 'Bedroom'))
|
sensors.add(SleepSensor('sleep1', 'Bedroom'))
|
||||||
|
sensors.add(SolarSensor('solar', 'Solar'))
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.create_task(poll_sensors())
|
loop.create_task(poll_sensors())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user