From e6547227db8131b59bb9c21238cbe12ab8bd5e2a Mon Sep 17 00:00:00 2001 From: juanjoqg Date: Wed, 3 Feb 2021 23:21:33 +0100 Subject: [PATCH 1/5] New feature that allows to limit the max power TWC will take from the Grid q --- TWCManager.py | 30 +++++++++++++ etc/twcmanager/config.json | 45 +++++++++++++++++++ .../Control/themes/Default/jsrefresh.html.j2 | 2 +- lib/TWCManager/TWCMaster.py | 38 ++++++++++++++++ setup.py | 4 +- svisorTWC.sh | 20 +++++++++ 6 files changed, 136 insertions(+), 3 deletions(-) create mode 100755 svisorTWC.sh diff --git a/TWCManager.py b/TWCManager.py index 63b76607..ba662dc8 100755 --- a/TWCManager.py +++ b/TWCManager.py @@ -97,6 +97,15 @@ debugLog(1, "Unable to find a configuration file.") sys.exit() + + + +######################################################################## +# Write the PID in order to let a supervisor restart it in case of crash +PIDTWCManager=open("/home/pi/TWCManager/TWCManager.pid","w") +PIDTWCManager.write(str(os.getpid())) +PIDTWCManager.close() + # All TWCs ship with a random two-byte TWCID. We default to using 0x7777 as our # fake TWC ID. There is a 1 in 64535 chance that this ID will match each real # TWC on the network, in which case you should pick a different random id below. @@ -237,6 +246,9 @@ def background_tasks_thread(master): requests.post(task["url"], json=body) elif task["cmd"] == "saveSettings": master.saveSettings() + elif task["cmd"] == "checkMaxPowerFromGrid": + check_max_power_from_grid() + except: master.debugLog( @@ -285,6 +297,24 @@ def check_green_energy(): master.setMaxAmpsToDivideAmongSlaves(master.getMaxAmpsToDivideGreenEnergy()) +def check_max_power_from_grid(): + global config, hass, master + + # Check solar panel generation using an API exposed by + # the HomeAssistant API. + # + # You may need to customize the sensor entity_id values + # to match those used in your environment. This is configured + # in the config section at the top of this file. + # + # Poll all loaded EMS modules for consumption and generation values + for module in master.getModulesByType("EMS"): + master.setConsumption(module["name"], module["ref"].getConsumption()) + master.setGeneration(module["name"], module["ref"].getGeneration()) + master.setMaxAmpsToDivideFromGrid(master.getMaxAmpsToDivideFromGrid()) + + + def update_statuses(): # Print a status update if we are on track green energy showing the diff --git a/etc/twcmanager/config.json b/etc/twcmanager/config.json index 755f7969..c4e87eb4 100644 --- a/etc/twcmanager/config.json +++ b/etc/twcmanager/config.json @@ -32,6 +32,32 @@ # wiringMaxAmpsPerTWC = 50 * 0.8 = 40 and wiringMaxAmpsAllTWCs = 40 + 40 = 80. "wiringMaxAmpsPerTWC": 6, + + # If you what to limit the power drawn from the Grid you need to set this + # maxAmpsAllowedFromGrid and extend the policy you what it to apply, i.e.: + # { "name": "Charge Now with Grid power limit", + # "match": [ + # "settings.chargeNowAmps", + # "settings.chargeNowTimeEnd", + # "settings.chargeNowTimeEnd", + # ], + # "condition": ["gt", "gt", "gt"], + # "value": [0, 0, "now"], + # "background_task": "checkMaxPowerFromGrid", + # "charge_amps": "settings.chargeNowAmps", + # "charge_limit": "config.chargeNowLimit"}, + + # { "name": "Scheduled Charging with Grid power limit", + # "match": [ "checkScheduledCharging()" ], + # "condition": [ "eq" ], + # "value": [ 1 ], + # "background_task": "checkMaxPowerFromGrid", + # "charge_amps": "settings.scheduledAmpsMax", + # "charge_limit": "config.scheduledLimit"}, + + "maxAmpsAllowedFromGrid": 15, + + # https://teslamotorsclub.com/tmc/threads/model-s-gen2-charger-efficiency-testing.78740/#post-1844789 # says you're using 10.85% more power (91.75/82.77=1.1085) charging at 5A vs 40A, # 2.48% more power at 10A vs 40A, and 1.9% more power at 20A vs 40A. This is @@ -258,6 +284,25 @@ # # They should primarily be used to abort charging when necessary. "emergency":[ + { "name": "Charge Now with Grid power limit", + "match": [ + "settings.chargeNowAmps", + "settings.chargeNowTimeEnd", + "settings.chargeNowTimeEnd", + ], + "condition": ["gt", "gt", "gt"], + "value": [0, 0, "now"], + "background_task": "checkMaxPowerFromGrid", + "charge_amps": "settings.chargeNowAmps", + "charge_limit": "config.chargeNowLimit"}, + + { "name": "Scheduled Charging with Grid power limit", + "match": [ "checkScheduledCharging()" ], + "condition": [ "eq" ], + "value": [ 1 ], + "background_task": "checkMaxPowerFromGrid", + "charge_amps": "settings.scheduledAmpsMax", + "charge_limit": "config.scheduledLimit"}, ], # Rules in the before section here are evaluated after the Charge Now rule "before":[ diff --git a/lib/TWCManager/Control/themes/Default/jsrefresh.html.j2 b/lib/TWCManager/Control/themes/Default/jsrefresh.html.j2 index eaa93f10..2523259e 100644 --- a/lib/TWCManager/Control/themes/Default/jsrefresh.html.j2 +++ b/lib/TWCManager/Control/themes/Default/jsrefresh.html.j2 @@ -20,7 +20,7 @@ $(document).ready(function() { } // Change the state of the Charge Now button based on Charge Policy - if (json["currentPolicy"] == "Charge Now") { + if (json["currentPolicy"] == "Charge Now" || json["currentPolicy"] == "Charge Now with Grid power limit") { document.getElementById("start_chargenow").value = "Update Charge Now"; document.getElementById("cancel_chargenow").disabled = false; } else { diff --git a/lib/TWCManager/TWCMaster.py b/lib/TWCManager/TWCMaster.py index a2c7cbb0..7f167c9a 100644 --- a/lib/TWCManager/TWCMaster.py +++ b/lib/TWCManager/TWCMaster.py @@ -32,6 +32,7 @@ class TWCMaster: lastTWCResponseMsg = None masterTWCID = "" maxAmpsToDivideAmongSlaves = 0 + maxAmpsToDivideFromGrid = 0 modules = {} nextHistorySnap = 0 overrideMasterHeartbeatData = b"" @@ -532,6 +533,29 @@ def getMaxAmpsToDivideGreenEnergy(self): amps = amps / self.getRealPowerFactor(amps) return round(amps, 2) + def getMaxAmpsToDivideFromGrid(self): + # Calculate our current generation and consumption in watts + generationW = float(self.getGeneration()) + consumptionW = float(self.getConsumption()) + + currentOffer = min( + self.getTotalAmpsInUse(), + self.getMaxAmpsToDivideAmongSlaves(), + ) + + # Calculate what we should max offer to align with max grid energy + amps = self.config["config"]["maxAmpsAllowedFromGrid"] + \ + self.convertWattsToAmps(generationW - consumptionW) + \ + currentOffer + + amps = amps / self.getRealPowerFactor(amps) + self.debugLog( + 10, "TWCMaster", "MaxAmpsToDivideFromGrid: +++++++++++++++: " + str(amps) + ) + + return round(amps, 2) + + def getNormalChargeLimit(self, ID): if "chargeLimits" in self.settings and str(ID) in self.settings["chargeLimits"]: result = self.settings["chargeLimits"][str(ID)] @@ -1167,6 +1191,15 @@ def setMaxAmpsToDivideAmongSlaves(self, amps): ) amps = self.config["config"]["wiringMaxAmpsAllTWCs"] + + if amps > self.maxAmpsToDivideFromGrid: + # Never tell the slaves to draw more amps from grid than allowed + amps = self.maxAmpsToDivideFromGrid + self.debugLog( + 10, "TWCMaster","maxAmpsToDivideAmongSlaves limited to not draw more power from the grid than allowed: " + str(amps) + ) + + self.maxAmpsToDivideAmongSlaves = amps self.releaseBackgroundTasksLock() @@ -1175,6 +1208,11 @@ def setMaxAmpsToDivideAmongSlaves(self, amps): # to console / MQTT / etc self.queue_background_task({"cmd": "updateStatus"}) + def setMaxAmpsToDivideFromGrid(self, amps): + # This is called when check_max_power_from_grid is run + # It stablished how much power we allow getting from the grid + self.maxAmpsToDivideFromGrid = amps + def setNonScheduledAmpsMax(self, amps): self.settings["nonScheduledAmpsMax"] = amps diff --git a/setup.py b/setup.py index 6170b58d..074e2904 100755 --- a/setup.py +++ b/setup.py @@ -1,12 +1,12 @@ #!/usr/bin/python3 -from setuptools import setup, find_namespace_packages +from setuptools import setup, find_packages setup( name="TWCManager", version="1.2.1", package_dir={"": "lib"}, - packages=find_namespace_packages(where="lib"), + packages=find_packages(where="lib"), # Dependencies install_requires=[ "commentjson>=0.8.3", diff --git a/svisorTWC.sh b/svisorTWC.sh new file mode 100755 index 00000000..ca28ec44 --- /dev/null +++ b/svisorTWC.sh @@ -0,0 +1,20 @@ +PROGRAM=/usr/bin/python3.5 +PIDFILE=/home/pi/TWCManager/TWCManager.pid + +while true +do + +if [ -f $PIDFILE ]; then + read PID <$PIDFILE + echo $PID + if [ -d /proc/$PID ] && [ "$(readlink -f /proc/$PID/exe)" = "$PROGRAM" ]; then + echo "done." + else + echo "PID not found, Starting..." + screen -dm -S TWCManager /home/pi/TWCManager/TWCManager.py + fi +fi +sleep 30 +done + + From 0825b6d18ef0fba1957fa4b575a913c8266ad98d Mon Sep 17 00:00:00 2001 From: juanjoqg Date: Thu, 4 Feb 2021 11:37:57 +0100 Subject: [PATCH 2/5] Avoid using a hardcode path for the PID file, take the path from config.json --- TWCManager.py | 3 ++- svisorTWC.sh | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/TWCManager.py b/TWCManager.py index ba662dc8..37526247 100755 --- a/TWCManager.py +++ b/TWCManager.py @@ -102,7 +102,8 @@ ######################################################################## # Write the PID in order to let a supervisor restart it in case of crash -PIDTWCManager=open("/home/pi/TWCManager/TWCManager.pid","w") +PIDfile=config["config"]["settingsPath"] + "/TWCManager.pid" +PIDTWCManager=open(PIDfile,"w") PIDTWCManager.write(str(os.getpid())) PIDTWCManager.close() diff --git a/svisorTWC.sh b/svisorTWC.sh index ca28ec44..dba0c589 100755 --- a/svisorTWC.sh +++ b/svisorTWC.sh @@ -1,5 +1,6 @@ PROGRAM=/usr/bin/python3.5 -PIDFILE=/home/pi/TWCManager/TWCManager.pid +PIDFILE=/etc/twcmanager/TWCManager.pid +TWCMANAGER_PATH=/home/pi/TWCManager while true do @@ -11,8 +12,11 @@ if [ -f $PIDFILE ]; then echo "done." else echo "PID not found, Starting..." - screen -dm -S TWCManager /home/pi/TWCManager/TWCManager.py + screen -dm -S TWCManager $TWCMANAGER_PATH/TWCManager.py fi +else + echo "PID file not found "; echo $PIDFILE; echo ", Starting..." + screen -dm -S TWCManager $TWCMANAGER_PATH/TWCManager.py fi sleep 30 done From 4e781c0ee8c34abffec2d11cedd1de2fddff3643 Mon Sep 17 00:00:00 2001 From: juanjoqg Date: Thu, 4 Feb 2021 11:49:08 +0100 Subject: [PATCH 3/5] Rollback to the namespace remove --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 074e2904..6170b58d 100755 --- a/setup.py +++ b/setup.py @@ -1,12 +1,12 @@ #!/usr/bin/python3 -from setuptools import setup, find_packages +from setuptools import setup, find_namespace_packages setup( name="TWCManager", version="1.2.1", package_dir={"": "lib"}, - packages=find_packages(where="lib"), + packages=find_namespace_packages(where="lib"), # Dependencies install_requires=[ "commentjson>=0.8.3", From 5aec8ff0753ba22ce1e341b283c5cf3185e325f4 Mon Sep 17 00:00:00 2001 From: juanjoqg Date: Fri, 12 Feb 2021 13:52:48 +0100 Subject: [PATCH 4/5] Bug fix in the limit amps from the grid integration with track green energy --- lib/TWCManager/TWCMaster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/TWCManager/TWCMaster.py b/lib/TWCManager/TWCMaster.py index 7f167c9a..1ccf2dc9 100644 --- a/lib/TWCManager/TWCMaster.py +++ b/lib/TWCManager/TWCMaster.py @@ -1192,7 +1192,7 @@ def setMaxAmpsToDivideAmongSlaves(self, amps): amps = self.config["config"]["wiringMaxAmpsAllTWCs"] - if amps > self.maxAmpsToDivideFromGrid: + if not self.getModuleByName("Policy").policyIsGreen() and amps > self.maxAmpsToDivideFromGrid: # Never tell the slaves to draw more amps from grid than allowed amps = self.maxAmpsToDivideFromGrid self.debugLog( From 86667687aa37416266240472580351bb6b4e02da Mon Sep 17 00:00:00 2001 From: juanjoqg Date: Sat, 13 Feb 2021 20:52:57 +0100 Subject: [PATCH 5/5] Change to ensure it just limit the amps from the grid when the right policy is enable --- lib/TWCManager/TWCMaster.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/TWCManager/TWCMaster.py b/lib/TWCManager/TWCMaster.py index 1ccf2dc9..41b46a4b 100644 --- a/lib/TWCManager/TWCMaster.py +++ b/lib/TWCManager/TWCMaster.py @@ -1191,8 +1191,10 @@ def setMaxAmpsToDivideAmongSlaves(self, amps): ) amps = self.config["config"]["wiringMaxAmpsAllTWCs"] - - if not self.getModuleByName("Policy").policyIsGreen() and amps > self.maxAmpsToDivideFromGrid: + activePolicy=str(self.getModuleByName("Policy").active_policy) + if (activePolicy== "Charge Now with Grid power limit" or \ + activePolicy== "Scheduled Charging with Grid power limit") and \ + amps > self.maxAmpsToDivideFromGrid: # Never tell the slaves to draw more amps from grid than allowed amps = self.maxAmpsToDivideFromGrid self.debugLog(