Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -658,4 +658,5 @@ web/web/static/bundles/*

# Certs files Edge Devices
edge_devices/certs
edge_devices/config.py
.idea/
114 changes: 114 additions & 0 deletions edge_devices/egauge_local_api.py
Original file line number Diff line number Diff line change
@@ -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)

57 changes: 24 additions & 33 deletions edge_devices/tess_pubsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
# {
Expand Down Expand Up @@ -74,13 +75,17 @@ 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
resource_map = {'1':config.IP_ADDRESS}

# Edge Device Info
url = config.URL
# Egauge Info
mode = 'ip'
ip = config.egauge_ip

# AWS IoT client config
myAWSIoTMQTTClient = AWSIoTPyMQTT.AWSIoTMQTTClient(CLIENT_ID)
Expand All @@ -94,27 +99,18 @@ 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)

# 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')
Expand All @@ -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
Expand All @@ -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}
Expand All @@ -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)