diff --git a/.gitignore b/.gitignore index 9e3a6a6..a2a970c 100644 --- a/.gitignore +++ b/.gitignore @@ -658,4 +658,5 @@ web/web/static/bundles/* # Certs files Edge Devices edge_devices/certs +edge_devices/config.py .idea/ diff --git a/edge_devices/egauge_local_api.py b/edge_devices/egauge_local_api.py new file mode 100644 index 0000000..317c705 --- /dev/null +++ b/edge_devices/egauge_local_api.py @@ -0,0 +1,114 @@ + +import datetime +from xml.etree import ElementTree as ET +import requests +from requests.auth import HTTPDigestAuth +import time +import json +import xmltodict +import config + +''' +2 - Implement egauge data pull with local rest api. Endpoints are: +egauge house B: http://198.129.116.113/cgi-bin/egauge?ins&tot +egauge house A: http://198.129.119.226/cgi-bin/egauge?ins&tot +''' + + +class EgaugeInterface(): + + def __init__(self, mode=None, endpoint=None, username=None, password=None, t_sample=3): + # Initializing credentials + self.endpoint = endpoint + self.mode = mode + self.username = username + self.password = password + + # Initializing parameters + self.t_sample = t_sample + # self.keys = ["A.Battery", "A.SubPanel", "A.GridPower", "A.Solar", "A.EV", "ts"] + self.keys = ["tess.Transformer"] + + # Function to process data from e-gauge and convert to useful power values + def processing_egauge_data(self): + if self.endpoint == None: + print('enpoint is None') + return 'Error egauge: endpoint is none' + self.url = 'http://' + self.endpoint + '/cgi-bin/egauge?ins&tot' + power_values = dict.fromkeys(self.keys, None) + try: + if self.mode == 'web': + resp = requests.get(self.url, auth=HTTPDigestAuth(self.username, self.password)) + elif self.mode == 'ip': + resp = requests.get(self.url) + else: + print('mode type not acceptable') + return 'Error egauge: mode type not acceptable' + + resp.raise_for_status() + data_ini = self.get_egauge_registers(resp) + + except requests.exceptions.HTTPError as err: + print(err) + return json.dumps(power_values) + + time.sleep(self.t_sample) + + try: + if self.mode == 'web': + resp = requests.get(self.url, auth=HTTPDigestAuth(self.username, self.password)) + resp.raise_for_status() + elif self.mode == 'ip': + resp = requests.get(self.url) + resp.raise_for_status() + else: + print('mode type not acceptable') + return 'Error egauge: mode type not acceptable' + + data_end = self.get_egauge_registers(resp) + + except requests.exceptions.HTTPError as err: + print(err) + return json.dumps(power_values) + + ts_delta = data_end['ts'] - data_ini['ts'] + try: + for i in power_values: + if i == 'ts': + power_values['ts'] = datetime.datetime.fromtimestamp(int(data_end['ts'])).strftime('%Y-%m-%d %H:%M:%S') + else: + power_values[i] = round(((data_end[i] - data_ini[i]) / ts_delta) / 1000, 3) + + json_dict = json.dumps(power_values) + # print('Data Dict: ', json_dict) + return power_values + except Exception as e: + print('Error retrieving data from E-Gauge API: ', e) + return ('Error egauge: mode type not acceptable',e) + + + def get_egauge_registers(self, response): + power_values = dict.fromkeys(self.keys, None) + data = xmltodict.parse(response.text) + data_json = json.loads(json.dumps(data)) + keys_set = self.keys + if data_json['data']['ts'] != None: + power_values['ts'] = int(data_json['data']['ts']) + egauge_vals = data_json['data']['r'] + + for i in egauge_vals: + if i['@n'] in keys_set: + power_values[i['@n']] = int(i['v']) + + return power_values + + +if __name__ == "__main__": + user = config.egauge_user + password = config.egauge_pw + url240 = 'https://egauge47573.egaug.es/cgi-bin/egauge' + mode = 'web' + endpoint = 'egauge47570.egaug.es/' + retval = EgaugeInterface(mode=mode, endpoint=endpoint, username=user, password=password).processing_egauge_data() + print(retval) + diff --git a/edge_devices/tess_pubsub.py b/edge_devices/tess_pubsub.py index 711734b..d0364fc 100644 --- a/edge_devices/tess_pubsub.py +++ b/edge_devices/tess_pubsub.py @@ -6,6 +6,7 @@ import sonnen_comms as sonnen import requests from datetime import datetime +import egauge_local_api as eg # Expected payload to be received by edge devices when subscribing to a topic: # { @@ -74,6 +75,7 @@ def request(): PATH_TO_KEY = config.PATH_TO_KEY PATH_TO_ROOT = config.PATH_TO_ROOT TOPIC_PUBLISH = config.TOPIC_PUBLISH +TOPIC_PUBLISH_TX = config.TOPIC_PUBLISH_TX TOPIC_SUBSCRIBE = config.TOPIC_SUBSCRIBE #TODO: Include all Heilas IP and configure based on the meter_id in the meter_intervals table @@ -81,6 +83,9 @@ def request(): # Edge Device Info url = config.URL +# Egauge Info +mode = 'ip' +ip = config.egauge_ip # AWS IoT client config myAWSIoTMQTTClient = AWSIoTPyMQTT.AWSIoTMQTTClient(CLIENT_ID) @@ -94,10 +99,7 @@ def request(): # myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec # myAWSIoTMQTTClient.configureMQTTOperationTimeout(5) # 5 sec -mqtt_connect = False -while not (mqtt_connect): - mqtt_connect = myAWSIoTMQTTClient.connect() - print('mqq_connect: ', mqtt_connect) +myAWSIoTMQTTClient.connect() # subscribing to topic subscribe(myAWSIoTMQTTClient, TOPIC_SUBSCRIBE) @@ -105,16 +107,10 @@ def request(): # Initializing battery object sonnen_obj = sonnen.SonnenApiInterface() -# Testing purpose: -import datetime - - # publishing to topic is_submitted = False next_5min = (int(datetime.now().minute / 5) * 5) + 5 while True: - - print('next5min: ', next_5min) if datetime.now().minute == next_5min: is_submitted = False print('submitted is False') @@ -126,6 +122,8 @@ def request(): if not is_submitted: print('Call publish method and submitted is True now') is_submitted = True + time_start = (int(t.time()/300)+1)*300 + time_end = (int(t.time()/300)+2)*300 try: # Testing connection retval = requests.get('https://www.google.com/').status_code @@ -136,15 +134,15 @@ def request(): # {'resource': 'ev', 'payload': None}] # print('all payload done! ') ### DONE ### - payload = hc.heila_update(url=url) if payload == None: tessPV_payload = None else: pv_info = payload["sunnyboy_inverter.calc_ac_power_kw"] pv_power = pv_info['value'] - pv_time = datetime.fromtimestamp(pv_info['timestamp'] / 1000) - tessPV_payload = {'rate_id': 1, 'meter_id': 1, 'start_time': str(pv_time), 'end_time': str(datetime.fromtimestamp(int(t.time())+60)), + # pv_time = datetime.fromtimestamp(pv_info['timestamp'] / 1000) + pv_time = datetime.fromtimestamp(time_start) # This is a temporary fix until figure out why heila is giving a time 5min ahead of current time + tessPV_payload = {'rate_id': 1, 'meter_id': 1, 'start_time': str(pv_time), 'end_time': str(datetime.fromtimestamp(time_end)), 'e': pv_power / 12, 'qmtp': pv_power, 'p_bid': 0, 'q_bid': 0, 'is_bid': 1, 'mode_dispatch': 0, 'mode_market': 0} @@ -157,24 +155,17 @@ def request(): print('Transfer control back to HEILA... Implement function - TBD') # to disable control from the power market, you need to send a POST request to the API endpoint /api/unsync . # To give back control, send a POST request to the API endpoint /api/sync - t.sleep(10) - - try: - # Testing connection - retval = requests.get('https://www.google.com/').status_code - print('status code: ', retval) - payload = [{'resource': 'solar', 'payload': hc.heila_update(url=url)}, - {'resource': 'battery', 'payload': sonnen_obj.get_batteries_status_json(serial=config.SONNEN_BATT)}, - {'resource': 'ev', 'payload': None}] - # print('all payload done! ') - publish(myAWSIoTMQTTClient, TOPIC_PUBLISH, payload, CLIENT_ID) - print('Published ', datetime.datetime.now()) - - except requests.exceptions.RequestException as e: - print('error: ', e) - print('Transfer control back to HEILA... Implement function - TBD') - # to disable control from the power market, you need to send a POST request to the API endpoint /api/unsync . - # To give back control, send a POST request to the API endpoint /api/sync - - t.sleep(30) # Need to define how often to provide info updates + payloadTransformer = eg.EgaugeInterface(mode=mode,endpoint=ip).processing_egauge_data() + if payloadTransformer == None: + tessTX_payload = None + else: + tx_power = -payloadTransformer["tess.Transformer"] + tx_time = datetime.fromtimestamp(time_start) # This is a temporary fix until figure out why heila is giving a time 5min ahead of current time + tessTX_payload = {'transformer_id':str(1),'q':str(tx_power), 'start_time': str(tx_time), 'end_time': str(datetime.fromtimestamp(time_end))} + + print('Transformer payload: ', tessTX_payload) + publish(myAWSIoTMQTTClient, TOPIC_PUBLISH_TX, tessTX_payload, 1) # deviceID = 1 -> see TODO on def publish(): to automate this part + # print(payload) + print('Published ', datetime.now()) + t.sleep(10)