diff --git a/incomfortclient/__init__.py b/incomfortclient/__init__.py index 0a451d3..b8f04e5 100644 --- a/incomfortclient/__init__.py +++ b/incomfortclient/__init__.py @@ -10,6 +10,8 @@ from __future__ import annotations import logging +from enum import IntEnum +from typing import Any import aiohttp @@ -29,45 +31,54 @@ BITMASK_PUMP = 0x02 # pump state: on / off BITMASK_TAP = 0x04 # tap (DHW) state: function on / off -# key label: displ_code -DISPLAY_CODES: dict[int, str] = { - 0: "opentherm", - 15: "boiler ext.", - 24: "frost", - 37: "central heating rf", - 51: "tapwater int.", - 85: "sensortest", - 102: "central heating", - 126: "standby", - 153: "postrun boiler", - 170: "service", - 204: "tapwater", - 231: "postrun ch", - 240: "boiler int.", - 255: "buffer", -} -FAULT_CODES: dict[int, str] = { - 0: "Sensor fault after self check", - 1: "Temperature too high", - 2: "S1 and S2 interchanged", - 4: "No flame signal", - 5: "Poor flame signal", - 6: "Flame detection fault", - 8: "Incorrect fan speed", - 10: "Sensor fault S1", - 11: "Sensor fault S1", - 12: "Sensor fault S1", - 13: "Sensor fault S1", - 14: "Sensor fault S1", - 20: "Sensor fault S2", - 21: "Sensor fault S2", - 22: "Sensor fault S2", - 23: "Sensor fault S2", - 24: "Sensor fault S2", - 27: "Shortcut outside sensor temperature", - 29: "Gas valve relay faulty", - 30: "Gas valve relay faulty", -} # "0.0": "Low system pressure" +ISSUE_URL = "https://github.com/jbouwh/incomfort-client/issues" + + +class DiplayCode(IntEnum): + """Label to display code.""" + + UNKNOWN = -1 + OPENTHERM = 0 + BOILET_EXT = 15 + FROST = 24 + CENNTRAL_HEATING_RF = 37 + TAPWATER_INT = 51 + SENSOR_TEST = 85 + CENNTRAL_HEATING = 102 + STANDBY = 126 + POSTRUN_BOYLER = 153 + SERVICE = 170 + TAPWATER = 204 + POSTRUN_CH = 231 + BOILER_INT = 240 + BUFFER = 255 + + +class FaultCode(IntEnum): + """Label to fault code.""" + + UNKNOWN = -1 + SENSOR_FAULT_AFTER_SELF_CHECK_E0: 0 + CV_TEMPERATURE_TOO_HIGH_E1: 1 + S1_AND_S2_INTERCHANGED_E2: 2 + NO_FLAME_SIGNAL_E4: 4 + POOR_FLAME_SIGNAL_E5: 5 + FLAME_DETECTION_FAULT_E6: 6 + INCORRECT_FAN_SPEED_E8: 8 + SENSOR_FAULT_S1_E10: 10 + SENSOR_FAULT_S1_E11: 11 + SENSOR_FAULT_S1_E12: 12 + SENSOR_FAULT_S1_E13: 13 + SENSOR_FAULT_S1_E14: 14 + SENSOR_FAULT_S2_E20: 20 + SENSOR_FAULT_S2_E21: 21 + SENSOR_FAULT_S2_E22: 22 + SENSOR_FAULT_S2_E23: 23 + SENSOR_FAULT_S2_E24: 24 + SHORTCUT_OUTSIDE_SENSOR_TEMPERATURE_E27: 27 + GAS_VALVE_RELAY_FAULTY_E29: 29 + GAS_VALVE_RELAY_FAULTY_E30: 30 + HEATER_ATTRS: tuple[str, ...] = ( "display_code", @@ -221,11 +232,45 @@ def __init__(self, serial_no: str, idx: int, gateway: Gateway) -> None: self._data: dict = {} self._status: dict = {} self._rooms: list[Room] = None + self.display_code: DiplayCode | None = None + self.fault_code: FaultCode | None = None + self._last_display_code: int | None = None async def update(self) -> None: """Retrieve the Heater's latest status from the Gateway.""" self._data = await self._get(f"data.json?heater={self._heater_idx}") + code: int = self._data["displ_code"] + if self.is_failed: + self.display_code = None + try: + self.fault_code = FaultCode(code) + except ValueError: + self.fault_code = FaultCode.UNKNOWN + if self._last_display_code != code: + _LOGGER.warning( + "Unknown fault code %s reported by heater %s. " + "Log an issue at %s to report the unknown fault code", + code, + self._serial_no, + ISSUE_URL, + ) + else: + self.fault_code = None + try: + self.display_code = DiplayCode(code) + except ValueError: + self.display_code = DiplayCode.UNKNOWN + if self._last_display_code != code: + _LOGGER.warning( + "Unknown operation code %s reported by heater %s" + "Log an issue at %s to report the unknown display code", + code, + self._serial_no, + ISSUE_URL, + ) + self._last_display_code = code + self._status = {} for attr in HEATER_ATTRS: @@ -237,26 +282,16 @@ async def update(self) -> None: _LOGGER.debug("Heater(%s).status() = %s", self._serial_no, self._status) @property - def status(self) -> dict: + def status(self) -> dict[str, Any]: """Return the current state of the heater.""" return self._status @property - def display_code(self) -> int: - """Return the display code, 'displ_code'.""" - return self._data["displ_code"] - - @property - def display_text(self) -> None | str: - """Return the display code as text rather than a code.""" - code = self.display_code - code_map = FAULT_CODES if self.is_failed else DISPLAY_CODES - return code_map.get(code, f"unknown/other, code = '{code}'") - - @property - def fault_code(self) -> None | int: - """Return the fault code when the heater is in a failed state.""" - return self._data["displ_code"] if self.is_failed else None + def display_text(self) -> str | None: + """Return the display or fault code as text label rather than a code.""" + if self.is_failed: + return self.fault_code.name.lower() if self.fault_code else None + return self.display_code.name.lower() if self.display_code else None @property def is_burning(self) -> bool: