@@ -38,6 +38,11 @@ def make_id(name: str) -> str:
3838 return name .strip ("_" )
3939
4040
41+ def is_strict_number (s : str ) -> bool :
42+ s2 = s .strip ().replace (',' , '.' )
43+ return bool (re .fullmatch (r'[-+]?\d+(\.\d+)?' , s2 ))
44+
45+
4146def friendly_name (group : str , sensor : str ) -> str :
4247 """Format a friendly sensor name with group context."""
4348 group_lower = group .lower ()
@@ -88,18 +93,30 @@ def normalize_value_and_unit(
8893 - Fallback to default unit if unit is missing but device_class is known.
8994 Returns: value (str), unit (str or None)
9095 """
96+
97+ # determine if the context is suggesting a numeric value
98+ numeric_device_classes = {
99+ "energy" , "power" , "voltage" , "current" , "temperature" ,
100+ "frequency" , "battery" , "humidity" , "pressure"
101+ }
102+ numeric_context = (unit is not None ) or (device_class in numeric_device_classes ) or is_strict_number (value_raw )
103+
104+ if not numeric_context :
105+ # not a numeric context, return raw value and no unit
106+ return value_raw , None
107+
91108 value_clean = get_numeric_value (value_raw )
92109 unit_out = unit
93110
94- # Wh zu kWh konvertieren
111+ # convert Wh to kWh if applicable
95112 if unit == "Wh" :
96113 try :
97114 value_clean = str (round (float (value_clean ) / 1000 , 3 ))
98115 unit_out = "kWh"
99116 except ValueError :
100117 pass
101118
102- # Fallback: Wenn device_class bekannt, aber unit fehlt
119+ # fallback to default unit if missing
103120 if device_class and not unit_out :
104121 unit_out = default_units .get (device_class )
105122
@@ -110,7 +127,7 @@ def parse_enpal_html_sensors(
110127 html : str ,
111128 groups : List [str ]
112129) -> List [Dict [str , Any ]]:
113- """Parst Enpal HTML und liefert Sensor-Data-Listen für Home Assistant ."""
130+ """parsing the html content and extracting sensor data ."""
114131 soup = BeautifulSoup (html , 'html.parser' )
115132 sensors = []
116133
@@ -128,14 +145,14 @@ def parse_enpal_html_sensors(
128145
129146
130147def extract_group_from_card (card : Tag ) -> Optional [str ]:
131- """Liest die Gruppenüberschrift (h2) aus einer Card."""
148+ """Reads the group name from a Card header (h2) ."""
132149 h2_tag = card .find ("h2" )
133150 return h2_tag .text .strip () if h2_tag else None
134151
135152
136153def parse_card_rows (card : Tag , group : str , groups : List [str ]) -> List [Dict [str , Any ]]:
137- """Extrahiert Sensorsätze aus einer Card-Tabelle ."""
138- rows = card .find_all ("tr" )[1 :] # erste Zeile = Header
154+ """Extracts sensors from a group ."""
155+ rows = card .find_all ("tr" )[1 :] # assume first row == header
139156 sensor_list = []
140157
141158 for row in rows :
@@ -173,7 +190,7 @@ def parse_card_rows(card: Tag, group: str, groups: List[str]) -> List[Dict[str,
173190
174191
175192def parse_timestamp (raw : Optional [str ]) -> Optional [str ]:
176- """Parst einen Enpal-Timestamp (z. B . 06.06.2025 08:42) ins ISO-Format."""
193+ """Converts a timestamp (i.g . 06.06.2025 08:42) to ISO-Format."""
177194 if not raw :
178195 return None
179196 try :
0 commit comments