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 diff --git a/apps/predbat/tests/test_web_functions.py b/apps/predbat/tests/test_web_functions.py index 4d55bc2bf..daf52ef57 100644 --- a/apps/predbat/tests/test_web_functions.py +++ b/apps/predbat/tests/test_web_functions.py @@ -155,5 +155,76 @@ 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 + 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 + + 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: + 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 + + return failed diff --git a/apps/predbat/userinterface.py b/apps/predbat/userinterface.py index 151463e88..19991a972 100644 --- a/apps/predbat/userinterface.py +++ b/apps/predbat/userinterface.py @@ -453,6 +453,18 @@ 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 + 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): return await self.run_in_executor(self.expose_config, name, value, quiet, event, force, in_progress) @@ -482,9 +494,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: