From 956c7feab9851c563920f4acbb99958d15bd84f3 Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Thu, 5 Mar 2020 00:24:58 -0800 Subject: [PATCH] add support for fetching grid data... --- setup.py | 1 + solardash/__init__.py | 76 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 48b5eb2..cbb53b9 100644 --- a/setup.py +++ b/setup.py @@ -16,6 +16,7 @@ setup(name='solardash', install_requires=[ 'aiohttp', 'RainEagle @ git+https://www.funkthat.com/gitea/jmg/RainEagle.git', + 'mock', ], extras_require = { 'dev': [ 'coverage' ], diff --git a/solardash/__init__.py b/solardash/__init__.py index 7f5072b..1aa5384 100644 --- a/solardash/__init__.py +++ b/solardash/__init__.py @@ -27,16 +27,19 @@ import aiohttp import asyncio import concurrent import logging +import json +import mock import os import shutil +import solardash import tempfile import unittest import urllib -logging.basicConfig(level=logging.DEBUG) +#logging.basicConfig(level=logging.DEBUG) from aiohttp import web -from RainEagle.parse import LogDir as RELogDir, _cmaiter +from RainEagle.parse import LogDir as RELogDir, _cmaiter, MeterRead # https://twitter.com/encthenet/status/1220412987732787200?s=20 aiter = lambda x: x.__aiter__() @@ -106,10 +109,21 @@ class SolarDataWS(object): injecttask = asyncio.create_task(injector()) + #pending = [] async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT and msg.data == 'q': - break - else: + break + elif msg.type == aiohttp.WSMsgType.TEXT: + fun, data = msg.data.split(' ', 1) + try: + fun = getattr(self, 'wst_%s' % fun) + # XXX add tests to make this a task + #pending.append(asyncio.create_task(fun(ws, data))) + await fun(ws, data) + except Exception: # pragma: no cover + import traceback; traceback.print_exc() + print('failed to handle msg:', repr(msg)) + else: # pragma: no cover print('unknown msg:', repr(msg)) injecttask.cancel() @@ -118,6 +132,14 @@ class SolarDataWS(object): return ws + async def wst_win(self, ws, data): + start, stop = (int(x) / 1000 for x in data.split()) + + griddata = [ (x.meterts, x.load * 1000) for x in self._raineagle[start:stop] ] + + obj = dict(production=[], grid=griddata, consumption=[]) + await ws.send_str('windata %s' % json.dumps(obj)) + def get_routes(self): return [ web.get('/solar.ws', self.websocket_handler), @@ -341,3 +363,49 @@ class Test(unittest.TestCase): self.assertEqual(r[1].data, 'ng 1.0000') finally: await runner.cleanup() + + @async_test + @mock.patch('solardash.RELogDir.__getitem__') + async def test_winowdata(self, relogdir): + loop = asyncio.get_event_loop() + + runner = web.AppRunner(self._app) + await runner.setup() + try: + site = web.TCPSite(runner, 'localhost', self._webport) + await site.start() + + # Test the websocket + r = [] + + async with aiohttp.ClientSession() as session, \ + session.ws_connect(self.makeurl('solar.ws')) as ws: + startts = 128743 + endts = 1987343 + + meterdata = [ MeterRead(x * 2, x * 2 + 1, 'bogus', + x, 12, 'kW', x * 5, -x * 5, + 'kW') for x in (1, 2, 3, 4) ] + data = [ [x.meterts, x.load * 1000] for x in meterdata ] + relogdir.return_value = meterdata + windataresp = 'this is some response' + + # send the window command + await ws.send_str('win %s %s' % (startts, + endts)) + + async for msg in ws: + r.append(msg) + if len(r) == 1: + break + + # we get passed in miliseconds from JS, but + # we want seconds + relogdir.assert_called_with(slice(startts / + 1000, endts / 1000)) + self.assertEqual(r[0].type, aiohttp.WSMsgType.TEXT) + obj = json.loads(r[0].data.split(' ', 1)[1]) + self.assertEqual(obj, dict(production=[], + grid=data, consumption=[])) + finally: + await runner.cleanup()