Skip to content

Web UI improvements, Axle update sensor every minute#3333

Merged
springfall2008 merged 6 commits intomainfrom
fixes5
Feb 8, 2026
Merged

Web UI improvements, Axle update sensor every minute#3333
springfall2008 merged 6 commits intomainfrom
fixes5

Conversation

@springfall2008
Copy link
Owner

No description provided.

@springfall2008 springfall2008 marked this pull request as ready for review February 8, 2026 15:54
Copilot AI review requested due to automatic review settings February 8, 2026 15:54
@springfall2008 springfall2008 changed the title Web UI improvements Web UI improvements, Axle update sensor every minute Feb 8, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances Predbat’s built-in web UI by improving the Apps.yaml editor experience (detecting external file changes), adding a new “Savings” chart, and making the Components page easier to scan/filter.

Changes:

  • Add Apps.yaml external-change detection via checksum polling and a new /apps_editor_checksum endpoint.
  • Add a new “Savings” chart and simplify charts menu active-state handling.
  • Improve Components page status summary/sorting and add a “show disabled components” toggle; expand web interface endpoint testing.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
apps/predbat/web_helper.py Adds JS polling logic to detect external Apps.yaml changes and warn/auto-reload.
apps/predbat/web.py Adds checksum endpoint, fixes /api/state POST parsing, adds “Savings” chart, improves charts/components UI.
apps/predbat/tests/test_web_if.py Expands web interface tests to hit more endpoints and runs in a temp directory.
apps/predbat/ha.py Extends service name parsing to support both domain/service and domain.service.
apps/predbat/axle.py Publishes Axle event state every minute to keep “active” status current between API polls.

Comment on lines 116 to 123
# /api/ping returns 500 when Predbat isn't fully initialized (expected in test)
# Other endpoints may return 400 for missing optional params, which is fine
if res.status_code in [200, 400, 404, 500]:
accessed_endpoints.add(("GET", page))
else:
print("ERROR: Unexpected status from {} got {} value {}".format(address, res.status_code, res.text))
failed = 1

Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GET endpoint loop treats 404 (and 500) as acceptable for all GET endpoints, which can mask real regressions (missing routes should fail the test). It would be more effective to fail on 404 by default and only allow specific non-200 statuses for the few endpoints where they’re expected (e.g., /api/ping returning 500 in this test harness).

Copilot uses AI. Check for mistakes.
elif "." in service:
domain, service = service.split(".")
else:
domain = ""
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If service contains neither '/' nor '.', this now sets domain = "" and continues, which can hide invalid inputs (e.g. from the web API) and lead to confusing failures downstream. Prefer to reject/raise on invalid service formats (or return an explicit error) rather than calling HA/loopback with an empty domain.

Suggested change
domain = ""
# Invalid service format: must be 'domain/service' or 'domain.service'
raise ValueError("Invalid service format: {}. Expected 'domain/service' or 'domain.service'".format(service))

Copilot uses AI. Check for mistakes.
Comment on lines +3397 to +3404
Return the current checksum and content of apps.yaml for external change detection
"""
try:
apps_yaml_path = "apps.yaml"
with open(apps_yaml_path, "r") as f:
content = f.read()
checksum = hashlib.md5(content.encode("utf-8")).hexdigest()
return web.json_response({"checksum": checksum, "content": content})
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/apps_editor_checksum returns the full apps.yaml content on every poll. Since the editor JS calls this endpoint on an interval, this can generate unnecessary disk reads and network traffic for large files. Consider returning only the checksum by default (or supporting conditional requests like ETag/If-None-Match) and only returning content when the client indicates its checksum is stale (or requests ?include_content=1).

Suggested change
Return the current checksum and content of apps.yaml for external change detection
"""
try:
apps_yaml_path = "apps.yaml"
with open(apps_yaml_path, "r") as f:
content = f.read()
checksum = hashlib.md5(content.encode("utf-8")).hexdigest()
return web.json_response({"checksum": checksum, "content": content})
Return the current checksum (and optionally content) of apps.yaml for external change detection
"""
try:
# By default we preserve existing behaviour and return both checksum and content.
# Callers that only need the checksum can pass ?include_content=0 to reduce payload size.
include_content = request.rel_url.query.get("include_content", "1")
apps_yaml_path = "apps.yaml"
with open(apps_yaml_path, "r") as f:
content = f.read()
checksum = hashlib.md5(content.encode("utf-8")).hexdigest()
response_data = {"checksum": checksum}
# Treat common "false" values as a request to omit content
if include_content.lower() not in ("0", "false", "no"):
response_data["content"] = content
return web.json_response(response_data)

Copilot uses AI. Check for mistakes.
Comment on lines 5345 to 5346
// Check every 2 seconds for external changes
setInterval(checkForExternalChanges, 2000);
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The editor polls for external changes every 2 seconds, which can create a steady stream of requests (and currently downloads the full apps.yaml content each time). Consider increasing the interval and/or pausing polling when the tab is hidden, and combine with a checksum-only endpoint so polling is lightweight.

Copilot uses AI. Check for mistakes.
Comment on lines 251 to 260
# Test /component_restart POST
print("Test POST /component_restart")
address = "http://127.0.0.1:5052/component_restart"
data = {"component_name": "web"}
res = requests.post(address, data=data)
if res.status_code in [200, 302, 303, 400]:
accessed_endpoints.add(("POST", "/component_restart"))
else:
print("ERROR: Unexpected response from /component_restart: {} - {}".format(res.status_code, res.text))
failed = 1
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This POST test uses form field component_name, but the /component_restart handler reads component (see component_name = data.get("component") in web.py). As written the test will always hit the error path (400) and won’t validate the happy-path behavior. Update the test payload to send the expected field name so the endpoint is meaningfully exercised.

Copilot uses AI. Check for mistakes.
@springfall2008 springfall2008 merged commit 46c5594 into main Feb 8, 2026
1 check passed
@springfall2008 springfall2008 deleted the fixes5 branch February 8, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant