From 938d80c310d536293e6c2d39ebec97b980ad3e63 Mon Sep 17 00:00:00 2001 From: Trefor Southwell Date: Sun, 21 Jun 2026 16:45:09 +0100 Subject: [PATCH 1/4] Fix web UI showing raw 'p' unit instead of configured currency (#4071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Predbat web UI config pages (/config and /entity) displayed the raw config item unit ("p") without applying the currency_symbols conversion, so entities like car_charging_plan_max_price always showed "p" even when currency_symbols was set to e.g. ['€', 'c']. The HA entity itself was already converted correctly via expose_config. Factor the conversion into a shared convert_currency_unit() helper on UserInterface, reuse it in expose_config, and apply it at the three web.py config display sites. Add unit tests covering the helper and the rendered config item HTML. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/predbat/tests/test_web_functions.py | 65 ++++++++++++++++++++++++ apps/predbat/userinterface.py | 15 ++++-- apps/predbat/web.py | 6 +-- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/apps/predbat/tests/test_web_functions.py b/apps/predbat/tests/test_web_functions.py index 4d55bc2bf..4cd582209 100644 --- a/apps/predbat/tests/test_web_functions.py +++ b/apps/predbat/tests/test_web_functions.py @@ -155,5 +155,70 @@ def set_exporting(on): my_predbat.dashboard_index = original_dashboard_index + # ------------------------------------------------------------------------- + # Currency unit display in the web config pages (issue #4071) + # The web UI must show the user's configured currency symbol, not the raw "p". + failed += run_currency_unit_tests(my_predbat, web) + print("**** Web functions tests completed ****") return failed + + +def run_currency_unit_tests(my_predbat, web): + """Verify config item units are converted to the user's currency symbols in the web UI.""" + failed = 0 + print("Test: web config pages convert currency units (issue #4071)") + + original_symbols = my_predbat.currency_symbols + original_num_cars = my_predbat.num_cars + + try: + my_predbat.currency_symbols = ["€", "c"] + my_predbat.num_cars = 1 + + # convert_currency_unit helper + if my_predbat.convert_currency_unit("p") != "c": + print(f" ERROR: 'p' should convert to 'c', got: {my_predbat.convert_currency_unit('p')}") + failed += 1 + if my_predbat.convert_currency_unit("p/kWh") != "c/kWh": + print(f" ERROR: 'p/kWh' should convert to 'c/kWh', got: {my_predbat.convert_currency_unit('p/kWh')}") + failed += 1 + if my_predbat.convert_currency_unit("£") != "€": + print(f" ERROR: '£' should convert to '€', got: {my_predbat.convert_currency_unit('£')}") + failed += 1 + if my_predbat.convert_currency_unit("kWh") != "kWh": + print(f" ERROR: 'kWh' should be unchanged, got: {my_predbat.convert_currency_unit('kWh')}") + failed += 1 + if my_predbat.convert_currency_unit("") != "": + print(f" ERROR: empty unit should stay empty, got: {my_predbat.convert_currency_unit('')}") + failed += 1 + + # Enable and locate the car charging max price config item + entity = None + for item in my_predbat.CONFIG_ITEMS: + if item.get("name") == "car_charging_plan_max_price": + item["value"] = 14 + entity = item.get("entity") + break + + if entity is None: + print(" ERROR: car_charging_plan_max_price config item not found") + return failed + 1 + + # html_config_item_text (shown on the /entity page) must use the converted unit + item_html = web.html_config_item_text(entity) + if item_html is None: + print(" ERROR: html_config_item_text returned None for car_charging_plan_max_price") + failed += 1 + else: + if "14 c" not in item_html: + print(f" ERROR: expected '14 c' in config item HTML, got: {item_html}") + failed += 1 + if "14 p" in item_html: + print(f" ERROR: unexpected raw 'p' unit in config item HTML: {item_html}") + failed += 1 + finally: + my_predbat.currency_symbols = original_symbols + my_predbat.num_cars = original_num_cars + + return failed diff --git a/apps/predbat/userinterface.py b/apps/predbat/userinterface.py index 151463e88..c8d5ecc7b 100644 --- a/apps/predbat/userinterface.py +++ b/apps/predbat/userinterface.py @@ -453,6 +453,17 @@ def get_ha_config(self, name, default): return value, default return None, default + def convert_currency_unit(self, unit): + """ + Convert a config item unit string (using the default £/p symbols) into the + user's configured currency symbols so displayed units match the rates. + """ + if not unit: + return unit + unit = unit.replace("£", self.currency_symbols[0]) + unit = unit.replace("p", self.currency_symbols[1]) + return unit + async def async_expose_config(self, name, value, quiet=True, event=False, force=False, in_progress=False): return await self.run_in_executor(self.expose_config, name, value, quiet, event, force, in_progress) @@ -482,9 +493,7 @@ def expose_config(self, name, value, quiet=True, event=False, force=False, in_pr if item["type"] == "input_number": """INPUT_NUMBER""" icon = item.get("icon", "mdi:numeric") - unit = item["unit"] - unit = unit.replace("£", self.currency_symbols[0]) - unit = unit.replace("p", self.currency_symbols[1]) + unit = self.convert_currency_unit(item["unit"]) self.set_state_wrapper( entity_id=entity, state=value, diff --git a/apps/predbat/web.py b/apps/predbat/web.py index b5578e57b..180daef91 100644 --- a/apps/predbat/web.py +++ b/apps/predbat/web.py @@ -762,7 +762,7 @@ def get_entity_list_data(self): if self.base.user_config_item_enabled(item): entity_id = item.get("entity", "") entity_friendly_name = item.get("friendly_name", "") - unit = item.get("unit", "") + unit = self.base.convert_currency_unit(item.get("unit", "")) if unit: entity_friendly_name = f"{entity_friendly_name} ({unit})" if entity_id: @@ -3583,7 +3583,7 @@ def html_config_item_text(self, entity): itemtype = item.get("type", "") default = item.get("default", "") icon = self.icon2html(item.get("icon", "")) - unit = item.get("unit", "") + unit = self.base.convert_currency_unit(item.get("unit", "")) text += "{}{}{}{}".format(icon, friendly, entity, itemtype) if value == default: text += '{} {}{} {}\n'.format(value, unit, default, unit) @@ -3634,7 +3634,7 @@ async def html_config(self, request): itemtype = item.get("type", "") default = item.get("default", "") useid = entity.replace(".", "__") - unit = item.get("unit", "") + unit = self.base.convert_currency_unit(item.get("unit", "")) icon = self.icon2html(item.get("icon", "")) if itemtype in ["input_number", "number"] and item.get("step", 1) == 1: From eee1edccbef3b73ef851a59830f241c59a8b482d Mon Sep 17 00:00:00 2001 From: Trefor Southwell Date: Sun, 21 Jun 2026 16:45:54 +0100 Subject: [PATCH 2/4] Version bump --- apps/predbat/predbat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/predbat/predbat.py b/apps/predbat/predbat.py index 88296f798..20c768308 100644 --- a/apps/predbat/predbat.py +++ b/apps/predbat/predbat.py @@ -35,7 +35,7 @@ import pytz import asyncio -THIS_VERSION = "v8.41.0" +THIS_VERSION = "v8.41.1" from download import predbat_update_move, predbat_update_download, check_install, DEFAULT_PREDBAT_REPOSITORY from const import MINUTE_WATT From 41e35fdb382443935416054879696be94ef1d267 Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Sun, 21 Jun 2026 18:39:39 +0100 Subject: [PATCH 3/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- apps/predbat/userinterface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/predbat/userinterface.py b/apps/predbat/userinterface.py index c8d5ecc7b..19991a972 100644 --- a/apps/predbat/userinterface.py +++ b/apps/predbat/userinterface.py @@ -460,8 +460,9 @@ def convert_currency_unit(self, unit): """ if not unit: return unit - unit = unit.replace("£", self.currency_symbols[0]) - unit = unit.replace("p", self.currency_symbols[1]) + major = self.currency_symbols[0] if self.currency_symbols and len(self.currency_symbols) > 0 else "£" + minor = self.currency_symbols[1] if self.currency_symbols and len(self.currency_symbols) > 1 else "p" + unit = unit.replace("£", "%%CURR_MAJOR%%").replace("p", minor).replace("%%CURR_MAJOR%%", major) return unit async def async_expose_config(self, name, value, quiet=True, event=False, force=False, in_progress=False): From c5fffd27f68deb464668906d2d8e9d7847565e29 Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Sun, 21 Jun 2026 18:39:58 +0100 Subject: [PATCH 4/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- apps/predbat/tests/test_web_functions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/predbat/tests/test_web_functions.py b/apps/predbat/tests/test_web_functions.py index 4cd582209..daf52ef57 100644 --- a/apps/predbat/tests/test_web_functions.py +++ b/apps/predbat/tests/test_web_functions.py @@ -195,8 +195,12 @@ def run_currency_unit_tests(my_predbat, web): # Enable and locate the car charging max price config item entity = None + original_item_value = None + original_item_ref = None for item in my_predbat.CONFIG_ITEMS: if item.get("name") == "car_charging_plan_max_price": + original_item_ref = item + original_item_value = item.get("value", None) item["value"] = 14 entity = item.get("entity") break @@ -218,6 +222,8 @@ def run_currency_unit_tests(my_predbat, web): print(f" ERROR: unexpected raw 'p' unit in config item HTML: {item_html}") failed += 1 finally: + if original_item_ref is not None: + original_item_ref["value"] = original_item_value my_predbat.currency_symbols = original_symbols my_predbat.num_cars = original_num_cars