diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..9b77ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "" +labels: "" +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..2bc5d5f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "" +labels: "" +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/CHANGELOG.md b/CHANGELOG.md index fc512a3..100b53f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## v3.0.1 + +- Refactor entity's creation (using EntityDescription) +- Mapped all protocol codes to `EntityDescription` +- Add more entities (updated list in README) +- Create [Protocol Codes](https://github.com/radical-squared/aquatemp/blob/master/PROTOCOL_CODES.md) document +- Fix `climate` HVAC Modes + ## v3.0.0 - Major refactor to the code - Breaking changes diff --git a/PROTOCOL_CODES.md b/PROTOCOL_CODES.md new file mode 100644 index 0000000..8288b46 --- /dev/null +++ b/PROTOCOL_CODES.md @@ -0,0 +1,243 @@ +# Protocol Codes + +## Control parameters + +| Parameter | Description | +| ----------- | --------------- | +| Power | Power | +| Mode | HVAC Mode | +| Manual-mute | Fan Mode | +| 2074 | 2074 | +| 2075 | 2075 | +| 2076 | 2076 | +| 2077 | 2077 | +| Set_Temp | Set Temperature | +| 1158 | 1158 | +| 1159 | 1159 | + +## Hardware parameter + +| Parameter | Description | +| --------- | ---------------------------------------------------- | +| /01 | Whether enable system 1 high pressure switch | +| /02 | Whether enable system 2 high pressure switch | +| /03 | Whether enable system 1 low pressure switch | +| /04 | Whether enable system 2 low pressure switch | +| /05 | Whether enable phase monitor protection | +| /06 | Whether enable water flow protection | +| /07 | Whether enable electrical heater Overload protection | +| /08 | Whether enable system 1 overload protection | +| /09 | Whether enable system 2 overload protection | +| /10 | Whether enable remote on/off switch | +| /11 | Whether enable system protect | +| /12 | Whether enable Outlet probe | +| /13 | Whether enable Coil 1 probe | +| /14 | Whether enable Coil 2 probe | +| /15 | Whether enable Ambient probe | +| /16 | Whether enable deice 1 probe | +| /17 | Whether enable deice 2 probe | +| /18 | Whether enable Suction 1 probe | +| /19 | Whether enable Suction 2 probe | +| /20 | Whether enable exhaust 1 probe | +| /21 | Whether enable exhaust 2 probe | +| /22 | Whether enable ΔT1 protect | +| /23 | Whether enable Cooling mode | +| /24 | Whether enable Economic heating | +| /25 | Whether enable AUTO mode | +| /26 | Whether enable Heating mode | +| /27 | Whether enable High demand | +| /28 | Whether enable heat recovery temperature | + +## Protection parameter + +| Parameter | Description | +| --------- | ----------------------------------------------- | +| A01 | High pressure alarm time delay | +| A02 | Low pressure alarm time delay | +| A03 | Stop unit air temperature | +| A04 | Antifreeze setting temperature | +| A05 | Antifreeze differential protection | +| A06 | Discharge temp. protection setting | +| A07 | Discharge temp. differential protection | +| A08 | Inlet/Out differential protection setting value | +| A09 | Start spraying air temperature | + +## Compressor parameter + +| Parameter | Description | +| --------- | ----------------------------------------- | +| C01 | Minimum on time | +| C02 | Minimum off time | +| C03 | Delay between starts of the 2 compressors | +| C04 | Rotation | + +## Defrost parameter + +| Parameter | Description | +| --------- | ---------------------------- | +| D01 | Start defrosting temperature | +| D02 | End defrost temperature | +| D03 | defrosting cycle | +| D04 | Maximum defrosting time | +| D05 | Minimum defrosting time | +| D06 | Defrost mode | +| D07 | Defrost heater control | +| D08 | Defrost AUTO set | + +## EEV parameter + +| Parameter | Description | +| --------- | --------------- | +| E01 | EEV 1 mode | +| E02 | Super heat 1 | +| E03 | Initial place 1 | +| E04 | EEV 2 mode | +| E05 | Super heat 2 | +| E06 | Initial place 2 | +| E07 | Minimum place | +| E08 | Defrost place | +| E09 | Cooling place | +| E10 | Low exhaust | +| E11 | High exhaust | + +## Fan parameter + +| Parameter | Description | +| --------- | ------------------------------------------------- | +| F01 | Fan parameter | +| F02 | Coil temperature in high speed fan mode (Cooling) | +| F03 | Coil temperature in low speed fan mode (Cooling) | +| F04 | Coil temperature when the fan stop (Cooling) | +| F05 | Coil temperature in high speed fan mode (Heating) | +| F06 | Coil temperature in low speed fan mode (Heating) | +| F07 | Coil temperature when the fan stop (Heating) | +| F08 | Fan start low speed running time | +| F09 | Fan stop low speed running time | +| F10 | Fan quantity | +| F11 | Fan speed control temp. | + +## System parameter + +| Parameter | Description | +| --------- | -------------------- | +| H01 | Automatic restarting | +| H02 | System quantity | +| H03 | 4-way valve polarity | +| H04 | 4-way valve control | +| H05 | Model | +| H06 | Type | +| H07 | Class | +| H08 | Capacity Control | +| H09 | Coil sensor function | +| H10 | Physical address | +| H11 | Baud rate | +| H12 | Parity bit | +| H13 | Stop bit | + +## Water pump parameter + +| Parameter | Description | +| --------- | ---------------------------------------------------------------- | +| R01 | Water pump mode | +| P02 | Water pump running cycle | +| P03 | Water pump running time | +| P04 | Delay in switching on the compressor after switching on the pump | +| P05 | Filter | +| P06 | Start filter 1 | +| P07 | Stop filter 1 | +| P08 | Start filter 2 | +| P09 | Stop filter 2 | + +## Temp parameter + +| Parameter | Description | +| --------- | ---------------------------------------------------- | +| R01 | Inlet water setting temperature (cooling) | +| R02 | Inlet water setting temperature (Heating) | +| R03 | Target setting temperature (Auto mode) | +| R04 | Cooling differential | +| R05 | Cooling stop differential | +| R06 | Heating differential | +| R07 | Heating stop differential | +| R08 | Minimum set point in Cooling | +| R09 | Maximum Cooling set point | +| R10 | Minimum Heating set point | +| R11 | Maximum Heating set point | +| R12 | Electrical ΔT6 | +| R13 | Electrical Ambient | +| R14 | Electrical Delay | +| R15 | Electrical force | +| R16 | Compensation | +| R17 | Maximum ΔT7 | +| R18 | Cooling compensation constant | +| R19 | Cooling compensation start air temperature | +| R20 | Heating compensation start air temperature | +| R21 | Whether enable heat recovery | +| R22 | The target temperature Of heat recovery | +| R23 | Temperature differential of heat recovery | +| R24 | Temperature to stop heat recovery | +| R25 | Temperature differential to stop heat recovery | +| R26 | Electric heater mode | +| R27 | Ambient temperature to start up antifreezing heater | +| R28 | Temperature differential to stop antifreezing heater | + +## Water flow parameter + +| Parameter | Description | +| --------- | ------------ | +| U01 | Flow meter | +| U02 | Pulse | +| U03 | Flow protect | +| U04 | Flow alarm | + +## Switch state checking + +| Parameter | Description | +| --------- | -------------------------- | +| S01 | System1 HP | +| S02 | System2 HP | +| S03 | System1 LP | +| S04 | System2 LP | +| S05 | Phase monitor | +| S06 | Water Flow switch | +| S07 | Electrical heater overload | +| S08 | COMP1 overload | +| S09 | COMP2 overload | +| S10 | On/Off switch | +| S11 | Mode switch | +| S12 | System protect | +| S13 | Water flow | + +## Temp. checking + +| Parameter | Description | +| --------- | ------------------------ | +| T01 | Suction temperature | +| T02 | Inlet water temp. | +| T03 | Outlet water temp | +| T04 | Coil temperature | +| T05 | Ambient temperature | +| T06 | Antifreeze 1 temperature | +| T07 | Antifreeze 2 temperature | +| T08 | Suction 1 temperature | +| T09 | Suction 2 temperature | +| T10 | Exhaust 1 temperature | +| T11 | Exhaust 2 temperature | +| T12 | Hot water temperature | +| T14 | None | + +## Load output + +| Parameter | Description | +| --------- | ----------------------------------- | +| O01 | Compressor 1 output | +| O02 | Compressor 2 output | +| O03 | Fan output (High speed) | +| O04 | Fan output (Low speed) | +| O05 | Circulate pump output | +| O06 | 4-way valve output | +| O07 | Heat element output | +| O08 | Alarm output | +| O09 | Spray valve output | +| O10 | Electronic Expansion valve 1 output | +| O11 | Electronic Expansion valve 2 output | diff --git a/README.md b/README.md index e078ccd..8113407 100644 --- a/README.md +++ b/README.md @@ -51,115 +51,37 @@ Device scheme is according to data from the API Per device the following components will be generated: -| Entity Name | Type | Source | Parameter | Description | -| ----------------------------------------- | ------------- | ------------------- | ---------------- | ------------------------------------------------------------------------------------------------- | -| {HA Device Name} Status | Binary Sensor | Devices list | device_status | Represents whether device is online or not | -| {HA Device Name} Fault | Binary Sensor | Devices status | is_fault | Represents whether device has an issue, in case it has, attribute `fault` will hold the details | -| {HA Device Name} Power | Binary Sensor | Protocol Codes | Power | Represents whether device is on or off | -| {HA Device Name} Power | Climate | Protocol Codes | Power | Controls heat pump - HVAC Modes (Off, Cool, Heat, Auto), Fan Mode (Low, Auto), Target temperature | -| {HA Device Name} Suction temperature | Sensor | Protocol Codes | T01 | Represents the temperature according to the name of the component | -| {HA Device Name} Inlet water temp. | Sensor | Protocol Codes | T02 | Represents the temperature according to the name of the component | -| {HA Device Name} Outlet water temp | Sensor | Protocol Codes | T03 | Represents the temperature according to the name of the component | -| {HA Device Name} Coil temperature | Sensor | Protocol Codes | T04 | Represents the temperature according to the name of the component | -| {HA Device Name} Ambient temperature | Sensor | Protocol Codes | T05 | Represents the temperature according to the name of the component | -| {HA Device Name} Antifreeze 1 temperature | Sensor | Protocol Codes | T06 | Represents the temperature according to the name of the component | -| {HA Device Name} Antifreeze 2 temperature | Sensor | Protocol Codes | T07 | Represents the temperature according to the name of the component | -| {HA Device Name} Suction 1 temperature | Sensor | Protocol Codes | T08 | Represents the temperature according to the name of the component | -| {HA Device Name} Suction 2 temperature | Sensor | Protocol Codes | T08 | Represents the temperature according to the name of the component | -| {HA Device Name} Exhaust 1 temperature | Sensor | Protocol Codes | T09 | Represents the temperature according to the name of the component | -| {HA Device Name} Exhaust 1 temperature | Sensor | Protocol Codes | T10 | Represents the temperature according to the name of the component | -| {HA Device Name} Hot water temperature | Sensor | Protocol Codes | T12 | Represents the temperature according to the name of the component | -| {HA Device Name} Temperature Unit | Select | Local configuration | temperature_unit | Controls the unit of temperature to match the one configured in the Aqua Temp device | - -## Protocol Codes - -Protocol Codes first character represent a category, below are the categories: - -| Character | Description | Parameters | -| --------- | --------------------- | ---------- | -| / | Hardware parameter | /01-/28 | -| A | Protection parameter | A01-A09 | -| C | Compressor parameter | C01-C04 | -| D | Defrost parameter | D01-D08 | -| E | EEV parameter | E01-E11 | -| F | Fan parameter | F01-F11 | -| H | System parameter | H01-H13 | -| P | Water pump parameter | P01-P09 | -| R | Temp parameter | R01-R28 | -| U | Water flow parameter | U01-U04 | -| S | Switch state checking | S01-S13 | -| T | Temp. checking | T01-T12 | -| O | Load output | O01-O11 | - -Integration is collecting the following - -```json -{ - "Power": "0 - Off or 1 - On", - "Mode": "HVAC mode (Off, Cool, Heat, Auto)", - "Manual-mode": "Fan speed (Low, Auto)", - "2074": "Unknown", - "2075": "Unknown", - "2076": "Unknown", - "2077": "Unknown", - "Set_Temp": "Set Target temperature", - "1158": "Unknown", - "1159": "Unknown", - "D01": "Start defrosting temperature", - "D02": "End defrost temperature", - "D03": "defrosting cycle", - "D04": "Maximum defrosting time", - "D05": "Minimum defrosting time", - "D06": "Defrost mode", - "D07": "Defrost heater control", - "D08": "Defrost AUTO set", - "E01": "EEV 1 mode", - "E02": "Super heat 1", - "E03": "Initial place 1", - "E04": "EEV 2 mode", - "E05": "Super heat 2", - "E06": "Initial place 2", - "E07": "Minimum place", - "E08": "Defrost place", - "E09": "Cooling place", - "E10": "Low exhaust", - "E11": "High exhaust", - "F01": "Fan parameter", - "F02": "Coil temperature in high speed fan mode (Cooling)", - "F03": "Coil temperature in low speed fan mode (Cooling)", - "F04": "Coil temperature when the fan stop (Cooling)", - "F05": "Coil temperature in high speed fan mode (Heating)", - "F06": "Coil temperature in low speed fan mode (Heating)", - "F07": "Coil temperature when the fan stop (Heating)", - "F08": "Fan start low speed running time", - "F09": "Fan stop low speed running time", - "F10": "Fan quantity", - "F11": "Fan speed control temp." - "H02": "System quantity", - "H03": "4-way valve polarity", - "H04": "4-way valve control", - "R01": "Inlet water setting temperature (cooling)", - "R02": "Inlet water setting temperature (Heating)", - "R03": "Target setting temperature (Auto mode)", - "R08": "Minimum set point in Cooling", - "R09": "Maximum Cooling set point", - "R10": "Minimum Heating set point", - "R11": "Maximum Heating set point", - "U02": "Pulse", - "T01": "Suction temperature", - "T02": "Inlet water temp.", - "T03": "Outlet water temp", - "T04": "Coil temperature", - "T05": "Ambient temperature", - "T06": "Antifreeze 1 temperature", - "T07": "Antifreeze 2 temperature", - "T08": "Suction 1 temperature", - "T09": "Suction 2 temperature", - "T10": "Exhaust 1 temperature", - "T11": "Exhaust 2 temperature", - "T12": "Hot water temperature" -} -``` +| Entity Name | Parameter | Platform | Protocol Code? | +| ---------------------------------------------------------- | ---------------- | ------------- | -------------- | +| {HA Device Name} Temperature Unit | temperature_unit | select | False | +| {HA Device Name} Fault | is_fault | binary_sensor | False | +| {HA Device Name} Status | device_status | binary_sensor | False | +| {HA Device Name} Power | Power | binary_sensor | True | +| {HA Device Name} HVAC Mode | Mode | climate | True | +| {HA Device Name} Inlet water setting temperature (cooling) | R01 | sensor | True | +| {HA Device Name} Inlet water setting temperature (Heating) | R02 | sensor | True | +| {HA Device Name} Target setting temperature (Auto mode) | R03 | sensor | True | +| {HA Device Name} Minimum set point in Cooling | R08 | sensor | True | +| {HA Device Name} Maximum Cooling set point | R09 | sensor | True | +| {HA Device Name} Minimum Heating set point | R10 | sensor | True | +| {HA Device Name} Maximum Heating set point | R11 | sensor | True | +| {HA Device Name} Suction temperature | T01 | sensor | True | +| {HA Device Name} Inlet water temp. | T02 | sensor | True | +| {HA Device Name} Outlet water temp | T03 | sensor | True | +| {HA Device Name} Coil temperature | T04 | sensor | True | +| {HA Device Name} Ambient temperature | T05 | sensor | True | +| {HA Device Name} Antifreeze 1 temperature | T06 | sensor | True | +| {HA Device Name} Antifreeze 2 temperature | T07 | sensor | True | +| {HA Device Name} Suction 1 temperature | T08 | sensor | True | +| {HA Device Name} Suction 2 temperature | T09 | sensor | True | +| {HA Device Name} Exhaust 1 temperature | T10 | sensor | True | +| {HA Device Name} Exhaust 2 temperature | T11 | sensor | True | +| {HA Device Name} Hot water temperature | T12 | sensor | True | +| {HA Device Name} 4-way valve output | O06 | sensor | True | +| {HA Device Name} Electronic Expansion valve 1 output | O10 | sensor | True | +| {HA Device Name} Electronic Expansion valve 2 output | O11 | sensor | True | + +[Protocol Codes](https://github.com/radical-squared/aquatemp/blob/master/PROTOCOL_CODES.md) ## Run API over CLI diff --git a/custom_components/aqua_temp/__init__.py b/custom_components/aqua_temp/__init__.py index 98b0ee4..810d895 100644 --- a/custom_components/aqua_temp/__init__.py +++ b/custom_components/aqua_temp/__init__.py @@ -4,7 +4,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from .common.consts import DOMAIN, PLATFORMS +from .common.consts import ALL_ENTITIES, DOMAIN from .managers.aqua_temp_api import AquaTempAPI from .managers.aqua_temp_config_manager import AquaTempConfigManager from .managers.aqua_temp_coordinator import AquaTempCoordinator @@ -27,6 +27,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: api = AquaTempAPI(hass, config_manager) await api.initialize() + await api.update() + coordinator = AquaTempCoordinator(hass, api, config_manager) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator @@ -35,7 +37,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.info("Finished loading integration") - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + platforms = [] + for entity_description in ALL_ENTITIES: + if ( + entity_description.platform not in platforms + and entity_description.platform is not None + ): + platforms.append(entity_description.platform) + + _LOGGER.debug(f"Loading platforms: {platforms}") + + await hass.config_entries.async_forward_entry_setups(entry, platforms) _LOGGER.info("Finished loading components") diff --git a/custom_components/aqua_temp/binary_sensor.py b/custom_components/aqua_temp/binary_sensor.py index a3fef59..12bba21 100644 --- a/custom_components/aqua_temp/binary_sensor.py +++ b/custom_components/aqua_temp/binary_sensor.py @@ -1,18 +1,16 @@ import logging +import sys from typing import Any -from homeassistant.components.binary_sensor import ( - DOMAIN as BINARY_SENSOR_DOMAIN, - BinarySensorDeviceClass, - BinarySensorEntity, - BinarySensorEntityDescription, -) +from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import slugify -from .common.consts import BINARY_SENSOR_CONFIG, DOMAIN +from .common.consts import ALL_ENTITIES, DOMAIN +from .common.entity_descriptions import AquaTempBinarySensorEntityDescription from .managers.aqua_temp_coordinator import AquaTempCoordinator _LOGGER = logging.getLogger(__name__) @@ -22,65 +20,73 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities ): """Set up the sensor platform.""" - coordinator = hass.data[DOMAIN][entry.entry_id] - entities = [] + try: + coordinator = hass.data[DOMAIN][entry.entry_id] + entities = [] + entity_descriptions = [] - for device_code in coordinator.api_data: - for entity_type in BINARY_SENSOR_CONFIG: - entity = AquaTempBinarySensorEntity(device_code, entity_type, coordinator) + for entity_description in ALL_ENTITIES: + if entity_description.platform == Platform.BINARY_SENSOR: + entity_descriptions.append(entity_description) - entities.append(entity) + for device_code in coordinator.api_data: + for entity_description in entity_descriptions: + entity = AquaTempBinarySensorEntity( + device_code, entity_description, coordinator + ) - _LOGGER.debug(f"Setting up binary sensor entities: {entities}") + entities.append(entity) - async_add_entities(entities, True) + _LOGGER.debug(f"Setting up binary sensor entities: {entities}") + + async_add_entities(entities, True) + + except Exception as ex: + exc_type, exc_obj, tb = sys.exc_info() + line_number = tb.tb_lineno + + _LOGGER.error(f"Failed to initialize select, Error: {ex}, Line: {line_number}") class AquaTempBinarySensorEntity(CoordinatorEntity, BinarySensorEntity): """Representation of a sensor.""" - def __init__(self, device_code: str, key: str, coordinator: AquaTempCoordinator): + def __init__( + self, + device_code: str, + entity_description: AquaTempBinarySensorEntityDescription, + coordinator: AquaTempCoordinator, + ): super().__init__(coordinator) - self._key = key self._device_code = device_code self._api_data = self.coordinator.api_data[self._device_code] self._config_data = self.coordinator.config_data - entity_configuration = BINARY_SENSOR_CONFIG.get(key) - - entity_type_name = entity_configuration.get("name") - device_class = entity_configuration.get("device_class") - attributes_keys = entity_configuration.get("attributes") - on_value = entity_configuration.get("value") - device_info = coordinator.get_device(device_code) device_name = device_info.get("name") - entity_name = f"{device_name} {entity_type_name}" + entity_name = f"{device_name} {entity_description.name}" slugify_name = slugify(entity_name) device_id = self._api_data.get("device_id") - slugify_uid = slugify(f"{BINARY_SENSOR_DOMAIN}_{slugify_name}_{device_id}") + unique_id = slugify(f"{entity_description.platform}_{slugify_name}_{device_id}") - entity_description = BinarySensorEntityDescription(slugify_uid) - entity_description.name = entity_name - entity_description.device_class = BinarySensorDeviceClass.CONNECTIVITY + self.entity_description: AquaTempBinarySensorEntityDescription = ( + entity_description + ) - self.entity_description = entity_description self._attr_device_info = device_info self._attr_name = entity_name - self._attr_unique_id = slugify_uid - self._attr_device_class = device_class - self._on_value = on_value - self._attributes_keys = attributes_keys + self._attr_unique_id = unique_id + self._attr_device_class = entity_description.device_class @property def state_attributes(self) -> dict[str, Any] | None: attributes = {} - if self._attributes_keys is not None: - for attribute_key in self._attributes_keys: + if self.entity_description.attributes is not None: + for attribute_key in self.entity_description.attributes: value = self._api_data.get(attribute_key) attributes[attribute_key] = value @@ -90,8 +96,8 @@ def state_attributes(self) -> dict[str, Any] | None: @property def is_on(self) -> bool | None: """Return true if the binary sensor is on.""" - status = self._api_data.get(self._key) + status = self._api_data.get(self.entity_description.key) - is_on = status == self._on_value + is_on = status == self.entity_description.on_value return is_on diff --git a/custom_components/aqua_temp/climate.py b/custom_components/aqua_temp/climate.py index 53d107e..1db9200 100644 --- a/custom_components/aqua_temp/climate.py +++ b/custom_components/aqua_temp/climate.py @@ -3,33 +3,27 @@ import logging import sys -from homeassistant.components.climate import ClimateEntity, ClimateEntityDescription +from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate.const import ( - DOMAIN as CLIMATE_DOMAIN, FAN_AUTO, SUPPORT_FAN_MODE, SUPPORT_TARGET_TEMPERATURE, HVACMode, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import slugify from .common.consts import ( - BINARY_SENSOR_CONFIG, + ALL_ENTITIES, DOMAIN, - FAN_MODE_MAPPING, HVAC_MODE_MAPPING, - HVAC_MODE_MAX_TEMP, - HVAC_MODE_MIN_TEMP, - HVAC_PC_MAPPING, MANUAL_MUTE_MAPPING, - PROTOCOL_CODE_CURRENT_TEMP, - PROTOCOL_CODE_FAN_MODE, - PROTOCOL_CODE_HVAC_MODE, - PROTOCOL_CODE_POWER, + POWER_MODE_ON, ) +from .common.entity_descriptions import AquaTempClimateEntityDescription from .managers.aqua_temp_coordinator import AquaTempCoordinator _LOGGER = logging.getLogger(__name__) @@ -39,15 +33,31 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities ): """Set up the climate platform.""" - coordinator = hass.data[DOMAIN][entry.entry_id] - climate_entities = [ - AquaTempClimateEntity(device_code, coordinator) - for device_code in coordinator.api_data - ] + try: + coordinator = hass.data[DOMAIN][entry.entry_id] + entities = [] + entity_descriptions = [] - _LOGGER.debug(f"Setting up climate entities: {climate_entities}") + for entity_description in ALL_ENTITIES: + if entity_description.platform == Platform.CLIMATE: + entity_descriptions.append(entity_description) - async_add_entities(climate_entities, True) + for device_code in coordinator.api_data: + for entity_description in entity_descriptions: + entity = AquaTempClimateEntity( + device_code, entity_description, coordinator + ) + + entities.append(entity) + + _LOGGER.debug(f"Setting up climate entities: {entities}") + + async_add_entities(entities, True) + except Exception as ex: + exc_type, exc_obj, tb = sys.exc_info() + line_number = tb.tb_lineno + + _LOGGER.error(f"Failed to initialize climate, Error: {ex}, Line: {line_number}") class AquaTempClimateEntity(CoordinatorEntity, ClimateEntity, ABC): @@ -55,7 +65,12 @@ class AquaTempClimateEntity(CoordinatorEntity, ClimateEntity, ABC): _attributes: dict - def __init__(self, device_code: str, coordinator: AquaTempCoordinator): + def __init__( + self, + device_code: str, + entity_description: AquaTempClimateEntityDescription, + coordinator: AquaTempCoordinator, + ): """Initialize the climate entity.""" super().__init__(coordinator) @@ -65,41 +80,28 @@ def __init__(self, device_code: str, coordinator: AquaTempCoordinator): device_info = coordinator.get_device(device_code) device_name = device_info.get("name") - entity_name = device_name + entity_name = f"{device_name}" - device_id = self._api_data.get("device_id") - slugify_uid = slugify(f"{CLIMATE_DOMAIN}_{device_id}") + slugify_name = slugify(entity_name) - entity_description = ClimateEntityDescription(slugify_uid) - entity_description.name = entity_name + device_id = self._api_data.get("device_id") + unique_id = slugify(f"{entity_description.platform}_{slugify_name}_{device_id}") - self.entity_description = entity_description + self.entity_description: AquaTempClimateEntityDescription = entity_description self._device_code = device_code - self._attributes = {} - self._attr_device_info = device_info self._attr_supported_features = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE - self._attr_fan_modes = list(FAN_MODE_MAPPING.keys()) - self._attr_hvac_modes = list(HVAC_MODE_MAPPING.keys()) + self._attr_fan_modes = entity_description.fan_modes + self._attr_hvac_modes = entity_description.hvac_modes self._attr_hvac_mode = HVACMode.OFF self._attr_fan_mode = FAN_AUTO self._attr_temperature_unit = self.coordinator.get_temperature_unit(device_code) self._attr_name = entity_name - self._attr_unique_id = slugify_uid - - @property - def extra_state_attributes(self): - """Return entity specific state attributes.""" - return self._attributes - - @property - def device_state_attributes(self): - """Return device specific state attributes.""" - return self._attributes + self._attr_unique_id = unique_id async def async_set_temperature(self, **kwargs): """Set new target temperature.""" @@ -121,6 +123,8 @@ async def async_set_temperature(self, **kwargs): async def async_set_hvac_mode(self, hvac_mode): """Set new target hvac mode.""" try: + _LOGGER.debug(f"Set HVAC Mode to: {hvac_mode}") + await self.coordinator.set_hvac_mode(self._device_code, hvac_mode) await self.coordinator.async_request_refresh() @@ -134,6 +138,8 @@ async def async_set_hvac_mode(self, hvac_mode): async def async_set_fan_mode(self, fan_mode): """Set new target fan mode.""" try: + _LOGGER.debug(f"Set Fan Mode to: {fan_mode}") + await self.coordinator.set_fan_mode(self._device_code, fan_mode) await self.coordinator.async_request_refresh() @@ -146,23 +152,36 @@ async def async_set_fan_mode(self, fan_mode): def _handle_coordinator_update(self) -> None: """Fetch new state data for the sensor.""" - mode = self._api_data.get(PROTOCOL_CODE_HVAC_MODE) - power = self._api_data.get(PROTOCOL_CODE_POWER) - manual_mute = self._api_data.get(PROTOCOL_CODE_FAN_MODE) - current_temperature = self._api_data.get(PROTOCOL_CODE_CURRENT_TEMP) + entity_description = self.entity_description + + mode = self._api_data.get(entity_description.key) + power = self._api_data.get(entity_description.power_key) + manual_mute = self._api_data.get(entity_description.fan_mode_key) + current_temperature = self._api_data.get( + entity_description.current_temperature_key + ) + + is_power_on = power == POWER_MODE_ON + hvac_mode = HVACMode.OFF - power_config = BINARY_SENSOR_CONFIG.get(PROTOCOL_CODE_POWER) - power_on_value = power_config.get("value") + if is_power_on: + for key in HVAC_MODE_MAPPING: + pc_hvac_mode = HVAC_MODE_MAPPING[key] + if pc_hvac_mode == mode: + hvac_mode = key + break - is_power_on = power == power_on_value + if entity_description.minimum_temperature_keys is not None: + min_temp_key = entity_description.minimum_temperature_keys.get(hvac_mode) + min_temp = self._api_data.get(min_temp_key, 0) - hvac_mode = HVAC_PC_MAPPING.get(mode) if is_power_on else HVACMode.OFF + self._attr_min_temp = float(str(min_temp)) - min_temp_key = HVAC_MODE_MIN_TEMP.get(hvac_mode) - max_temp_key = HVAC_MODE_MAX_TEMP.get(hvac_mode) + if entity_description.maximum_temperature_keys is not None: + max_temp_key = entity_description.maximum_temperature_keys.get(hvac_mode) + max_temp = self._api_data.get(max_temp_key, 0) - min_temp = self._api_data.get(min_temp_key, 0) - max_temp = self._api_data.get(max_temp_key, 0) + self._attr_max_temp = float(str(max_temp)) hvac_mode_code = HVAC_MODE_MAPPING.get(hvac_mode) hvac_mode_param = f"R0{hvac_mode_code}" @@ -174,17 +193,17 @@ def _handle_coordinator_update(self) -> None: self._attr_hvac_mode = hvac_mode self._attr_fan_mode = MANUAL_MUTE_MAPPING.get(manual_mute) - self._attr_target_temperature = float(str(target_temperature)) - self._attr_min_temp = float(str(min_temp)) - self._attr_max_temp = float(str(max_temp)) + self._attr_target_temperature = target_temperature if current_temperature is not None: self._attr_current_temperature = float(str(current_temperature)) - _LOGGER.debug(f"{PROTOCOL_CODE_HVAC_MODE}: {mode}") - _LOGGER.debug(f"{PROTOCOL_CODE_POWER}: {power}") - _LOGGER.debug(f"{PROTOCOL_CODE_FAN_MODE}: {manual_mute}") - _LOGGER.debug(f"{PROTOCOL_CODE_CURRENT_TEMP}: {current_temperature}") + _LOGGER.debug(f"{entity_description.key}: {mode}") + _LOGGER.debug(f"{entity_description.power_key}: {power}") + _LOGGER.debug(f"{entity_description.fan_mode_key}: {manual_mute}") + _LOGGER.debug( + f"{entity_description.current_temperature_key}: {current_temperature}" + ) _LOGGER.debug(f"is_power_on: {is_power_on}") _LOGGER.debug(f"_attr_hvac_mode: {self._attr_hvac_mode}") diff --git a/custom_components/aqua_temp/common/consts.py b/custom_components/aqua_temp/common/consts.py index c3997c5..a249f6a 100644 --- a/custom_components/aqua_temp/common/consts.py +++ b/custom_components/aqua_temp/common/consts.py @@ -1,11 +1,14 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.climate.const import FAN_AUTO, FAN_LOW, HVACMode -from homeassistant.const import ( - CONF_TEMPERATURE_UNIT, - STATE_OFF, - STATE_ON, - Platform, - UnitOfTemperature, +from homeassistant.components.sensor import SensorDeviceClass +from homeassistant.const import EntityCategory, UnitOfTemperature + +from .entity_descriptions import ( + AquaTempBinarySensorEntityDescription, + AquaTempClimateEntityDescription, + AquaTempEntityDescription, + AquaTempSelectEntityDescription, + AquaTempSensorEntityDescription, ) DOMAIN = "aqua_temp" @@ -19,33 +22,18 @@ CONTROL_PATH = "/cloudservice/api/app/device/control.json" GETFAULT_PATH = "/cloudservice/api/app/device/getFaultDataByDeviceCode.json" - -HVAC_PC_COOL = "0" -HVAC_PC_HEAT = "1" -HVAC_PC_AUTO = "2" - -MODE_SET_TEMPERATURE_OFF = "0" -MODE_SET_TEMPERATURE_COOL = "1" -MODE_SET_TEMPERATURE_HEAT = "2" -MODE_SET_TEMPERATURE_AUTO = "3" +MODE_TEMPERATURE_OFF = "0" +MODE_TEMPERATURE_COOL = "1" +MODE_TEMPERATURE_HEAT = "2" +MODE_TEMPERATURE_AUTO = "3" HVAC_MODE_MAPPING = { - HVACMode.OFF: MODE_SET_TEMPERATURE_OFF, - HVACMode.COOL: MODE_SET_TEMPERATURE_COOL, - HVACMode.HEAT: MODE_SET_TEMPERATURE_HEAT, - HVACMode.AUTO: MODE_SET_TEMPERATURE_AUTO, -} - -HVAC_PC_MAPPING = { - HVAC_PC_COOL: HVACMode.COOL, - HVAC_PC_HEAT: HVACMode.HEAT, - HVAC_PC_AUTO: HVACMode.AUTO, + HVACMode.OFF: MODE_TEMPERATURE_OFF, + HVACMode.COOL: MODE_TEMPERATURE_COOL, + HVACMode.HEAT: MODE_TEMPERATURE_HEAT, + HVACMode.AUTO: MODE_TEMPERATURE_AUTO, } -HVAC_MODE_MIN_TEMP = {HVACMode.COOL: "R08", HVACMode.HEAT: "R10"} - -HVAC_MODE_MAX_TEMP = {HVACMode.COOL: "R09", HVACMode.HEAT: "R11"} - MANUAL_MUTE_AUTO = "0" MANUAL_MUTE_LOW = "1" @@ -54,112 +42,664 @@ POWER_MODE_OFF = "0" POWER_MODE_ON = "1" -POWER_MODE_MAPPING = {POWER_MODE_OFF: STATE_OFF, POWER_MODE_ON: STATE_ON} - MANUAL_MUTE_MAPPING = {MANUAL_MUTE_AUTO: FAN_AUTO, MANUAL_MUTE_LOW: FAN_LOW} -PROTOCOL_CODE_POWER = "Power" -PROTOCOL_CODE_HVAC_MODE = "Mode" -PROTOCOL_CODE_FAN_MODE = "Manual-mute" -PROTOCOL_CODE_CURRENT_TEMP = "T02" - -PROTOCOL_CODES = { - PROTOCOL_CODE_POWER: None, - PROTOCOL_CODE_HVAC_MODE: None, - PROTOCOL_CODE_FAN_MODE: None, - "2074": None, - "2075": None, - "2076": None, - "2077": None, - "Set_Temp": None, - "1158": None, - "1159": None, - "D01": "Start defrosting temperature", - "D02": "End defrost temperature", - "D03": "defrosting cycle", - "D04": "Maximum defrosting time", - "D05": "Minimum defrosting time", - "D06": "Defrost mode", - "D07": "Defrost heater control", - "D08": "Defrost AUTO set", - "E01": "EEV 1 mode", - "E02": "Super heat 1", - "E03": "Initial place 1", - "E04": "EEV 2 mode", - "E05": "Super heat 2", - "E06": "Initial place 2", - "E07": "Minimum place", - "E08": "Defrost place", - "E09": "Cooling place", - "E10": "Low exhaust", - "E11": "High exhaust", - "F01": "Fan parameter", - "F02": "Coil temperature in high speed fan mode (Cooling)", - "F03": "Coil temperature in low speed fan mode (Cooling)", - "F04": "Coil temperature when the fan stop (Cooling)", - "F05": "Coil temperature in high speed fan mode (Heating)", - "F06": "Coil temperature in low speed fan mode (Heating)", - "F07": "Coil temperature when the fan stop (Heating)", - "F08": "Fan start low speed running time", - "F09": "Fan stop low speed running time", - "F10": "Fan quantity", - "F11": "Fan speed control temp.", - "F17": None, - "H02": "System quantity", - "H03": "4-way valve polarity", - "H04": "4-way valve control", - "R01": "Inlet water setting temperature (cooling)", - "R02": "Inlet water setting temperature (Heating)", - "R03": "Target setting temperature (Auto mode)", - "R08": "Minimum set point in Cooling", - "R09": "Maximum Cooling set point", - "R10": "Minimum Heating set point", - "R11": "Maximum Heating set point", - "U02": "Pulse", - "T01": "Suction temperature", - "T02": "Inlet water temp.", - "T03": "Outlet water temp", - "T04": "Coil temperature", - "T05": "Ambient temperature", - "T06": "Antifreeze 1 temperature", - "T07": "Antifreeze 2 temperature", - "T08": "Suction 1 temperature", - "T09": "Suction 2 temperature", - "T10": "Exhaust 1 temperature", - "T11": "Exhaust 2 temperature", - "T12": "Hot water temperature", - "T14": None, -} - -BINARY_SENSOR_CONFIG = { - "Power": { - "value": "1", - "name": "Power", - "device_class": BinarySensorDeviceClass.POWER, - "attributes": None, - }, - "device_status": { - "value": "ONLINE", - "name": "Status", - "device_class": BinarySensorDeviceClass.CONNECTIVITY, - "attributes": None, - }, - "is_fault": { - "value": True, - "name": "Fault", - "device_class": BinarySensorDeviceClass.PROBLEM, - "attributes": ["fault"], - }, -} - HEADERS = {"Content-Type": "application/json; charset=utf-8"} -CONFIG_FIELDS = [CONF_TEMPERATURE_UNIT] - DATA_ITEM_API = "data" -DATA_ITEM_FAULT = "fault" DATA_ITEM_CONFIG = "configuration" -DEFAULT_TEMPERATURE_UNIT = UnitOfTemperature.CELSIUS - -PLATFORMS = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.SENSOR, Platform.SELECT] +ALL_ENTITIES = [ + AquaTempSelectEntityDescription( + key="temperature_unit", + name="Temperature Unit", + category="Status parameters", + options=[UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT], + entity_category=EntityCategory.CONFIG, + is_protocol_code=False, + ), + AquaTempBinarySensorEntityDescription( + key="is_fault", + name="Fault", + category="Status parameters", + on_value=POWER_MODE_ON, + is_protocol_code=False, + device_class=BinarySensorDeviceClass.PROBLEM, + attributes=["fault"], + ), + AquaTempBinarySensorEntityDescription( + key="device_status", + name="Status", + category="Status parameters", + on_value="ONLINE", + is_protocol_code=False, + device_class=BinarySensorDeviceClass.CONNECTIVITY, + ), + AquaTempBinarySensorEntityDescription( + key="Power", + name="Power", + category="Control parameters", + on_value=POWER_MODE_ON, + device_class=BinarySensorDeviceClass.POWER, + ), + AquaTempClimateEntityDescription( + key="Mode", + name="HVAC Mode", + category="Control parameters", + fan_mode_key="Manual-mute", + current_temperature_key="T02", + power_key="Power", + fan_modes=list(FAN_MODE_MAPPING.keys()), + hvac_modes=list(HVAC_MODE_MAPPING.keys()), + minimum_temperature_keys={HVACMode.COOL: "R08", HVACMode.HEAT: "R10"}, + maximum_temperature_keys={HVACMode.COOL: "R09", HVACMode.HEAT: "R11"}, + ), + AquaTempEntityDescription( + key="Manual-mute", name="Fan Mode", category="Control parameters" + ), + AquaTempEntityDescription(key="2074", name="2074", category="Control parameters"), + AquaTempEntityDescription(key="2075", name="2075", category="Control parameters"), + AquaTempEntityDescription(key="2076", name="2076", category="Control parameters"), + AquaTempEntityDescription(key="2077", name="2077", category="Control parameters"), + AquaTempEntityDescription( + key="Set_Temp", name="Set Temperature", category="Control parameters" + ), + AquaTempEntityDescription(key="1158", name="1158", category="Control parameters"), + AquaTempEntityDescription(key="1159", name="1159", category="Control parameters"), + AquaTempEntityDescription( + key="/01", + name="Whether enable system 1 high pressure switch", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/02", + name="Whether enable system 2 high pressure switch", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/03", + name="Whether enable system 1 low pressure switch", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/04", + name="Whether enable system 2 low pressure switch", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/05", + name="Whether enable phase monitor protection", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/06", + name="Whether enable water flow protection", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/07", + name="Whether enable electrical heater Overload protection", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/08", + name="Whether enable system 1 overload protection", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/09", + name="Whether enable system 2 overload protection", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/10", + name="Whether enable remote on/off switch", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="/11", name="Whether enable system protect", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/12", name="Whether enable Outlet probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/13", name="Whether enable Coil 1 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/14", name="Whether enable Coil 2 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/15", name="Whether enable Ambient probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/16", name="Whether enable deice 1 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/17", name="Whether enable deice 2 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/18", name="Whether enable Suction 1 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/19", name="Whether enable Suction 2 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/20", name="Whether enable exhaust 1 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/21", name="Whether enable exhaust 2 probe", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/22", name="Whether enable ΔT1 protect", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/23", name="Whether enable Cooling mode", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/24", name="Whether enable Economic heating", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/25", name="Whether enable AUTO mode", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/26", name="Whether enable Heating mode", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/27", name="Whether enable High demand", category="Hardware parameter" + ), + AquaTempEntityDescription( + key="/28", + name="Whether enable heat recovery temperature", + category="Hardware parameter", + ), + AquaTempEntityDescription( + key="A01", + name="High pressure alarm time delay", + category="Protection parameter", + ), + AquaTempEntityDescription( + key="A02", name="Low pressure alarm time delay", category="Protection parameter" + ), + AquaTempEntityDescription( + key="A03", name="Stop unit air temperature", category="Protection parameter" + ), + AquaTempEntityDescription( + key="A04", + name="Antifreeze setting temperature", + category="Protection parameter", + ), + AquaTempEntityDescription( + key="A05", + name="Antifreeze differential protection", + category="Protection parameter", + ), + AquaTempEntityDescription( + key="A06", + name="Discharge temp. protection setting", + category="Protection parameter", + ), + AquaTempEntityDescription( + key="A07", + name="Discharge temp. differential protection", + category="Protection parameter", + ), + AquaTempEntityDescription( + key="A08", + name="Inlet/Out differential protection setting value", + category="Protection parameter", + ), + AquaTempEntityDescription( + key="A09", + name="Start spraying air temperature", + category="Protection parameter", + ), + AquaTempEntityDescription( + key="C01", name="Minimum on time", category="Compressor parameter" + ), + AquaTempEntityDescription( + key="C02", name="Minimum off time", category="Compressor parameter" + ), + AquaTempEntityDescription( + key="C03", + name="Delay between starts of the 2 compressors", + category="Compressor parameter", + ), + AquaTempEntityDescription( + key="C04", name="Rotation", category="Compressor parameter" + ), + AquaTempEntityDescription( + key="D01", name="Start defrosting temperature", category="Defrost parameter" + ), + AquaTempEntityDescription( + key="D02", name="End defrost temperature", category="Defrost parameter" + ), + AquaTempEntityDescription( + key="D03", name="defrosting cycle", category="Defrost parameter" + ), + AquaTempEntityDescription( + key="D04", name="Maximum defrosting time", category="Defrost parameter" + ), + AquaTempEntityDescription( + key="D05", name="Minimum defrosting time", category="Defrost parameter" + ), + AquaTempEntityDescription( + key="D06", name="Defrost mode", category="Defrost parameter" + ), + AquaTempEntityDescription( + key="D07", name="Defrost heater control", category="Defrost parameter" + ), + AquaTempEntityDescription( + key="D08", name="Defrost AUTO set", category="Defrost parameter" + ), + AquaTempEntityDescription(key="E01", name="EEV 1 mode", category="EEV parameter"), + AquaTempEntityDescription(key="E02", name="Super heat 1", category="EEV parameter"), + AquaTempEntityDescription( + key="E03", name="Initial place 1", category="EEV parameter" + ), + AquaTempEntityDescription(key="E04", name="EEV 2 mode", category="EEV parameter"), + AquaTempEntityDescription(key="E05", name="Super heat 2", category="EEV parameter"), + AquaTempEntityDescription( + key="E06", name="Initial place 2", category="EEV parameter" + ), + AquaTempEntityDescription( + key="E07", name="Minimum place", category="EEV parameter" + ), + AquaTempEntityDescription( + key="E08", name="Defrost place", category="EEV parameter" + ), + AquaTempEntityDescription( + key="E09", name="Cooling place", category="EEV parameter" + ), + AquaTempEntityDescription(key="E10", name="Low exhaust", category="EEV parameter"), + AquaTempEntityDescription(key="E11", name="High exhaust", category="EEV parameter"), + AquaTempEntityDescription( + key="F01", name="Fan parameter", category="Fan parameter" + ), + AquaTempEntityDescription( + key="F02", + name="Coil temperature in high speed fan mode (Cooling)", + category="Fan parameter", + ), + AquaTempEntityDescription( + key="F03", + name="Coil temperature in low speed fan mode (Cooling)", + category="Fan parameter", + ), + AquaTempEntityDescription( + key="F04", + name="Coil temperature when the fan stop (Cooling)", + category="Fan parameter", + ), + AquaTempEntityDescription( + key="F05", + name="Coil temperature in high speed fan mode (Heating)", + category="Fan parameter", + ), + AquaTempEntityDescription( + key="F06", + name="Coil temperature in low speed fan mode (Heating)", + category="Fan parameter", + ), + AquaTempEntityDescription( + key="F07", + name="Coil temperature when the fan stop (Heating)", + category="Fan parameter", + ), + AquaTempEntityDescription( + key="F08", name="Fan start low speed running time", category="Fan parameter" + ), + AquaTempEntityDescription( + key="F09", name="Fan stop low speed running time", category="Fan parameter" + ), + AquaTempEntityDescription(key="F10", name="Fan quantity", category="Fan parameter"), + AquaTempEntityDescription( + key="F11", name="Fan speed control temp.", category="Fan parameter" + ), + AquaTempEntityDescription( + key="H01", name="Automatic restarting", category="System parameter" + ), + AquaTempEntityDescription( + key="H02", name="System quantity", category="System parameter" + ), + AquaTempEntityDescription( + key="H03", name="4-way valve polarity", category="System parameter" + ), + AquaTempEntityDescription( + key="H04", name="4-way valve control", category="System parameter" + ), + AquaTempEntityDescription(key="H05", name="Model", category="System parameter"), + AquaTempEntityDescription(key="H06", name="Type", category="System parameter"), + AquaTempEntityDescription(key="H07", name="Class", category="System parameter"), + AquaTempEntityDescription( + key="H08", name="Capacity Control", category="System parameter" + ), + AquaTempEntityDescription( + key="H09", name="Coil sensor function", category="System parameter" + ), + AquaTempEntityDescription( + key="H10", name="Physical address", category="System parameter" + ), + AquaTempEntityDescription(key="H11", name="Baud rate", category="System parameter"), + AquaTempEntityDescription( + key="H12", name="Parity bit", category="System parameter" + ), + AquaTempEntityDescription(key="H13", name="Stop bit", category="System parameter"), + AquaTempEntityDescription( + key="R01", name="Water pump mode", category="Water pump parameter" + ), + AquaTempEntityDescription( + key="P02", name="Water pump running cycle", category="Water pump parameter" + ), + AquaTempEntityDescription( + key="P03", name="Water pump running time", category="Water pump parameter" + ), + AquaTempEntityDescription( + key="P04", + name="Delay in switching on the compressor after switching on the pump", + category="Water pump parameter", + ), + AquaTempEntityDescription( + key="P05", name="Filter", category="Water pump parameter" + ), + AquaTempEntityDescription( + key="P06", name="Start filter 1", category="Water pump parameter" + ), + AquaTempEntityDescription( + key="P07", name="Stop filter 1", category="Water pump parameter" + ), + AquaTempEntityDescription( + key="P08", name="Start filter 2", category="Water pump parameter" + ), + AquaTempEntityDescription( + key="P09", name="Stop filter 2", category="Water pump parameter" + ), + AquaTempSensorEntityDescription( + key="R01", + name="Inlet water setting temperature (cooling)", + category="Temp parameter", + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempSensorEntityDescription( + key="R02", + name="Inlet water setting temperature (Heating)", + category="Temp parameter", + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempSensorEntityDescription( + key="R03", + name="Target setting temperature (Auto mode)", + category="Temp parameter", + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempEntityDescription( + key="R04", name="Cooling differential", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R05", name="Cooling stop differential", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R06", name="Heating differential", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R07", name="Heating stop differential", category="Temp parameter" + ), + AquaTempSensorEntityDescription( + key="R08", + name="Minimum set point in Cooling", + category="Temp parameter", + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempSensorEntityDescription( + key="R09", + name="Maximum Cooling set point", + category="Temp parameter", + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempSensorEntityDescription( + key="R10", + name="Minimum Heating set point", + category="Temp parameter", + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempSensorEntityDescription( + key="R11", + name="Maximum Heating set point", + category="Temp parameter", + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempEntityDescription( + key="R12", name="Electrical ΔT6", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R13", name="Electrical Ambient", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R14", name="Electrical Delay", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R15", name="Electrical force", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R16", name="Compensation", category="Temp parameter" + ), + AquaTempEntityDescription(key="R17", name="Maximum ΔT7", category="Temp parameter"), + AquaTempEntityDescription( + key="R18", name="Cooling compensation constant", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R19", + name="Cooling compensation start air temperature", + category="Temp parameter", + ), + AquaTempEntityDescription( + key="R20", + name="Heating compensation start air temperature", + category="Temp parameter", + ), + AquaTempEntityDescription( + key="R21", name="Whether enable heat recovery", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R22", + name="The target temperature Of heat recovery", + category="Temp parameter", + ), + AquaTempEntityDescription( + key="R23", + name="Temperature differential of heat recovery", + category="Temp parameter", + ), + AquaTempEntityDescription( + key="R24", name="Temperature to stop heat recovery", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R25", + name="Temperature differential to stop heat recovery", + category="Temp parameter", + ), + AquaTempEntityDescription( + key="R26", name="Electric heater mode", category="Temp parameter" + ), + AquaTempEntityDescription( + key="R27", + name="Ambient temperature to start up antifreezing heater", + category="Temp parameter", + ), + AquaTempEntityDescription( + key="R28", + name="Temperature differential to stop antifreezing heater", + category="Temp parameter", + ), + AquaTempEntityDescription( + key="U01", name="Flow meter", category="Water flow parameter" + ), + AquaTempEntityDescription(key="U02", name="Pulse", category="Water flow parameter"), + AquaTempEntityDescription( + key="U03", name="Flow protect", category="Water flow parameter" + ), + AquaTempEntityDescription( + key="U04", name="Flow alarm", category="Water flow parameter" + ), + AquaTempEntityDescription( + key="S01", name="System1 HP", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S02", name="System2 HP", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S03", name="System1 LP", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S04", name="System2 LP", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S05", name="Phase monitor", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S06", name="Water Flow switch", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S07", name="Electrical heater overload", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S08", name="COMP1 overload", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S09", name="COMP2 overload", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S10", name="On/Off switch", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S11", name="Mode switch", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S12", name="System protect", category="Switch state checking" + ), + AquaTempEntityDescription( + key="S13", name="Water flow", category="Switch state checking" + ), + AquaTempSensorEntityDescription( + key="T01", + name="Suction temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T02", + name="Inlet water temp.", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T03", + name="Outlet water temp", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T04", + name="Coil temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T05", + name="Ambient temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T06", + name="Antifreeze 1 temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T07", + name="Antifreeze 2 temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T08", + name="Suction 1 temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T09", + name="Suction 2 temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T10", + name="Exhaust 1 temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T11", + name="Exhaust 2 temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempSensorEntityDescription( + key="T12", + name="Hot water temperature", + category="Temp. checking", + device_class=SensorDeviceClass.TEMPERATURE, + ), + AquaTempEntityDescription(key="T14", name="None", category="Temp. checking"), + AquaTempEntityDescription( + key="O01", name="Compressor 1 output", category="Load output" + ), + AquaTempEntityDescription( + key="O02", name="Compressor 2 output", category="Load output" + ), + AquaTempEntityDescription( + key="O03", name="Fan output (High speed)", category="Load output" + ), + AquaTempEntityDescription( + key="O04", name="Fan output (Low speed)", category="Load output" + ), + AquaTempEntityDescription( + key="O05", name="Circulate pump output", category="Load output" + ), + AquaTempSensorEntityDescription( + key="O06", + name="4-way valve output", + category="Load output", + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempEntityDescription( + key="O07", name="Heat element output", category="Load output" + ), + AquaTempEntityDescription(key="O08", name="Alarm output", category="Load output"), + AquaTempEntityDescription( + key="O09", name="Spray valve output", category="Load output" + ), + AquaTempSensorEntityDescription( + key="O10", + name="Electronic Expansion valve 1 output", + category="Load output", + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + AquaTempSensorEntityDescription( + key="O11", + name="Electronic Expansion valve 2 output", + category="Load output", + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + ), +] diff --git a/custom_components/aqua_temp/common/entity_descriptions.py b/custom_components/aqua_temp/common/entity_descriptions.py new file mode 100644 index 0000000..1c59eda --- /dev/null +++ b/custom_components/aqua_temp/common/entity_descriptions.py @@ -0,0 +1,52 @@ +from dataclasses import dataclass + +from homeassistant.components.binary_sensor import BinarySensorEntityDescription +from homeassistant.components.climate import ClimateEntityDescription, HVACMode +from homeassistant.components.select import SelectEntityDescription +from homeassistant.components.sensor import SensorEntityDescription +from homeassistant.const import Platform +from homeassistant.helpers.entity import EntityDescription + + +@dataclass(slots=True) +class AquaTempEntityDescription(EntityDescription): + category: str | None = None + platform: Platform | None = None + is_protocol_code: bool = True + + +@dataclass(slots=True) +class AquaTempClimateEntityDescription( + ClimateEntityDescription, AquaTempEntityDescription +): + platform: Platform | None = Platform.CLIMATE + power_key: str | None = None + fan_mode_key: str | None = None + current_temperature_key: str | None = None + fan_modes: list[str] | None = None + hvac_modes: list[HVACMode] | list[str] = None + minimum_temperature_keys: dict[HVACMode, str] | None = None + maximum_temperature_keys: dict[HVACMode, str] | None = None + + +@dataclass(slots=True) +class AquaTempBinarySensorEntityDescription( + BinarySensorEntityDescription, AquaTempEntityDescription +): + platform: Platform | None = Platform.BINARY_SENSOR + on_value: str | bool | None = None + attributes: list[str] | None = None + + +@dataclass(slots=True) +class AquaTempSensorEntityDescription( + SensorEntityDescription, AquaTempEntityDescription +): + platform: Platform | None = Platform.SENSOR + + +@dataclass(slots=True) +class AquaTempSelectEntityDescription( + SelectEntityDescription, AquaTempEntityDescription +): + platform: Platform | None = Platform.SELECT diff --git a/custom_components/aqua_temp/managers/aqua_temp_api.py b/custom_components/aqua_temp/managers/aqua_temp_api.py index e2fa02f..9688f4d 100644 --- a/custom_components/aqua_temp/managers/aqua_temp_api.py +++ b/custom_components/aqua_temp/managers/aqua_temp_api.py @@ -4,12 +4,13 @@ from aiohttp import ClientSession -from homeassistant.components.climate.const import HVAC_MODE_OFF +from homeassistant.components.climate.const import HVAC_MODE_OFF, HVACMode from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_create_clientsession from ..common.consts import ( + ALL_ENTITIES, CONTROL_PATH, DEVICELIST_PATH, FAN_MODE_MAPPING, @@ -19,10 +20,8 @@ HEADERS, HVAC_MODE_MAPPING, LOGIN_PATH, - MODE_SET_TEMPERATURE_HEAT, POWER_MODE_OFF, POWER_MODE_ON, - PROTOCOL_CODES, SERVER_URL, ) from ..common.exceptions import LoginError, OperationFailedException @@ -52,6 +51,7 @@ def __init__( self._hass = hass self._headers = HEADERS self._config_manager = config_manager + self.protocol_codes = [] @property def is_connected(self): @@ -62,6 +62,13 @@ def is_connected(self): async def initialize(self): try: if not self.is_connected: + for entity_description in ALL_ENTITIES: + if ( + entity_description.key not in self.protocol_codes + and entity_description.is_protocol_code + ): + self.protocol_codes.append(entity_description.key) + if self._hass is None: self._session = ClientSession() else: @@ -77,6 +84,10 @@ async def initialize(self): f"Failed to initialize session, Error: {ex}, Line: {line_number}" ) + async def terminate(self): + if self._hass is None: + await self._session.close() + async def validate(self): await self.initialize() @@ -100,20 +111,25 @@ async def update(self): _LOGGER.error(f"Error fetching data. Reconnecting, Error: {ex}") async def set_hvac_mode(self, device_code: str, hvac_mode): - if hvac_mode == HVAC_MODE_OFF: + if hvac_mode == HVACMode.OFF: await self._set_power_mode(device_code, POWER_MODE_OFF) else: - is_on = False + device_data = self.data.get(device_code) + current_power = device_data.get("Power") + + is_on = current_power == POWER_MODE_ON if not is_on: await self._set_power_mode(device_code, POWER_MODE_ON) - await self._set_hvac_mode(device_code, hvac_mode) + pc_hvac_mode = HVAC_MODE_MAPPING.get(hvac_mode) + + await self._set_hvac_mode(device_code, pc_hvac_mode) async def set_temperature(self, device_code: str, hvac_mode, temperature): """Set new target temperature.""" - hvac_mode_mapping = HVAC_MODE_MAPPING.get(hvac_mode, MODE_SET_TEMPERATURE_HEAT) + hvac_mode_mapping = HVAC_MODE_MAPPING.get(hvac_mode) mode = f"R0{hvac_mode_mapping}" request_data = {mode: temperature, "Set_Temp": temperature} @@ -162,7 +178,7 @@ async def _perform_action(self, device_code: str, request_data: dict): raise OperationFailedException(operation, value, error_msg) async def _fetch_data(self, device_code: str): - data = self._get_request_params(device_code, list(PROTOCOL_CODES.keys())) + data = self._get_request_params(device_code, self.protocol_codes) data_response = await self._post_request(GETDATABYCODE_PATH, data) object_result_items = data_response.get("object_result", []) diff --git a/custom_components/aqua_temp/managers/aqua_temp_config_manager.py b/custom_components/aqua_temp/managers/aqua_temp_config_manager.py index 06ebe6f..58fe900 100644 --- a/custom_components/aqua_temp/managers/aqua_temp_config_manager.py +++ b/custom_components/aqua_temp/managers/aqua_temp_config_manager.py @@ -1,10 +1,15 @@ from homeassistant.config_entries import STORAGE_VERSION, ConfigEntry -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, UnitOfTemperature +from homeassistant.const import ( + CONF_PASSWORD, + CONF_TEMPERATURE_UNIT, + CONF_USERNAME, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.storage import Store -from ..common.consts import CONF_TEMPERATURE_UNIT, CONFIG_FIELDS, DOMAIN +from ..common.consts import DOMAIN class AquaTempConfigManager: @@ -64,8 +69,7 @@ async def _load(self): async def _save(self): data = {} - for key in CONFIG_FIELDS: - if key not in [CONF_PASSWORD, CONF_PASSWORD]: - data[key] = self.data[key] + for key in [CONF_TEMPERATURE_UNIT]: + data[key] = self.data[key] await self._store.async_save(self.data) diff --git a/custom_components/aqua_temp/manifest.json b/custom_components/aqua_temp/manifest.json index 1021f1b..a2c2b39 100644 --- a/custom_components/aqua_temp/manifest.json +++ b/custom_components/aqua_temp/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/radical-squared/aquatemp/issues", "requirements": [], - "version": "3.0.0" + "version": "3.0.1" } diff --git a/custom_components/aqua_temp/select.py b/custom_components/aqua_temp/select.py index 67386a2..de93d1f 100644 --- a/custom_components/aqua_temp/select.py +++ b/custom_components/aqua_temp/select.py @@ -1,19 +1,17 @@ from abc import ABC import logging +import sys -from homeassistant.components.select import ( - DOMAIN as SELECT_DOMAIN, - SelectEntity, - SelectEntityDescription, -) +from homeassistant.components.select import SelectEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import EntityCategory, UnitOfTemperature +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import slugify -from .common.consts import DOMAIN +from .common.consts import ALL_ENTITIES, DOMAIN +from .common.entity_descriptions import AquaTempSelectEntityDescription from .managers.aqua_temp_coordinator import AquaTempCoordinator _LOGGER = logging.getLogger(__name__) @@ -23,23 +21,42 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ): """Set up the sensor platform.""" - coordinator = hass.data[DOMAIN][entry.entry_id] - entities = [] + try: + coordinator = hass.data[DOMAIN][entry.entry_id] + entities = [] + entity_descriptions = [] - for device_code in coordinator.api_data: - entity = AquaTempSelectEntity(device_code, coordinator) + for entity_description in ALL_ENTITIES: + if entity_description.platform == Platform.SELECT: + entity_descriptions.append(entity_description) - entities.append(entity) + for device_code in coordinator.api_data: + for entity_description in entity_descriptions: + entity = AquaTempSelectEntity( + device_code, entity_description, coordinator + ) - _LOGGER.debug(f"Setting up sensor entities: {entities}") + entities.append(entity) - async_add_entities(entities, True) + _LOGGER.debug(f"Setting up sensor entities: {entities}") + + async_add_entities(entities, True) + except Exception as ex: + exc_type, exc_obj, tb = sys.exc_info() + line_number = tb.tb_lineno + + _LOGGER.error(f"Failed to initialize select, Error: {ex}, Line: {line_number}") class AquaTempSelectEntity(CoordinatorEntity, SelectEntity, ABC): """Representation of a sensor.""" - def __init__(self, device_code: str, coordinator: AquaTempCoordinator): + def __init__( + self, + device_code: str, + entity_description: AquaTempSelectEntityDescription, + coordinator: AquaTempCoordinator, + ): super().__init__(coordinator) self._device_code = device_code @@ -49,23 +66,18 @@ def __init__(self, device_code: str, coordinator: AquaTempCoordinator): device_info = coordinator.get_device(device_code) device_name = device_info.get("name") - entity_name = f"{device_name} Temperature Unit" + entity_name = f"{device_name} {entity_description.name}" + + slugify_name = slugify(entity_name) device_id = self._api_data.get("device_id") - slugify_uid = slugify(f"{SELECT_DOMAIN}_temp_unit_{device_id}") + unique_id = slugify(f"{entity_description.platform}_{slugify_name}_{device_id}") - entity_description = SelectEntityDescription(slugify_uid) - entity_description.name = entity_name - entity_description.entity_category = EntityCategory.CONFIG - entity_description.options = [ - UnitOfTemperature.CELSIUS, - UnitOfTemperature.FAHRENHEIT, - ] + self.entity_description: AquaTempSelectEntityDescription = entity_description - self.entity_description = entity_description self._attr_device_info = device_info self._attr_name = entity_name - self._attr_unique_id = slugify_uid + self._attr_unique_id = unique_id @property def current_option(self) -> str | None: diff --git a/custom_components/aqua_temp/sensor.py b/custom_components/aqua_temp/sensor.py index e5c4709..0c98808 100644 --- a/custom_components/aqua_temp/sensor.py +++ b/custom_components/aqua_temp/sensor.py @@ -1,18 +1,16 @@ import logging +import sys -from homeassistant.components.sensor import ( - DOMAIN as SENSOR_DOMAIN, - SensorDeviceClass, - SensorEntity, - SensorEntityDescription, -) +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import slugify -from .common.consts import DOMAIN, PROTOCOL_CODES +from .common.consts import ALL_ENTITIES, DOMAIN +from .common.entity_descriptions import AquaTempSensorEntityDescription from .managers.aqua_temp_coordinator import AquaTempCoordinator _LOGGER = logging.getLogger(__name__) @@ -21,31 +19,45 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ): - """Setup the sensor platform.""" - coordinator = hass.data[DOMAIN][entry.entry_id] - entities = [] + """Set up the sensor platform.""" + try: + coordinator = hass.data[DOMAIN][entry.entry_id] + entities = [] + entity_descriptions = [] - for device_code in coordinator.api_data: - temperature_entities = [ - AquaTempTemperatureEntity(device_code, key, coordinator) - for key in PROTOCOL_CODES - if key.startswith("T") and PROTOCOL_CODES.get(key) is not None - ] + for entity_description in ALL_ENTITIES: + if entity_description.platform == Platform.SENSOR: + entity_descriptions.append(entity_description) - entities.extend(temperature_entities) + for device_code in coordinator.api_data: + for entity_description in entity_descriptions: + entity = AquaTempSensorEntity( + device_code, entity_description, coordinator + ) - _LOGGER.debug(f"Setting up sensor entities: {entities}") + entities.append(entity) - async_add_entities(entities, True) + _LOGGER.debug(f"Setting up sensor entities: {entities}") + async_add_entities(entities, True) + except Exception as ex: + exc_type, exc_obj, tb = sys.exc_info() + line_number = tb.tb_lineno -class AquaTempTemperatureEntity(CoordinatorEntity, SensorEntity): + _LOGGER.error(f"Failed to initialize sensor, Error: {ex}, Line: {line_number}") + + +class AquaTempSensorEntity(CoordinatorEntity, SensorEntity): """Representation of a sensor.""" - def __init__(self, device_code: str, key: str, coordinator: AquaTempCoordinator): + def __init__( + self, + device_code: str, + entity_description: AquaTempSensorEntityDescription, + coordinator: AquaTempCoordinator, + ): super().__init__(coordinator) - self._key = key self._device_code = device_code self._api_data = self.coordinator.api_data[self._device_code] self._config_data = self.coordinator.config_data @@ -53,27 +65,31 @@ def __init__(self, device_code: str, key: str, coordinator: AquaTempCoordinator) device_info = coordinator.get_device(device_code) device_name = device_info.get("name") - entity_name = f"{device_name} {PROTOCOL_CODES.get(key)}" + entity_name = f"{device_name} {entity_description.name}" - device_id = self._api_data.get("device_id") - slugify_uid = slugify(f"{SENSOR_DOMAIN}_{key}_{device_id}") + slugify_name = slugify(entity_name) - native_unit = self.coordinator.get_temperature_unit(device_code) + device_id = self._api_data.get("device_id") + unique_id = slugify(f"{entity_description.platform}_{slugify_name}_{device_id}") - entity_description = SensorEntityDescription(slugify_uid) - entity_description.name = entity_name - entity_description.device_class = SensorDeviceClass.TEMPERATURE - entity_description.native_unit_of_measurement = native_unit + self.entity_description: AquaTempSensorEntityDescription = entity_description - self.entity_description = entity_description self._attr_device_info = device_info self._attr_name = entity_name - self._attr_unique_id = slugify_uid + self._attr_unique_id = unique_id + self._attr_device_class = entity_description.device_class + + if entity_description.device_class == SensorDeviceClass.TEMPERATURE: + self._attr_native_unit_of_measurement = ( + self.coordinator.get_temperature_unit(device_code) + ) @property def native_value(self) -> float | int | str | None: """Return current state.""" - state: float | int | str | None = self._api_data.get(self._key) + state: float | int | str | None = self._api_data.get( + self.entity_description.key + ) if isinstance(state, str): state = float(state) diff --git a/info.md b/info.md index e078ccd..8113407 100644 --- a/info.md +++ b/info.md @@ -51,115 +51,37 @@ Device scheme is according to data from the API Per device the following components will be generated: -| Entity Name | Type | Source | Parameter | Description | -| ----------------------------------------- | ------------- | ------------------- | ---------------- | ------------------------------------------------------------------------------------------------- | -| {HA Device Name} Status | Binary Sensor | Devices list | device_status | Represents whether device is online or not | -| {HA Device Name} Fault | Binary Sensor | Devices status | is_fault | Represents whether device has an issue, in case it has, attribute `fault` will hold the details | -| {HA Device Name} Power | Binary Sensor | Protocol Codes | Power | Represents whether device is on or off | -| {HA Device Name} Power | Climate | Protocol Codes | Power | Controls heat pump - HVAC Modes (Off, Cool, Heat, Auto), Fan Mode (Low, Auto), Target temperature | -| {HA Device Name} Suction temperature | Sensor | Protocol Codes | T01 | Represents the temperature according to the name of the component | -| {HA Device Name} Inlet water temp. | Sensor | Protocol Codes | T02 | Represents the temperature according to the name of the component | -| {HA Device Name} Outlet water temp | Sensor | Protocol Codes | T03 | Represents the temperature according to the name of the component | -| {HA Device Name} Coil temperature | Sensor | Protocol Codes | T04 | Represents the temperature according to the name of the component | -| {HA Device Name} Ambient temperature | Sensor | Protocol Codes | T05 | Represents the temperature according to the name of the component | -| {HA Device Name} Antifreeze 1 temperature | Sensor | Protocol Codes | T06 | Represents the temperature according to the name of the component | -| {HA Device Name} Antifreeze 2 temperature | Sensor | Protocol Codes | T07 | Represents the temperature according to the name of the component | -| {HA Device Name} Suction 1 temperature | Sensor | Protocol Codes | T08 | Represents the temperature according to the name of the component | -| {HA Device Name} Suction 2 temperature | Sensor | Protocol Codes | T08 | Represents the temperature according to the name of the component | -| {HA Device Name} Exhaust 1 temperature | Sensor | Protocol Codes | T09 | Represents the temperature according to the name of the component | -| {HA Device Name} Exhaust 1 temperature | Sensor | Protocol Codes | T10 | Represents the temperature according to the name of the component | -| {HA Device Name} Hot water temperature | Sensor | Protocol Codes | T12 | Represents the temperature according to the name of the component | -| {HA Device Name} Temperature Unit | Select | Local configuration | temperature_unit | Controls the unit of temperature to match the one configured in the Aqua Temp device | - -## Protocol Codes - -Protocol Codes first character represent a category, below are the categories: - -| Character | Description | Parameters | -| --------- | --------------------- | ---------- | -| / | Hardware parameter | /01-/28 | -| A | Protection parameter | A01-A09 | -| C | Compressor parameter | C01-C04 | -| D | Defrost parameter | D01-D08 | -| E | EEV parameter | E01-E11 | -| F | Fan parameter | F01-F11 | -| H | System parameter | H01-H13 | -| P | Water pump parameter | P01-P09 | -| R | Temp parameter | R01-R28 | -| U | Water flow parameter | U01-U04 | -| S | Switch state checking | S01-S13 | -| T | Temp. checking | T01-T12 | -| O | Load output | O01-O11 | - -Integration is collecting the following - -```json -{ - "Power": "0 - Off or 1 - On", - "Mode": "HVAC mode (Off, Cool, Heat, Auto)", - "Manual-mode": "Fan speed (Low, Auto)", - "2074": "Unknown", - "2075": "Unknown", - "2076": "Unknown", - "2077": "Unknown", - "Set_Temp": "Set Target temperature", - "1158": "Unknown", - "1159": "Unknown", - "D01": "Start defrosting temperature", - "D02": "End defrost temperature", - "D03": "defrosting cycle", - "D04": "Maximum defrosting time", - "D05": "Minimum defrosting time", - "D06": "Defrost mode", - "D07": "Defrost heater control", - "D08": "Defrost AUTO set", - "E01": "EEV 1 mode", - "E02": "Super heat 1", - "E03": "Initial place 1", - "E04": "EEV 2 mode", - "E05": "Super heat 2", - "E06": "Initial place 2", - "E07": "Minimum place", - "E08": "Defrost place", - "E09": "Cooling place", - "E10": "Low exhaust", - "E11": "High exhaust", - "F01": "Fan parameter", - "F02": "Coil temperature in high speed fan mode (Cooling)", - "F03": "Coil temperature in low speed fan mode (Cooling)", - "F04": "Coil temperature when the fan stop (Cooling)", - "F05": "Coil temperature in high speed fan mode (Heating)", - "F06": "Coil temperature in low speed fan mode (Heating)", - "F07": "Coil temperature when the fan stop (Heating)", - "F08": "Fan start low speed running time", - "F09": "Fan stop low speed running time", - "F10": "Fan quantity", - "F11": "Fan speed control temp." - "H02": "System quantity", - "H03": "4-way valve polarity", - "H04": "4-way valve control", - "R01": "Inlet water setting temperature (cooling)", - "R02": "Inlet water setting temperature (Heating)", - "R03": "Target setting temperature (Auto mode)", - "R08": "Minimum set point in Cooling", - "R09": "Maximum Cooling set point", - "R10": "Minimum Heating set point", - "R11": "Maximum Heating set point", - "U02": "Pulse", - "T01": "Suction temperature", - "T02": "Inlet water temp.", - "T03": "Outlet water temp", - "T04": "Coil temperature", - "T05": "Ambient temperature", - "T06": "Antifreeze 1 temperature", - "T07": "Antifreeze 2 temperature", - "T08": "Suction 1 temperature", - "T09": "Suction 2 temperature", - "T10": "Exhaust 1 temperature", - "T11": "Exhaust 2 temperature", - "T12": "Hot water temperature" -} -``` +| Entity Name | Parameter | Platform | Protocol Code? | +| ---------------------------------------------------------- | ---------------- | ------------- | -------------- | +| {HA Device Name} Temperature Unit | temperature_unit | select | False | +| {HA Device Name} Fault | is_fault | binary_sensor | False | +| {HA Device Name} Status | device_status | binary_sensor | False | +| {HA Device Name} Power | Power | binary_sensor | True | +| {HA Device Name} HVAC Mode | Mode | climate | True | +| {HA Device Name} Inlet water setting temperature (cooling) | R01 | sensor | True | +| {HA Device Name} Inlet water setting temperature (Heating) | R02 | sensor | True | +| {HA Device Name} Target setting temperature (Auto mode) | R03 | sensor | True | +| {HA Device Name} Minimum set point in Cooling | R08 | sensor | True | +| {HA Device Name} Maximum Cooling set point | R09 | sensor | True | +| {HA Device Name} Minimum Heating set point | R10 | sensor | True | +| {HA Device Name} Maximum Heating set point | R11 | sensor | True | +| {HA Device Name} Suction temperature | T01 | sensor | True | +| {HA Device Name} Inlet water temp. | T02 | sensor | True | +| {HA Device Name} Outlet water temp | T03 | sensor | True | +| {HA Device Name} Coil temperature | T04 | sensor | True | +| {HA Device Name} Ambient temperature | T05 | sensor | True | +| {HA Device Name} Antifreeze 1 temperature | T06 | sensor | True | +| {HA Device Name} Antifreeze 2 temperature | T07 | sensor | True | +| {HA Device Name} Suction 1 temperature | T08 | sensor | True | +| {HA Device Name} Suction 2 temperature | T09 | sensor | True | +| {HA Device Name} Exhaust 1 temperature | T10 | sensor | True | +| {HA Device Name} Exhaust 2 temperature | T11 | sensor | True | +| {HA Device Name} Hot water temperature | T12 | sensor | True | +| {HA Device Name} 4-way valve output | O06 | sensor | True | +| {HA Device Name} Electronic Expansion valve 1 output | O10 | sensor | True | +| {HA Device Name} Electronic Expansion valve 2 output | O11 | sensor | True | + +[Protocol Codes](https://github.com/radical-squared/aquatemp/blob/master/PROTOCOL_CODES.md) ## Run API over CLI diff --git a/tests/main.py b/tests/main.py index 04f97bc..418a92e 100644 --- a/tests/main.py +++ b/tests/main.py @@ -3,10 +3,12 @@ import os import sys +from custom_components.aqua_temp.common.consts import ALL_ENTITIES from custom_components.aqua_temp.managers.aqua_temp_api import AquaTempAPI from custom_components.aqua_temp.managers.aqua_temp_config_manager import ( AquaTempConfigManager, ) +from homeassistant.const import Platform DEBUG = str(os.environ.get("DEBUG", False)).lower() == str(True).lower() @@ -30,7 +32,81 @@ def __init__(self): config_manager.update_credentials("elad.bar@hotmail.com", "Amit0807!") self._api = AquaTempAPI(None, config_manager) - async def initialize(self): + async def parameters_list(self): + await self._api.initialize() + + _LOGGER.debug(self._api.protocol_codes) + + async def list_data(self): + await self._api.initialize() + + await self._api.update() + + print(f"| Parameter | Device Code | Value | Description |") + print(f"| --------- | ----------- | ----- | --------------------- |") + + for device_code in self._api.data: + device_data = self._api.data[device_code] + + for entity_description in ALL_ENTITIES: + value = device_data.get(entity_description.key) + + if entity_description.platform == Platform.BINARY_SENSOR: + value = value == entity_description.on_value + + if value is not None and value != "": + print(f"| {entity_description.key} | {device_code} | {value} | {entity_description.name} |") + + await self._api.terminate() + + async def protocol_code_mapping(self): + protocol_categories = {} + + for entity_description in ALL_ENTITIES: + if entity_description.is_protocol_code: + if entity_description.category not in protocol_categories: + protocol_categories[entity_description.category] = [] + + protocol_categories[entity_description.category].append(entity_description) + + for protocol_category in protocol_categories: + entity_descriptions = protocol_categories[protocol_category] + print(f"### {protocol_category}") + + print(f"| Parameter | Description |") + print(f"| --------- | --------------------- |") + + for entity_description in entity_descriptions: + print(f"| {entity_description.key} | {entity_description.name} |") + + async def entities_mapping(self): + print("| Entity Name | Parameter | Platform | Protocol Code? |") + print("| ----------- | --------- | -------- | -------------- |") + + for entity_description in ALL_ENTITIES: + if entity_description.platform is not None: + print(f"| {{HA Device Name}} {entity_description.name} | {entity_description.key} | {entity_description.platform} | {entity_description.is_protocol_code} |") + + async def parameters_details(self): + await self.parameters_list() + + await self._api.update() + + print(self._api.data) + + for device_code in self._api.data: + device_data = self._api.data[device_code] + + for entity_description in ALL_ENTITIES: + value = device_data.get(entity_description.key) + _LOGGER.debug( + f"{entity_description.key}::" + f"{entity_description.name} " + f"[{entity_description.category}] = " + f"{value}" + ) + + async def api_test(self): await self._api.initialize() for i in range(1, 10): @@ -40,13 +116,16 @@ async def initialize(self): await asyncio.sleep(30) + async def terminate(self): + await self._api.terminate() + loop = asyncio.new_event_loop() instance = Test() try: - loop.run_until_complete(instance.initialize()) + loop.run_until_complete(instance.list_data()) except KeyboardInterrupt: _LOGGER.info("Aborted")