+ {% autoescape false %}
+ {% endautoescape %}
+{% endblock %}
diff --git a/apps/predbat/templates/plan.html b/apps/predbat/templates/plan.html
new file mode 100644
index 000000000..d3a5e4865
--- /dev/null
+++ b/apps/predbat/templates/plan.html
@@ -0,0 +1,11 @@
+{% extends "layout.html" %}
+
+{% block title %}Home{% endblock %}
+
+
+{% block content %}
+Plan
+{% autoescape false %}
+{{ plan_html }}
+{% endautoescape %}
+{% endblock %}
diff --git a/apps/predbat/web.py b/apps/predbat/web.py
index 980ed327a..3670e85b7 100644
--- a/apps/predbat/web.py
+++ b/apps/predbat/web.py
@@ -9,6 +9,7 @@
from config import CONFIG_ITEMS
from utils import calc_percent_limit
from config import TIME_FORMAT, TIME_FORMAT_SECONDS
+from environment import is_jinja2_installed
class WebInterface:
@@ -16,10 +17,18 @@ def __init__(self, base) -> None:
self.abort = False
self.base = base
self.log = base.log
- self.default_page = "./dash"
self.pv_power_hist = {}
self.pv_forecast_hist = {}
+ # Set up Jinja2 templating (as long as installed)
+ if is_jinja2_installed():
+ from jinja2 import Environment, FileSystemLoader, select_autoescape
+
+ self.template_env = Environment(loader=FileSystemLoader("templates"), autoescape=select_autoescape())
+
+ # Disable autoescaping for the HTML plan
+ self.template_env.filters["raw"] = lambda value: value
+
def history_attribute(self, history, state_key="state", last_updated_key="last_updated", scale=1.0):
results = {}
if history:
@@ -60,16 +69,24 @@ def history_update(self):
async def start(self):
# Start the web server on port 5052
- app = web.Application()
- app.router.add_get("/", self.html_index)
- app.router.add_get("/plan", self.html_plan)
- app.router.add_get("/log", self.html_log)
- app.router.add_get("/menu", self.html_menu)
- app.router.add_get("/apps", self.html_apps)
- app.router.add_get("/charts", self.html_charts)
- app.router.add_get("/config", self.html_config)
- app.router.add_post("/config", self.html_config_post)
- app.router.add_get("/dash", self.html_dash)
+ # TODO: Turn off debugging
+ app = web.Application(debug=True)
+
+ # Check if required dependencies are present
+ if is_jinja2_installed():
+ app.router.add_get("/", self.html_dash)
+ app.router.add_get("/plan", self.html_plan)
+ app.router.add_get("/log", self.html_log)
+ app.router.add_get("/menu", self.html_menu)
+ app.router.add_get("/apps", self.html_apps)
+ app.router.add_get("/charts", self.html_charts)
+ app.router.add_get("/config", self.html_config)
+ app.router.add_post("/config", self.html_config_post)
+ app.router.add_get("/docs", self.html_docs)
+ else:
+ # Display the missing dependencies/update message
+ app.router.add_get("/", self.update_message)
+
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "0.0.0.0", 5052)
@@ -141,82 +158,6 @@ def get_status_html(self, level, status):
return text
- def get_header(self, title, refresh=0):
- """
- Return the HTML header for a page
- """
- text = "Predbat Web Interface"
-
- text += """
-
-
-"
-
- if refresh:
- text += ''.format(refresh)
- text += "\n"
- return text
-
def get_entity_detailedForecast(self, entity, subitem="pv_estimate"):
results = {}
detailedForecast = self.base.dashboard_values.get(entity, {}).get("attributes", {}).get("detailedForecast", {})
@@ -374,11 +315,15 @@ async def html_plan(self, request):
"""
Return the Predbat plan as an HTML page
"""
- self.default_page = "./plan"
- html_plan = self.base.html_plan
- text = self.get_header("Predbat Plan", refresh=60)
- text += "{}\n".format(html_plan)
- return web.Response(content_type="text/html", text=text)
+ template = self.template_env.get_template("plan.html")
+
+ context = {
+ "plan_html": self.base.html_plan,
+ "refresh": 60,
+ }
+
+ rendered_template = template.render(context)
+ return web.Response(text=rendered_template, content_type="text/html")
async def html_log(self, request):
"""
@@ -386,7 +331,6 @@ async def html_log(self, request):
"""
logfile = "predbat.log"
logdata = ""
- self.default_page = "./log"
if os.path.exists(logfile):
with open(logfile, "r") as f:
logdata = f.read()
@@ -397,31 +341,18 @@ async def html_log(self, request):
warnings = False
if "errors" in args:
errors = True
- self.default_page = "./log?errors"
if "warnings" in args:
warnings = True
- self.default_page = "./log?warnings"
loglines = logdata.split("\n")
- text = self.get_header("Predbat Log", refresh=10)
- text += ""
-
- if errors:
- text += "
Logfile (errors)
\n"
- elif warnings:
- text += "
Logfile (Warnings)
\n"
- else:
- text += "
Logfile (All)
\n"
- text += '- All '
- text += 'Warnings '
- text += 'Errors \n'
-
- text += "
\n"
+ text = ""
+ text = "
\n"
total_lines = len(loglines)
count_lines = 0
lineno = total_lines - 1
+ line_data = []
while count_lines < 1024 and lineno >= 0:
line = loglines[lineno]
line_lower = line.lower()
@@ -430,28 +361,34 @@ async def html_log(self, request):
start_line = line[0:27]
rest_line = line[27:]
- if "error" in line_lower:
- text += "
{}
{} {}
\n".format(lineno, start_line, rest_line)
- count_lines += 1
- continue
- elif (not errors) and ("warn" in line_lower):
- text += "
{}
{} {}
\n".format(lineno, start_line, rest_line)
+ if "error" in line_lower or ((not errors) and ("warn" in line_lower)) or (line and (not errors) and (not warnings)):
+ line_data.append(
+ {
+ "line_no": lineno,
+ "start_line": start_line,
+ "rest_line": rest_line,
+ }
+ )
count_lines += 1
- continue
- if line and (not errors) and (not warnings):
- text += "