import re from html.parser import HTMLParser from typing import Any, Callable, Dict, List, Tuple, Type _VARIABLE_DICT = { "BlackTonerPer": lambda x: { "toner_black": {"opt": 1, "remaining": int(x), "newError": ""} }, "tray1Status": lambda x: {"tray1": {"opt": 1, "newError": ""}}, "tray2Installed": lambda x: { "tray2": {"opt": 1 if x == "Installed" else 0, "newError": ""} }, "tray3Installed": lambda x: { "tray3": {"opt": 1 if x == "Installed" else 0, "newError": ""} }, "tray4Installed": lambda x: { "tray4": {"opt": 1 if x == "Installed" else 0, "newError": ""} }, } # type: Dict[str, Callable[[str], Dict[str, Any]]] _KNOWN_VARIABLES = "|".join(_VARIABLE_DICT.keys()) _VARIABLES_REG = ( r"var\s+(?P{})\s*=\s*[\"']?(?P[a-zA-Z0-9]+)[\"']?\s*;".format( _KNOWN_VARIABLES ) ) ENDPOINT_HTML_SUPPLIES_STATUS = "/Information/supplies_status.htm" ENDPOINT_HTML_HOME = "/home.htm" ENDPOINT_HTML_GENERAL_PROTOCOLS = "/Settings/Protocols/general_protocols.htm" class SyncThruParser(HTMLParser): """ General class of parsers that update a syncthru state dict """ def __init__(self, data: Dict[str, Any]): super().__init__() self._data = data class HomeParser(SyncThruParser): """ Parser for the home.htm endpoint """ def __init__(self, data: Dict[str, Any]): super().__init__(data) if "identity" not in data: data["identity"] = {} _first_lcdFont = True _model_name = False _name_tag = False _name_key: str = "" _value_tag = False def handle_starttag(self, tag: str, attrs: List[Tuple[str, Any]]) -> None: if tag == "font" and ("class", "lcdFont") in attrs: if self._first_lcdFont: self._model_name = True self._first_lcdFont = False else: self._name_tag = "color" not in map(lambda x: x[0], attrs) self._value_tag = not self._name_tag def handle_data(self, data: str) -> None: if self._model_name: self._data["identity"]["model_name"] = data.strip() self._model_name = False if self._name_tag: data = data.replace(":", "").strip().replace(" ", "_").lower() if data == "name": data = "host_name" self._name_key = data # we assume that the whole tag is read at once # (which is not necessarily true) self._name_tag = False if self._value_tag: self._data["identity"][self._name_key] = data.strip() # we assume that the whole tag is read at once # (which is not necessarily true) self._value_tag = False class VariableParser(SyncThruParser): """ Generic parser for variables declared in javascript """ def __init__(self, data: Dict[str, Any]): super().__init__(data) _inside_script = False def handle_starttag(self, tag: str, attrs: List[Tuple[str, Any]]) -> None: if tag == "script" and ( ("language", "javascript") in attrs or ("type", "text/javascript") in attrs ): self._inside_script = True def handle_data(self, data: str) -> None: # parse javascript variable declarations if self._inside_script: for match in re.finditer(_VARIABLES_REG, data): self._data.update( _VARIABLE_DICT[match.group("varname")](match.group("varval")) ) def handle_endtag(self, tag: str) -> None: if tag == "script": self._inside_script = False class GeneralProtocolParser(SyncThruParser): """ Parser for the general_protocols.htm website """ def __init__(self, data: Dict[str, Any]): super().__init__(data) if "identity" not in data: data["identity"] = {} _name_tag = False _name_key: str = "" _value_tag = False def handle_starttag(self, tag: str, attrs: List[Tuple[str, Any]]) -> None: if tag == "td": self._name_tag = ("class", "plainFont") in attrs self._value_tag = ("class", "valueFont") in attrs elif tag == "input" and self._value_tag and ("type", "text") in attrs: self._data["identity"][self._name_key] = dict(attrs)["value"] self._value_tag = False def handle_data(self, data: str) -> None: if self._name_tag: data = data.replace(":", "").strip().replace(" ", "_").lower() if data == "mac_address": data = "mac_addr" self._name_key = data # we assume that the whole tag is read at once # (which is not necessarily true) self._name_tag = False if self._value_tag: self._data["identity"][self._name_key] = data.strip() # we assume that the whole tag is read at once # (which is not necessarily true) self._value_tag = False ENDPOINT_HTML_PARSERS: Dict[str, List[Type[SyncThruParser]]] = { ENDPOINT_HTML_HOME: [HomeParser, VariableParser], ENDPOINT_HTML_SUPPLIES_STATUS: [VariableParser], ENDPOINT_HTML_GENERAL_PROTOCOLS: [GeneralProtocolParser], }