From 9d6997bfc7fc1fbf54ef7aea81fdd9ada9f13b50 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 20 Jun 2022 05:50:04 +0000 Subject: [PATCH] Add sensors, /latest API route --- main.py | 109 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 20 deletions(-) diff --git a/main.py b/main.py index d239c68..f96fe55 100644 --- a/main.py +++ b/main.py @@ -112,7 +112,7 @@ class Sensor(): } 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): if self.update_period: @@ -150,7 +150,7 @@ class ThermostatSensor(Sensor): self.name = name 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) class ERTSCMSensor(Sensor): @@ -183,6 +183,18 @@ class DustSensor(Sensor): except TypeError: 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): type_ = 'sleep' update_period = 90 @@ -195,6 +207,9 @@ class SleepSensor(Sensor): except TypeError: pass +class SolarSensor(Sensor): + type_ = 'solar' + class Acurite606TX(Sensor): type_ = 'temperature' bad_keys = [ @@ -203,11 +218,39 @@ class Acurite606TX(Sensor): 'battery_ok', ] 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 + +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']) + data['temperature_C'] = float(data['temperature_C']) + self.offset + data['humidity'] = float(data['humidity']) async def poll_sensors(): @@ -228,7 +271,7 @@ async def process_mqtt(message): topic = message.topic logging.debug('MQTT topic: %s, message: %s', topic, text) - if topic == 'test': + if topic.startswith('test'): logging.info('MQTT test, message: %s', text) return @@ -251,7 +294,7 @@ async def fetch_mqtt(): async def owntracks(request): data = await request.json() - logging.info('Web data: %s', str(data)) + logging.debug('Web data: %s', str(data)) if data.get('_type', '') == 'location': data['id'] = data['topic'].split('/')[-1] @@ -268,6 +311,9 @@ async def history(request): measurement = request.match_info.get('measurement') 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) if end_unix: end_unix = int(end_unix) @@ -293,8 +339,12 @@ async def history(request): elif duration == 'year': start = end - timedelta(days=365) window = '1d' + else: + raise window = request.rel_url.query.get('window', window) + if window not in ['10m', '1h', '1d', '7d', '30d']: + raise if name == 'Water': scale = 10 @@ -308,38 +358,54 @@ async def history(request): if measurement == 'temperature': 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': 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) elif measurement == 'thermostat': 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': 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) + 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': 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) elif measurement == 'solar': 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\')' - #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()) 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): 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_post('/owntracks', owntracks) 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(ERTSCMSensor('31005493', 'Water')) sensors.add(ERTSCMSensor('41249312', 'Gas')) 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('226', 'Bedroom')) - sensors.add(Acurite606TX('132', 'Nook')) + sensors.add(AcuRite6002RM('5613', 'Seeds', 0.0)) # A + sensors.add(AcuRite6002RM('5109', 'Nook', 0.4)) # B + sensors.add(AcuRite6002RM('11087', 'Bedroom', -0.3)) # C sensors.add(SleepSensor('sleep1', 'Bedroom')) + sensors.add(SolarSensor('solar', 'Solar')) loop = asyncio.get_event_loop() loop.create_task(poll_sensors())