Skip to content

Commit

Permalink
Migrate additional variables (scheduled charging) to TWCMaster, impro…
Browse files Browse the repository at this point in the history
…ve the HTTPControl user interface
  • Loading branch information
ngardiner committed Oct 17, 2019
1 parent 9c87f9a commit fc29e0f
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 51 deletions.
47 changes: 20 additions & 27 deletions TWCManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ def time_now():
".%f" if config['config']['displayMilliseconds'] else "")))

def load_settings():
global config, scheduledAmpsStartHour, scheduledAmpsEndHour, \
scheduledAmpsDaysBitmap, hourResumeTrackGreenEnergy, kWhDelivered, \
carapi, homeLat, homeLon
global config, scheduledAmpsDaysBitmap, kWhDelivered, carapi, homeLat, homeLon

try:
fh = open(config['config']['settingsPath'] + "/TWCManager.settings", 'r')
Expand All @@ -134,16 +132,16 @@ def load_settings():

m = re.search(r'^\s*scheduledAmpsStartHour\s*=\s*([-0-9.]+)', line, re.MULTILINE)
if(m):
scheduledAmpsStartHour = float(m.group(1))
master.setScheduledAmpsStartHour(float(m.group(1)))
if(config['config']['debugLevel'] >= 10):
print("load_settings: scheduledAmpsStartHour set to " + str(scheduledAmpsStartHour))
print("load_settings: scheduledAmpsStartHour set to " + str(m.group(1)))
continue

m = re.search(r'^\s*scheduledAmpsEndHour\s*=\s*([-0-9.]+)', line, re.MULTILINE)
if(m):
scheduledAmpsEndHour = float(m.group(1))
master.setScheduledAmpsEndHour(float(m.group(1)))
if(config['config']['debugLevel'] >= 10):
print("load_settings: scheduledAmpsEndHour set to " + str(scheduledAmpsEndHour))
print("load_settings: scheduledAmpsEndHour set to " + str(m.group(1)))
continue

m = re.search(r'^\s*scheduledAmpsDaysBitmap\s*=\s*([-0-9.]+)', line, re.MULTILINE)
Expand All @@ -155,9 +153,9 @@ def load_settings():

m = re.search(r'^\s*hourResumeTrackGreenEnergy\s*=\s*([-0-9.]+)', line, re.MULTILINE)
if(m):
hourResumeTrackGreenEnergy = float(m.group(1))
master.setHourResumeTrackGreenEnergy(float(m.group(1)))
if(config['config']['debugLevel'] >= 10):
print("load_settings: hourResumeTrackGreenEnergy set to " + str(hourResumeTrackGreenEnergy))
print("load_settings: hourResumeTrackGreenEnergy set to " + str(m.group(1)))
continue

m = re.search(r'^\s*kWhDelivered\s*=\s*([-0-9.]+)', line, re.MULTILINE)
Expand Down Expand Up @@ -210,17 +208,15 @@ def load_settings():
pass

def save_settings():
global config, scheduledAmpsStartHour, scheduledAmpsEndHour, \
scheduledAmpsDaysBitmap, hourResumeTrackGreenEnergy, kWhDelivered, \
carapi, homeLat, homeLon
global config, scheduledAmpsDaysBitmap, kWhDelivered, carapi, homeLat, homeLon

fh = open(config['config']['settingsPath'] + "/TWCManager.settings", 'w')
fh.write('nonScheduledAmpsMax=' + str(master.getNonScheduledAmpsMax()) +
'\nscheduledAmpsMax=' + str(master.getScheduledAmpsMax()) +
'\nscheduledAmpsStartHour=' + str(scheduledAmpsStartHour) +
'\nscheduledAmpsEndHour=' + str(scheduledAmpsEndHour) +
'\nscheduledAmpsStartHour=' + str(master.getScheduledAmpsStartHour()) +
'\nscheduledAmpsEndHour=' + str(master.getScheduledAmpsEndHour()) +
'\nscheduledAmpsDaysBitmap=' + str(scheduledAmpsDaysBitmap) +
'\nhourResumeTrackGreenEnergy=' + str(hourResumeTrackGreenEnergy) +
'\nhourResumeTrackGreenEnergy=' + str(master.getHourResumeTrackGreenEnergy()) +
'\nkWhDelivered=' + str(kWhDelivered) +
'\ncarApiBearerToken=' + str(carapi.getCarApiBearerToken()) +
'\ncarApiRefreshToken=' + str(carapi.getCarApiRefreshToken()) +
Expand Down Expand Up @@ -907,11 +903,8 @@ def check_green_energy():

idxSlaveToSendNextHeartbeat = 0

scheduledAmpsStartHour = -1
scheduledAmpsEndHour = -1
scheduledAmpsDaysBitmap = 0x7F

hourResumeTrackGreenEnergy = -1
kWhDelivered = 119
timeLastkWhDelivered = time.time()
timeLastkWhSaved = time.time()
Expand Down Expand Up @@ -1136,13 +1129,13 @@ def check_green_energy():
'`' + "%.2f" % (master.getChargeNowAmps()) +
'`' + str(master.getNonScheduledAmpsMax()) +
'`' + str(master.getScheduledAmpsMax()) +
'`' + "%02d:%02d" % (int(scheduledAmpsStartHour),
int((scheduledAmpsStartHour % 1) * 60)) +
'`' + "%02d:%02d" % (int(scheduledAmpsEndHour),
int((scheduledAmpsEndHour % 1) * 60)) +
'`' + "%02d:%02d" % (int(master.getScheduledAmpsStartHour()),
int((master.getScheduledAmpsStartHour() % 1) * 60)) +
'`' + "%02d:%02d" % (int(master.getScheduledAmpsEndHour()),
int((master.getScheduledAmpsEndHour() % 1) * 60)) +
'`' + str(scheduledAmpsDaysBitmap) +
'`' + "%02d:%02d" % (int(hourResumeTrackGreenEnergy),
int((hourResumeTrackGreenEnergy % 1) * 60)) +
'`' + "%02d:%02d" % (int(master.getHourResumeTrackGreenEnergy()),
int((master.getHourResumeTrackGreenEnergy() % 1) * 60)) +
# Send 1 if we need an email/password entered for car api, otherwise send 0
'`' + ('1' if needCarApiBearerToken else '0') +
'`' + str(master.countSlaveTWC())
Expand Down Expand Up @@ -1171,14 +1164,14 @@ def check_green_energy():
webMsg[17:len(webMsg)], re.MULTILINE)
if(m):
master.setScheduledAmpsMax(int(m.group(1)))
scheduledAmpsStartHour = int(m.group(2)) + (int(m.group(3)) / 60)
scheduledAmpsEndHour = int(m.group(4)) + (int(m.group(5)) / 60)
master.setScheduledAmpsStartHour(int(m.group(2)) + (int(m.group(3)) / 60))
master.setScheduledAmpsEndHour(int(m.group(4)) + (int(m.group(5)) / 60))
scheduledAmpsDaysBitmap = int(m.group(6))
save_settings()
elif(webMsg[0:30] == b'setResumeTrackGreenEnergyTime='):
m = re.search(b'([-0-9]+):([0-9]+)', webMsg[30:len(webMsg)], re.MULTILINE)
if(m):
hourResumeTrackGreenEnergy = int(m.group(1)) + (int(m.group(2)) / 60)
master.setHourResumeTrackGreenEnergy(int(m.group(1)) + (int(m.group(2)) / 60))
save_settings()
elif(webMsg[0:11] == b'sendTWCMsg='):
m = re.search(b'([0-9a-fA-F]+)', webMsg[11:len(webMsg)], re.MULTILINE)
Expand Down
98 changes: 80 additions & 18 deletions lib/TWCManager/Control/HTTPControl.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,36 @@ def do_css(self):
#vertical thead,#vertical tbody{
display:inline-block;
}
"""
page += "</style>"
return page

def do_navbar(self):
page = """
<p>&nbsp;</p>
<p>&nbsp;</p>
<nav class='navbar fixed-top navbar-dark bg-dark' role='navigation'>
<a class='navbar-brand' href='/'>TWCManager</a>
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home</a>
</li>
</ul>
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
</ul>
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<span class="navbar-text">v1.1.3</span>
</nav>"""
return page

def do_GET(self):
global master
url = urllib.parse.urlparse(self.path)
Expand All @@ -87,10 +113,13 @@ def do_GET(self):
# Send the html message
page = "<html><head>"
page += "<title>TWCManager</title>"
page += "<link rel='icon' type='image/png' href='favicon.png'>"
page += "<meta http-equiv='refresh' content='5' />"
page += "<meta name='viewport' content='width=device-width, initial-scale=1'>"
page += "<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>"
page += self.do_css()
page += "</head>"
page += "<body>"
page += self.do_navbar()
page += "<table border='0' padding='0' margin='0'><tr>"
page += "<td valign='top'>"

Expand All @@ -100,7 +129,9 @@ def do_GET(self):
page += self.show_status()
page += self.show_twcs()

page += "</table>"
page += "</body>"
page += "</table>"
page += "</html>"

self.wfile.write(page.encode("utf-8"))
Expand Down Expand Up @@ -188,7 +219,7 @@ def request_teslalogin(self):
page += "<p>"
page += "<table>"
page += "<tr><td>Tesla Account E-Mail:</td>"
page += "<td><input type='text' name='email' value='GETEMAILFROMCFG'></td></tr>"
page += "<td><input type='text' name='email' value=''></td></tr>"
page += "<tr><td>Password:</td>"
page += "<td><input type='password' name='password'></td></tr>"
page += "<tr><td><input type='submit' name='submit' value='Log In'></td>"
Expand All @@ -202,22 +233,53 @@ def request_teslalogin(self):
def show_status(self):
global master

page = "<table id='vertical' class='darkTable'>"
page += "<thead>"
page += "<tr><th>Amps to share across all TWCs:</th></tr>"
page += "<tr><th>Current Generation</th></tr>"
page += "<tr><th>Current Consumption</th></tr>"
page += "<tr><th>Current Charger Load</th></tr>"
page += "<tr><th>Number of Cars Charging</th></tr>"
page += "</thead>"
page += "<tbody>"
page += "<tr><td>" + str(master.getMaxAmpsToDivideAmongSlaves()) + "</td></tr>"
page += "<tr><td>" + str(master.getGeneration()) + "</td></tr>"
page += "<tr><td>" + str(master.getConsumption()) + "</td></tr>"
page += "<tr><td>" + str(master.getChargerLoad()) + "</td></tr>"
page += "<tr><td>" + str(master.num_cars_charging_now()) + "</td></tr>"
page += "</tbody>"
page += "</table>"
page = "<table width = '100%'><tr width = '100%'><td width='30%'>"
page += "<table class='table table-dark' width='50%'>"
page += "<tr><th>Amps to share across all TWCs:</th>"
page += "<td>" + str(master.getMaxAmpsToDivideAmongSlaves()) + "</td>"
page += "<td>amps</td></tr>"

page += "<tr><th>Current Generation</th>"
page += "<td>" + str(master.getGeneration()) + "</td>"
page += "<td>watts</td>"
genamps = 0
if (master.getGeneration()):
genamps = (master.getGeneration()/240)
page += "<td>" + str(genamps)+"</td><td>amps</td></tr>"

page += "<tr><th>Current Consumption</th>"
page += "<td>" + str(master.getConsumption()) + "</td>"
page += "<td>watts</td>"
conamps = 0
if (master.getConsumption()):
conamps = (master.getConsumption()/240)
page += "<td>" + str(conamps)+"</td><td>amps</td></tr>"

page += "<tr><th>Current Charger Load</th>"
page += "<td>" + str(master.getChargerLoad()) + "</td>"
page += "<td>watts</td>"
page += "</tr>"

page += "<tr><th>Number of Cars Charging</th>"
page += "<td>" + str(master.num_cars_charging_now()) + "</td>"
page += "<td>cars</td></tr></table></td>"

page += "<td>"
page += "<table class='table table-dark' width='50%'>"
page += "<tr><th>Scheduled Charging Amps</th>"
page += "<td>" + str(master.getScheduledAmpsMax()) + "</td></tr>"

page += "<tr><th>Scheduled Charging Start Hour</th>"
page += "<td>" + str(master.getScheduledAmpsStartHour()) + "</td></tr>"

page += "<tr><th>Scheduled Charging End Hour</th>"
page += "<td>" + str(master.getScheduledAmpsEndHour()) + "</td>"
page += "</tr>"

page += "<tr><th>Resume Tracking Green Energy at</th>"
page += "<td>" + str(master.getHourResumeTrackGreenEnergy()) + "</td>"
page += "</tr>"
page += "</table></td></tr></table>"
return page

def show_twcs(self):
Expand Down
21 changes: 21 additions & 0 deletions lib/TWCManager/TWCMaster.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ class TWCMaster:
consumptionValues = {}
generationValues = {}
hassstatus = None
hourResumeTrackGreenEnergy = -1
masterTWCID = ''
maxAmpsToDivideAmongSlaves = 0
mqttstatus = None
nonScheduledAmpsMax = -1
overrideMasterHeartbeatData = b''
scheduledAmpsMax = -1
scheduledAmpsStartHour = -1
scheduledAmpsEndHour = -1
ser = None
slaveTWCs = {}
slaveTWCRoundRobin = []
Expand Down Expand Up @@ -89,6 +92,9 @@ def getBackgroundTasksLock(self):
def getChargeNowAmps(self):
return (self.chargeNowAmps)

def getHourResumeTrackGreenEnergy(self):
return self.hourResumeTrackGreenEnergy

def getMasterTWCID(self):
# This is called when TWCManager is in Slave mode, to track the
# master's TWCID
Expand All @@ -109,6 +115,12 @@ def getNonScheduledAmpsMax(self):
def getScheduledAmpsMax(self):
return self.scheduledAmpsMax

def getScheduledAmpsStartHour(self):
return self.scheduledAmpsStartHour

def getScheduledAmpsEndHour(self):
return self.scheduledAmpsEndHour

def getSlaveSign(self):
return self.slaveSign

Expand Down Expand Up @@ -452,6 +464,9 @@ def sethassstatus(self, hass):
# Stores the hassstatus object
self.hassstatus = hass

def setHourResumeTrackGreenEnergy(self, hour):
self.hourResumeTrackGreenEnergy = hour

def setMasterTWCID(self, twcid):
# This is called when TWCManager is in Slave mode, to track the
# master's TWCID
Expand Down Expand Up @@ -500,6 +515,12 @@ def setNonScheduledAmpsMax(self, amps):
def setScheduledAmpsMax(self, amps):
self.scheduledAmpsMax = amps

def setScheduledAmpsStartHour(self, hour):
self.scheduledAmpsStartHour = hour

def setScheduledAmpsEndHour(self, hour):
self.scheduledAmpsEndHour = hour

def setSpikeAmps(self, amps):
self.spikeAmpsToCancel6ALimit = amps

Expand Down
12 changes: 6 additions & 6 deletions lib/TWCManager/TWCSlave.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,17 +429,17 @@ def receive_slave_heartbeat(self, heartbeatData):
yesterday += 7

# Check if it's time to resume tracking green energy.
if(self.master.getNonScheduledAmpsMax() != -1 and hourResumeTrackGreenEnergy > -1
and hourResumeTrackGreenEnergy == hourNow):
if(self.master.getNonScheduledAmpsMax() != -1 and self.master.getHourResumeTrackGreenEnergy() > -1
and self.master.getHourResumeTrackGreenEnergy() == hourNow):
self.master.setNonScheduledAmpsMax(-1)
save_settings()

# Check if we're within the hours we must use scheduledAmpsMax instead
# of nonScheduledAmpsMax
blnUseScheduledAmps = 0
if(self.master.getScheduledAmpsMax() > 0 and scheduledAmpsStartHour > -1
and scheduledAmpsEndHour > -1 and scheduledAmpsDaysBitmap > 0):
if(scheduledAmpsStartHour > scheduledAmpsEndHour):
and self.master.getScheduledAmpsEndHour() > -1 and scheduledAmpsDaysBitmap > 0):
if(scheduledAmpsStartHour > self.master.getScheduledAmpsEndHour()):
# We have a time like 8am to 7am which we must interpret as the
# 23-hour period after 8am or before 7am. Since this case always
# crosses midnight, we only ensure that scheduledAmpsDaysBitmap
Expand All @@ -448,12 +448,12 @@ def receive_slave_heartbeat(self, heartbeatData):
# 7am, we apply scheduledAmpsMax from Monday at 8am to Monday at
# 11:59pm, and on Tuesday at 12am to Tuesday at 6:59am.
if((hourNow >= scheduledAmpsStartHour and (scheduledAmpsDaysBitmap & (1 << ltNow.tm_wday)))
or (hourNow < scheduledAmpsEndHour and (scheduledAmpsDaysBitmap & (1 << yesterday)))):
or (hourNow < self.master.getScheduledAmpsEndHour() and (scheduledAmpsDaysBitmap & (1 << yesterday)))):
blnUseScheduledAmps = 1
else:
# We have a time like 7am to 8am which we must interpret as the
# 1-hour period between 7am and 8am.
if(hourNow >= scheduledAmpsStartHour and hourNow < scheduledAmpsEndHour
if(hourNow >= scheduledAmpsStartHour and hourNow < self.master.getScheduledAmpsEndHour()
and (scheduledAmpsDaysBitmap & (1 << ltNow.tm_wday))):
blnUseScheduledAmps = 1

Expand Down

0 comments on commit fc29e0f

Please sign in to comment.