diff --git a/emporia_proxy.py b/emporia_proxy.py new file mode 100644 index 0000000..65bbc65 --- /dev/null +++ b/emporia_proxy.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# +# Author: Jeremy Compostella +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +import os + +from datetime import datetime, timedelta +from multiprocessing.connection import Listener + +from pyemvue.enums import Scale + +from sensor import MyVue2 +from tools import debug, init + +def main(): + prefix = os.path.splitext(__file__)[0] + config = init(prefix + '.log') + + vue = MyVue2(config['Emporia']) + cache = {} + listener = Listener((config['EmporiaProxy']['host'], + int(config['EmporiaProxy']['port']))) + debug("... is now ready to run") + while True: + conn = listener.accept() + scale = conn.recv() + data = {} + if scale not in cache or datetime.now() > cache[scale]['expiration_time']: + try: + debug('Reading with scale %s' % scale) + data = vue.read(scale) + except: + debug('Failed to read from Emporia servers') + pass + if data: + cache[scale] = { 'data':data } + if scale == Scale.SECOND.value: + cache[scale]['expiration_time'] = \ + datetime.now() + timedelta(seconds=15) + else: + cache[scale]['expiration_time'] = \ + datetime.now().replace(second=0, microsecond=0) + \ + timedelta(minutes=1) + else: + data = cache[scale]['data'] + conn.send(data) + conn.close() + +if __name__ == "__main__": + main() diff --git a/emporia_proxy.service b/emporia_proxy.service new file mode 100644 index 0000000..fe45e37 --- /dev/null +++ b/emporia_proxy.service @@ -0,0 +1,17 @@ +[Unit] +Description=Emporia Proxy Service +DefaultDependencies=no +After=network.target + +[Service] +Type=simple +User=pi +Group=pi +ExecStart=/home/pi/emporia_proxy.sh +TimeoutStartSec=0 +RemainAfterExit=yes +Restart=on-failure +RestartSec=15s + +[Install] +WantedBy=default.target diff --git a/emporia_proxy.sh b/emporia_proxy.sh new file mode 100755 index 0000000..5c71651 --- /dev/null +++ b/emporia_proxy.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +cd /home/pi/ +python3 emporia_proxy.py + diff --git a/home.ini b/home.ini index f6ae9bc..21560ee 100644 --- a/home.ini +++ b/home.ini @@ -1,6 +1,6 @@ [general] sensor_db=sensor.csv -sensors=Emporia,OpenWeather,Ecobee,WirelessTag,CarData +sensors=EmporiaProxy,OpenWeather,Ecobee,WirelessTag,CarData producers=SolarPanels consumers=Pool,Aquanta,Ecobee,Range,Wallbox,Dryer utility=SRP @@ -12,6 +12,11 @@ token_file = keys.json map=net,water heater,dryer,air handler,EV,range,A/C,solar,pool class=MyVue2 +[EmporiaProxy] +class=EmporiaProxy +host=localhost +port=6001 + [SolarPanels] sensors=solar description=Solar Panels diff --git a/sensor.py b/sensor.py index 9732483..3691f9e 100644 --- a/sensor.py +++ b/sensor.py @@ -138,6 +138,20 @@ def read(self, scale=Scale.MINUTE.value): self.usage[self.mapping[i]] = usage[i].usage * factor[scale] return self.usage +class EmporiaProxy(Sensor): + def __init__(self, config): + self.address = (config['host'], int(config['port'])) + + def read(self, scale=Scale.MINUTE.value): + try: + proxy = Client(self.address) + proxy.send(scale) + data = proxy.recv() + proxy.close() + return data + except: + return {} + class MyWirelessTag(Sensor): expirationTime = None