import logging import xml.etree.ElementTree as ET from typing import List from custom_components.irm_kmi.const import POLLEN_NAMES _LOGGER = logging.getLogger(__name__) class PollenParser: def __init__( self, xml_string: str ): self._xml = xml_string @staticmethod def _validate_svg(elements: List[ET.Element]) -> bool: x_values = {"rectgreen": 80, "rectyellow": 95, "rectorange": 110, "rectred": 125, "rectpurple": 140} for e in elements: if e.attrib.get('id', '') in x_values.keys(): try: if float(e.attrib.get('x', '0')) != x_values.get(e.attrib.get('id')): return False except ValueError: return False return True @staticmethod def get_default_data() -> dict: return {k.lower(): 'none' for k in POLLEN_NAMES} @staticmethod def get_option_values() -> List[str]: return ['active', 'green', 'yellow', 'orange', 'red', 'purple', 'none'] @staticmethod def _extract_elements(root) -> List[ET.Element]: elements = [] for child in root: elements.append(child) elements.extend(PollenParser._extract_elements(child)) return elements @staticmethod def _dot_to_color_value(dot: ET.Element) -> str: try: cx = float(dot.attrib.get('cx')) except ValueError: return 'none' if cx > 155: return 'none' elif cx > 140: return 'purple' elif cx > 125: return 'red' elif cx > 110: return 'orange' elif cx > 95: return 'yellow' elif cx > 80: return 'green' else: return 'none' def get_pollen_data(self) -> dict: pollen_data = self.get_default_data() try: root = ET.fromstring(self._xml) except ET.ParseError: _LOGGER.warning("Could not parse SVG pollen XML") return pollen_data elements: List[ET.Element] = self._extract_elements(root) if not self._validate_svg(elements): _LOGGER.warning("Could not validate SVG pollen data") return pollen_data pollens = [e for e in elements if 'tspan' in e.tag and e.text in POLLEN_NAMES] active = [e for e in elements if 'tspan' in e.tag and e.text == 'active'] dots = [e for e in elements if 'ellipse' in e.tag and 'fill:#ffffff' in e.attrib.get('style', '') and 3 == float(e.attrib.get('rx', '0'))] for pollen in pollens: try: y = float(pollen.attrib.get('y')) if y in [float(e.attrib.get('y')) for e in active]: pollen_data[pollen.text.lower()] = 'active' else: dot = [d for d in dots if y - 3 <= float(d.attrib.get('cy', '0')) <= y + 3] if len(dot) == 1: dot = dot[0] pollen_data[pollen.text.lower()] = self._dot_to_color_value(dot) except ValueError | NameError: _LOGGER.warning("Skipped some data in the pollen SVG") _LOGGER.debug(f"Pollen data: {pollen_data}") return pollen_data