feat: Add P2 Pro scale sensor and secure history sharing
This commit is contained in:
52
main.py
52
main.py
@@ -23,6 +23,7 @@ import aiomqtt
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import pytz
|
import pytz
|
||||||
TIMEZONE = pytz.timezone('America/Edmonton')
|
TIMEZONE = pytz.timezone('America/Edmonton')
|
||||||
|
import hashlib
|
||||||
|
|
||||||
app = web.Application()
|
app = web.Application()
|
||||||
http_session = None
|
http_session = None
|
||||||
@@ -104,6 +105,13 @@ class Sensor():
|
|||||||
|
|
||||||
return str(before) != str(after)
|
return str(before) != str(after)
|
||||||
|
|
||||||
|
def check_cooldown(self):
|
||||||
|
if self.last_update and self.skip_cooldown and time.time() - self.last_update < self.skip_cooldown:
|
||||||
|
# ignore data point
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def log(self):
|
def log(self):
|
||||||
if not self.value:
|
if not self.value:
|
||||||
return
|
return
|
||||||
@@ -112,7 +120,7 @@ class Sensor():
|
|||||||
logging.debug('Skipping writing %s, data hasn\'t changed', self)
|
logging.debug('Skipping writing %s, data hasn\'t changed', self)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.last_update and self.skip_cooldown and time.time() - self.last_update < self.skip_cooldown:
|
if self.check_cooldown():
|
||||||
logging.debug('Skipping writing %s, cooldown limit', self)
|
logging.debug('Skipping writing %s, cooldown limit', self)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -261,6 +269,23 @@ class SoilSensor(Sensor):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class P2ProScaleSensor(Sensor):
|
||||||
|
type_ = 'scale'
|
||||||
|
skip_cooldown = 60.0 * 60 * 18 # 18 hours
|
||||||
|
|
||||||
|
def check_cooldown(self):
|
||||||
|
if 'weight' not in self.value:
|
||||||
|
return False
|
||||||
|
|
||||||
|
skip = super().check_cooldown()
|
||||||
|
|
||||||
|
if skip:
|
||||||
|
controller_message('Cooldown skipping scale weight: ' + str(self.value['weight']))
|
||||||
|
|
||||||
|
return skip
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SolarSensor(Sensor):
|
class SolarSensor(Sensor):
|
||||||
type_ = 'solar'
|
type_ = 'solar'
|
||||||
|
|
||||||
@@ -326,7 +351,7 @@ async def poll_sensors():
|
|||||||
await sensor.poll()
|
await sensor.poll()
|
||||||
sensor.check_update()
|
sensor.check_update()
|
||||||
|
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
async def process_data(data):
|
async def process_data(data):
|
||||||
sensor = sensors.get(data['id'])
|
sensor = sensors.get(data['id'])
|
||||||
@@ -388,6 +413,11 @@ async def owntracks(request):
|
|||||||
|
|
||||||
return web.Response()
|
return web.Response()
|
||||||
|
|
||||||
|
|
||||||
|
def share_sha256(measurement, share_start, share_end, api_key):
|
||||||
|
s = f'{measurement}-{share_start}-{share_end}-{api_key}'
|
||||||
|
return hashlib.sha256(s.encode()).hexdigest()
|
||||||
|
|
||||||
async def history(request):
|
async def history(request):
|
||||||
api_key = request.rel_url.query.get('api_key', '')
|
api_key = request.rel_url.query.get('api_key', '')
|
||||||
authed = api_key == settings.SENSORS_API_KEY
|
authed = api_key == settings.SENSORS_API_KEY
|
||||||
@@ -395,6 +425,14 @@ 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')
|
||||||
|
|
||||||
|
share_start = request.rel_url.query.get('shareStart', '')
|
||||||
|
share_end = request.rel_url.query.get('shareEnd', '')
|
||||||
|
share_sig = request.rel_url.query.get('shareSig', '')
|
||||||
|
|
||||||
|
share_authed = share_sig == share_sha256(measurement, share_start, share_end, settings.SENSORS_API_KEY)
|
||||||
|
authed = authed or share_authed
|
||||||
|
|
||||||
|
|
||||||
if not authed and measurement in ['owntracks', 'sleep']:
|
if not authed and measurement in ['owntracks', 'sleep']:
|
||||||
return web.json_response([])
|
return web.json_response([])
|
||||||
|
|
||||||
@@ -443,6 +481,13 @@ async def history(request):
|
|||||||
start = int(start.timestamp())
|
start = int(start.timestamp())
|
||||||
end = int(end.timestamp())
|
end = int(end.timestamp())
|
||||||
|
|
||||||
|
if share_authed:
|
||||||
|
if start < int(share_start):
|
||||||
|
start = int(share_start)
|
||||||
|
if end > int(share_end):
|
||||||
|
end = int(share_end)
|
||||||
|
|
||||||
|
|
||||||
if measurement == 'temperature':
|
if measurement == 'temperature':
|
||||||
client = sensors_client
|
client = sensors_client
|
||||||
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)
|
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)
|
||||||
@@ -553,6 +598,9 @@ if __name__ == '__main__':
|
|||||||
sensors.add(SleepSensor('sleep1', 'Bedroom'))
|
sensors.add(SleepSensor('sleep1', 'Bedroom'))
|
||||||
sensors.add(SolarSensor('solar', 'Solar'))
|
sensors.add(SolarSensor('solar', 'Solar'))
|
||||||
sensors.add(SoilSensor('soil1', 'Dumb Cane'))
|
sensors.add(SoilSensor('soil1', 'Dumb Cane'))
|
||||||
|
sensors.add(SoilSensor('soil2', 'Kitchen Pothos'))
|
||||||
|
sensors.add(SoilSensor('soil3', 'Dracaena'))
|
||||||
|
sensors.add(P2ProScaleSensor('scale1', 'Master Bathroom'))
|
||||||
|
|
||||||
sensors.add(QotMotionSensor('qot_dc3c', 'Bedroom'))
|
sensors.add(QotMotionSensor('qot_dc3c', 'Bedroom'))
|
||||||
sensors.add(QotMotionSensor('qot_88c3', 'Lower Stairs Hi'))
|
sensors.add(QotMotionSensor('qot_88c3', 'Lower Stairs Hi'))
|
||||||
|
|||||||
Reference in New Issue
Block a user