Skip to content

Commit 47f229e

Browse files
committed
fix: home assistant proxy #1235
Use dash_app.get_asset_url() to ensure image paths respect the X-Ingress-Path header (requests_pathname_prefix). - Update all SVG image paths in control.py, figures.py, and views.py to use get_asset_url() instead of hardcoded 'assets/images/...' paths - Add test_svg_images_with_ingress_path() to verify image paths contain the correct prefix when X-Ingress-Path header is present Fixes issue where image paths were not prefixed in Home Assistant ingress proxy setups.
1 parent dd35666 commit 47f229e

4 files changed

Lines changed: 51 additions & 11 deletions

File tree

psa_car_controller/web/figures.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from psa_car_controller.psacc.model.car import Car
1313
from psa_car_controller.psacc.repository.trips import Trip
1414
from psa_car_controller.psacc.repository.db import Database
15+
from psa_car_controller.web.app import dash_app
1516

1617
# pylint: disable=invalid-name
1718
from psa_car_controller.web.tools.utils import card_value_div, dash_date_to_datetime
@@ -41,15 +42,15 @@
4142
def get_summary_cards():
4243
return {"Average consumption": {"text": [card_value_div(AVG_CONSUM_KW, "kWh/100km"),
4344
card_value_div(AVG_CONSUM_PRICE, f"{CURRENCY}/100km")],
44-
"src": "assets/images/consumption.svg"},
45+
"src": dash_app.get_asset_url("images/consumption.svg")},
4546
"Average emission": {"text": [card_value_div(AVG_EMISSION_KM, " g/km"),
4647
card_value_div(AVG_EMISSION_KW, "g/kWh")],
47-
"src": "assets/images/pollution.svg"},
48+
"src": dash_app.get_asset_url("images/pollution.svg")},
4849
"Average charge speed": {"text": [card_value_div(AVG_CHARGE_SPEED, " kW")],
49-
"src": "assets/images/battery-charge-line.svg"},
50+
"src": dash_app.get_asset_url("images/battery-charge-line.svg")},
5051
"Electricity consumption": {"text": [card_value_div(ELEC_CONSUM_KW, "kWh"),
5152
card_value_div(ELEC_CONSUM_PRICE, CURRENCY)],
52-
"src": "assets/images/electricity bill.svg"}
53+
"src": dash_app.get_asset_url("images/electricity bill.svg")}
5354
}
5455

5556

psa_car_controller/web/view/control.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from psa_car_controller.psacc.application.psa_client import PSAClient
88
from psa_car_controller.psacc.repository.db import Database
9+
from psa_car_controller.web.app import dash_app
910
from psa_car_controller.web.tools.Button import Button
1011
from psa_car_controller.web.tools.Switch import Switch
1112
from psa_car_controller.web.tools.utils import card_value_div, create_card
@@ -39,18 +40,18 @@ def get_control_tabs(config):
3940
cards = OrderedDict({"Battery SOC": {"text": [card_value_div("battery_value", "%",
4041
value=convert_value_to_str(
4142
car.status.get_energy('Electric').level))],
42-
"src": "assets/images/battery-charge.svg"},
43+
"src": dash_app.get_asset_url("images/battery-charge.svg")},
4344
"Mileage": {"text": [card_value_div("mileage_value", "km",
4445
value=convert_value_to_str(
4546
car.status.timed_odometer.mileage))],
46-
"src": "assets/images/mileage.svg"}
47+
"src": dash_app.get_asset_url("images/mileage.svg")}
4748
})
4849
soh = Database.get_last_soh_by_vin(car.vin)
4950
if soh:
5051
cards["Battery SOH"] = {"text": [card_value_div("battery_soh_value", "%",
5152
value=convert_value_to_str(
5253
soh))],
53-
"src": "assets/images/battery-soh.svg"}
54+
"src": dash_app.get_asset_url("images/battery-soh.svg")}
5455
cards.move_to_end("Mileage")
5556
el.append(dbc.Container(dbc.Row(children=create_card(cards)), fluid=True))
5657
if config.remote_control:
@@ -60,7 +61,7 @@ def get_control_tabs(config):
6061

6162
refresh_date = car.status.get_energy('Electric').updated_at.astimezone().strftime("%X %x")
6263
buttons_row.extend([Button(REFRESH_SWITCH, car.vin,
63-
html.Div([html.Img(src="assets/images/sync.svg", width="50px"),
64+
html.Div([html.Img(src=dash_app.get_asset_url("images/sync.svg"), width="50px"),
6465
refresh_date]),
6566
myp.remote_client.wakeup).get_html(),
6667
Switch(CHARGE_SWITCH, car.vin, "Charge", myp.remote_client.charge_now,

psa_car_controller/web/view/views.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ def add_header(el):
5656
color="secondary",
5757
className="me-1 bi bi-github",
5858
external_link=True, href=github_url)
59-
return dbc.Row([dbc.Col(dcc.Link(html.H1('My car info'), href=dash_app.config.requests_pathname_prefix,
59+
prefix = dash_app.config.requests_pathname_prefix or ""
60+
return dbc.Row([dbc.Col(dcc.Link(html.H1('My car info'), href=prefix,
6061
style={"TextDecoration": "none"})),
6162
dbc.Col(html.Div([dbc_version,
62-
dcc.Link(html.Img(src="assets/images/settings.svg", width="30veh"),
63-
href=dash_app.config.requests_pathname_prefix + "config",
63+
dcc.Link(html.Img(src=dash_app.get_asset_url("images/settings.svg"), width="30veh"),
64+
href=prefix + "config",
6465
className="float-end")],
6566
className="d-grid gap-2 d-md-flex justify-content-md-end",))],
6667
className='align-items-center'), el

tests/test_ha_ingress_integration.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,43 @@ def test_style_json_with_ingress_path(self):
213213
except requests.exceptions.RequestException as e:
214214
self.fail(f"Request failed: {e}")
215215

216+
def test_svg_images_with_ingress_path(self):
217+
"""
218+
Test that SVG images in HTML have correct path with X-Ingress-Path header.
219+
Specifically checks for battery-soh.svg and other image paths.
220+
"""
221+
prefix = '/api/ingress/a93a74ea_psacc/'
222+
headers = {
223+
'X-Ingress-Path': prefix,
224+
'Host': f'127.0.0.1:{self.server_port}'
225+
}
226+
227+
try:
228+
response = requests.get(self.server_url, headers=headers, timeout=10)
229+
230+
self.assertEqual(response.status_code, 200,
231+
f"Expected 200, got {response.status_code}")
232+
233+
html = response.text
234+
235+
# Find all src attributes in img tags
236+
img_srcs = re.findall(r'<img[^>]+src="([^"]+)"', html)
237+
238+
# Check each image source
239+
for img_src in img_srcs:
240+
# Skip data URLs
241+
if img_src.startswith('data:'):
242+
continue
243+
244+
# Check if it's an assets/image path
245+
if 'assets/images/' in img_src:
246+
# The path should start with the prefix
247+
self.assertTrue(img_src.startswith(prefix),
248+
f"Image path {img_src} does not start with prefix {prefix}")
249+
250+
except requests.exceptions.RequestException as e:
251+
self.fail(f"Request failed: {e}")
252+
216253

217254
if __name__ == '__main__':
218255
import unittest

0 commit comments

Comments
 (0)