diff --git a/CHANGELOG.md b/CHANGELOG.md index c1609ca..14894e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -176,4 +176,11 @@ Thank for @pblxptr add new code line from him **Full Changelog**: [https://github.com/jontofront/ecoNET-300-Home-Assistant-Integration/compare/v0.3.3...v1.0.5](https://github.com/jontofront/ecoNET-300-Home-Assistant-Integration/compare/v0.3.3...v1.0.5) -## [v1.0.5] \ No newline at end of file +## [v1.0.11] + +Additions and Updates to Sensors: +Added boilerPowerKW as a new sensor type in custom_components/econet300/const.py and updated its unit, device class, and icon. [1] [2] [3] [4] [5] +Added feederWorks as a new binary sensor in custom_components/econet300/const.py and updated its icon. [1] [2] [3] +Updates to Existing Sensors: +Renamed the lighter sensor to lighterWorks in custom_components/econet300/const.py and updated its icon. [1] [2] +Updated the names of the lighter, boilerPower, and feeder sensors in custom_components/econet300/strings.json and custom_components/econet300/translations/en.json. [1] [2] [3] [4] [5] [6] \ No newline at end of file diff --git a/README.md b/README.md index 2abadc1..6e28acf 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,22 @@ he **ecoNET300 Home Assistant Integration** allows local control and monitoring - **Easy Configuration**: Integrate directly via Home Assistant UI. - **Tested With**: ecoMAX810P-L TOUCH controller from [Plum Sp. z o.o.](https://www.plum.pl/) +## Table of Contents +1. [ecoNET300 Home Assistant Integration](#econet300-home-assistant-integration) +2. [Overview](#overview) +3. [Versions](#versions) + - [Migrating to v1.0.0_beta](#migrating-to-v100_beta) +4. [Example](#example) +5. [Installation](#installation) + - [HACS (Recommended)](#hacs-recommended) + - [Manual Installation](#manual-installation) +6. [Configuration](#configuration) +7. [Entities](#entities) + - [Sensors](#sensors) + - [Binary Sensors](#binary-sensors) +8. [Contributing](#contributing) +9. [Acknowledgments](#acknowledgments) +10. [Disclaimer](#disclaimer) ## versions * v0.3.3 - version is stable. Most of the work was done by @pblxpt, for which we're very thankful as the community. @@ -103,36 +119,72 @@ __Password__: Local password (NOT the password that you use to login to econet24
## Entities + ### Sensors -These sensors are retrieved from the `../econet/regParams` endpoint. Below is the list of available entity keys and their descriptions: +These sensors are retrieved from the `../econet/regParams` and `../econet/sysParams` endpoints. Below is the list of available entity keys, their descriptions, and the corresponding API endpoint keys: +
+ **馃憠 Click here to expand the table** + + +| Entity Key | Description | Endpoint | +|----------------------|-----------------------------------------------------------|-----------------------| +| `tempFeeder` | Temperature of the feeder mechanism | `../econet/regParams` | +| `fuelLevel` | Current fuel level in the system | `../econet/regParams` | +| `tempCO` | Current fireplace temperature | `../econet/regParams` | +| `tempCOSet` | Desired fireplace set temperature | `../econet/regParams` | +| `statusCWU` | Status of the hot water (CWU) system | `../econet/regParams` | +| `tempCWU` | Current hot water (CWU) temperature | `../econet/regParams` | +| `tempCWUSet` | Desired hot water (CWU) temperature | `../econet/regParams` | +| `tempFlueGas` | Exhaust temperature reading | `../econet/regParams` | +| `mode` | Current operational mode of the device | `../econet/regParams` | +| `fanPower` | Current fan power usage | `../econet/regParams` | +| `thermostat` | Thermostat status or set temperature | `../econet/regParams` | +| `tempExternalSensor` | Outside (external) temperature | `../econet/regParams` | +| `tempLowerBuffer` | Temperature of the lower thermal buffer | `../econet/regParams` | +| `tempUpperBuffer` | Temperature of the upper thermal buffer | `../econet/regParams` | +| `boilerPower` | Current power output of the boiler | `../econet/regParams` | +| `quality` | Fuel quality or system quality indicator (if applicable) | `../econet/sysParams` | +| `signal` | Signal strength or communication status | `../econet/sysParams` | +| `softVer` | Software version of the controller | `../econet/sysParams` | +| `controllerID` | Unique identifier for the controller | `../econet/sysParams` | +| `moduleASoftVer` | Software version of Module A | `../econet/sysParams` | +| `moduleBSoftVer` | Software version of Module B | `../econet/sysParams` | +| `moduleCSoftVer` | Software version of Module C | `../econet/sysParams` | +| `moduleLambdaSoftVer`| Software version of the lambda module | `../econet/sysParams` | +| `modulePanelSoftVer` | Software version of the control panel | `../econet/sysParams` | +
+ +### Binary Sensors -| sensor Key | Description | -|----------------------|------------------------------------------| -| `tempFeeder` | Temperature of the feeder mechanism | -| `fuelLevel` | Current fuel level in the system | -| `tempCO` | Current fireplace temperature | -| `tempCOSet` | Desired fireplace set temperature | -| `statusCWU` | Status of the hot water (CWU) system | -| `tempCWUSet` | Desired hot water (CWU) temperature | -| `tempFlueGas` | Exhaust temperature reading | -| `mode` | Current operational mode of the device | -| `fanPower` | Current fan power usage | -| `thermostat` | Thermostat status or set temperature | -| `tempExternalSensor` | Outside (external) temperature | +These binary sensors are retrieved from the `../econet/regParams` and `../econet/sysParams` endpoints. Below is the list of available entity keys, their descriptions, and the corresponding API endpoint keys: +
+ **馃憠 Click here to expand the table** + +| Entity Key | Description | Endpoint | +|----------------------|--------------------------------------------------|-----------------------| +| `lighter` | Indicates if the lighter is active | `../econet/regParams` | +| `pumpCOWorks` | Indicates if the fireplace pump is working | `../econet/regParams` | +| `fanWorks` | Indicates if the fan is currently active | `../econet/regParams` | +| `pumpFireplaceWorks` | Indicates if the fireplace pump is working | `../econet/regParams` | +| `pumpCWUWorks` | Indicates if the hot water (CWU) pump is active | `../econet/regParams` | +| `mainSrv` | Indicates if the main server is operational | `../econet/sysParams` | +| `wifi` | Indicates if the Wi-Fi connection is active | `../econet/sysParams` | +| `lan` | Indicates if the LAN connection is active | `../econet/sysParams` | +
+### Number Entities -### Binary Sensors +These number entities are retrieved from the `../econet/rmCurrentDataParamsEdits` endpoint. Below is the list of available entity keys, their descriptions, and the corresponding API endpoint keys: -These binary sensors are retrieved from the `../econet/regParams` endpoint. Below is the list of available entity keys and their descriptions: -| Entity Key | Description | -|----------------------|---------------------------------------------| -| `lighter` | Indicates if the lighter is active | -| `pumpCOWorks` | Indicates if the fireplace pump is working | -| `fanWorks` | Indicates if the fan is currently active | -| `pumpFireplaceWorks` | Indicates if the fireplace pump is working | -| `pumpCWUWorks` | Indicates if the hot water (CWU) pump is active | +
+ **馃憠 Click here to expand the table** +| Entity Key | Description | Endpoint | +|----------------------|----------------------------------------------|--------------------------------------| +| `tempCOSet` | Desired fireplace set temperature | `../econet/rmCurrentDataParamsEdits` | +| `tempCWUSet` | Desired hot water (CWU) set temperature | `../econet/rmCurrentDataParamsEdits` | +
## Contributing diff --git a/custom_components/econet300/binary_sensor.py b/custom_components/econet300/binary_sensor.py index 35ae0e0..819da5e 100644 --- a/custom_components/econet300/binary_sensor.py +++ b/custom_components/econet300/binary_sensor.py @@ -4,6 +4,7 @@ import logging from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, BinarySensorEntity, BinarySensorEntityDescription, ) @@ -84,7 +85,9 @@ def create_binary_entity_description(key: str) -> EconetBinarySensorEntityDescri entity_description = EconetBinarySensorEntityDescription( key=key, translation_key=camel_to_snake(key), - device_class=ENTITY_BINARY_DEVICE_CLASS_MAP.get(key, None), + device_class=ENTITY_BINARY_DEVICE_CLASS_MAP.get( + key, BinarySensorDeviceClass.RUNNING + ), icon=ENTITY_ICON.get(key, None), icon_off=ENTITY_ICON_OFF.get(key, None), ) @@ -95,20 +98,31 @@ def create_binary_entity_description(key: str) -> EconetBinarySensorEntityDescri def create_binary_sensors(coordinator: EconetDataCoordinator, api: Econet300Api): """Create binary sensors.""" entities: list[EconetBinarySensor] = [] - coordinator_data = coordinator.data["regParams"] + data_regParams = coordinator.data.get("regParams", {}) + data_sysParams = coordinator.data.get("sysParams", {}) + for data_key in BINARY_SENSOR_MAP_KEY["_default"]: - _LOGGER.debug("Processing binary sensor data_key: %s", data_key) - if data_key in coordinator_data: + _LOGGER.debug( + "Processing binary sensor data_key: %s from regParams & sysParams", data_key + ) + if data_key in data_regParams: + entity = EconetBinarySensor( + create_binary_entity_description(data_key), coordinator, api + ) + entities.append(entity) + _LOGGER.debug("Created and appended entity from regParams: %s", entity) + elif data_key in data_sysParams: entity = EconetBinarySensor( create_binary_entity_description(data_key), coordinator, api ) entities.append(entity) - _LOGGER.debug("Created and appended entity: %s", entity) + _LOGGER.debug("Created and appended entity from sysParams: %s", entity) else: _LOGGER.warning( - "key: %s is not mapped, binary sensor entity will not be added", + "key: %s is not mapped in regParams, binary sensor entity will not be added", data_key, ) + _LOGGER.info("Total entities created: %d", len(entities)) return entities diff --git a/custom_components/econet300/common.py b/custom_components/econet300/common.py index 2679d31..6ff15ca 100644 --- a/custom_components/econet300/common.py +++ b/custom_components/econet300/common.py @@ -60,7 +60,7 @@ async def _async_update_data(self): try: # Note: asyncio.TimeoutError and aiohttp.ClientError are already # handled by the data update coordinator. - async with asyncio.timeout(10): + async with asyncio.timeout(20): data = await self._api.fetch_sys_params() reg_params = await self._api.fetch_reg_params() params_edits = await self._api.fetch_param_edit_data() diff --git a/custom_components/econet300/const.py b/custom_components/econet300/const.py index 9e574e0..ef0dab0 100644 --- a/custom_components/econet300/const.py +++ b/custom_components/econet300/const.py @@ -11,6 +11,7 @@ STATE_PROBLEM, STATE_UNKNOWN, EntityCategory, + UnitOfPower, UnitOfTemperature, UnitOfTime, ) @@ -25,6 +26,7 @@ DEVICE_INFO_MODEL = "ecoNET300" DEVICE_INFO_CONTROLLER_NAME = "PLUM ecoNET300" DEVICE_INFO_MIXER_NAME = "Mixer device" +DEVICE_INFO_LAMBDA_NAME = "Module Lambda" CONF_ENTRY_TITLE = "ecoNET300" CONF_ENTRY_DESCRIPTION = "PLUM Econet300" @@ -44,14 +46,7 @@ API_REG_PARAMS_DATA_URI = "regParamsData" API_REG_PARAMS_DATA_PARAM_DATA = "data" -## Map names for params data in API_REG_PARAMS_DATA_URI -API_RM_CURRENT_DATA_PARAMS_URI = "rmCurrentDataParams" - -## Map units for params data map API_RM_CURRENT_DATA_PARAMS_URI -API_RM_PARAMSUNITSNAMES_URI = "rmParamsUnitsNames" - # Boiler status keys map -# boiler mode names from endpoint http://LocalIP/econet/rmParamsEnums? OPERATION_MODE_NAMES = { 0: STATE_OFF, 1: "fire_up", @@ -83,7 +78,7 @@ # Dynamically generate SENSOR_MIXER_KEY SENSOR_MIXER_KEY = { - str(i): {f"{MIXER_AVAILABILITY_KEY}{i}", f"{MIXER_SET_AVAILABILITY_KEY}{i}"} + i: {f"{MIXER_AVAILABILITY_KEY}{i}", f"{MIXER_SET_AVAILABILITY_KEY}{i}"} for i in range(1, AVAILABLE_NUMBER_OF_MIXERS + 1) } @@ -102,27 +97,45 @@ "lambdaLevel", }, "_default": { + "boilerPower", + "boilerPowerKW", "tempFeeder", "fuelLevel", "tempCO", "tempCOSet", "statusCWU", + "tempCWU", "tempCWUSet", "tempFlueGas", "mode", "fanPower", "thermostat", "tempExternalSensor", + "tempLowerBuffer", + "tempUpperBuffer", + "quality", + "signal", + "softVer", + "controllerID", + "moduleASoftVer", + "moduleBSoftVer", + "moduleCSoftVer", + "moduleLambdaSoftVer", + "modulePanelSoftVer", }, } BINARY_SENSOR_MAP_KEY = { "_default": { - "lighter", + "lighterWorks", "pumpCOWorks", "fanWorks", + "feederWorks", "pumpFireplaceWorks", "pumpCWUWorks", + "mainSrv", + "wifi", + "lan", }, } @@ -131,6 +144,7 @@ "1281": "tempCWUSet", } +# By default all sensors unit_of_measurement are None ENTITY_UNIT_MAP = { "tempCO": UnitOfTemperature.CELSIUS, "tempCOSet": UnitOfTemperature.CELSIUS, @@ -143,7 +157,6 @@ "workAt50": UnitOfTime.HOURS, "workAt30": UnitOfTime.HOURS, "FeederWork": UnitOfTime.HOURS, - "FiringUpCount": None, "thermoTemp": UnitOfTemperature.CELSIUS, "fanPower": PERCENTAGE, "tempFlueGas": UnitOfTemperature.CELSIUS, @@ -152,6 +165,7 @@ "tempBack": UnitOfTemperature.CELSIUS, "tempCWU": UnitOfTemperature.CELSIUS, "boilerPower": PERCENTAGE, + "boilerPowerKW": UnitOfPower.KILO_WATT, "fuelLevel": PERCENTAGE, "tempUpperBuffer": UnitOfTemperature.CELSIUS, "tempLowerBuffer": UnitOfTemperature.CELSIUS, @@ -163,36 +177,22 @@ "mixerSetTemp": UnitOfTemperature.CELSIUS, } -STATE_CLASS_MAP: dict[str, SensorStateClass] = { - "tempFeeder": SensorStateClass.MEASUREMENT, - "tempExternalSensor": SensorStateClass.MEASUREMENT, - "lambdaSet": SensorStateClass.MEASUREMENT, - "lambdaLevel": SensorStateClass.MEASUREMENT, - "workAt100": SensorStateClass.MEASUREMENT, - "workAt50": SensorStateClass.MEASUREMENT, - "workAt30": SensorStateClass.MEASUREMENT, - "FeederWork": SensorStateClass.MEASUREMENT, - "FiringUpCount": SensorStateClass.MEASUREMENT, - "tempCO": SensorStateClass.MEASUREMENT, - "tempCOSet": SensorStateClass.MEASUREMENT, - "tempCWUSet": SensorStateClass.MEASUREMENT, - "boiler_power": SensorStateClass.MEASUREMENT, - "fanPower": SensorStateClass.MEASUREMENT, - "tempFlueGas": SensorStateClass.MEASUREMENT, - "mixerSetTemp1": SensorStateClass.MEASUREMENT, - "tempBack": SensorStateClass.MEASUREMENT, - "tempCWU": SensorStateClass.MEASUREMENT, - "fuelLevel": SensorStateClass.MEASUREMENT, - "tempUpperBuffer": SensorStateClass.MEASUREMENT, - "tempLowerBuffer": SensorStateClass.MEASUREMENT, - "signal": SensorStateClass.MEASUREMENT, - "quality": SensorStateClass.MEASUREMENT, - "valveMixer1": SensorStateClass.MEASUREMENT, - "burnerOutput": SensorStateClass.MEASUREMENT, - "mixerTemp": SensorStateClass.MEASUREMENT, - "mixerSetTemp": SensorStateClass.MEASUREMENT, +# By default all sensors state_class are MEASUREMENT +STATE_CLASS_MAP: dict[str, SensorStateClass | None] = { + "lambdaStatus": None, + "mode": None, + "thermostat": None, + "statusCWU": None, + "softVer": None, + "controllerID": None, + "moduleASoftVer": None, + "moduleBSoftVer": None, + "moduleCSoftVer": None, + "moduleLambdaSoftVer": None, + "modulePanelSoftVer": None, } +# By default all sensors device_class are None ENTITY_SENSOR_DEVICE_CLASS_MAP: dict[str, SensorDeviceClass | None] = { ############################# # SENSORS @@ -201,6 +201,7 @@ "tempExternalSensor": SensorDeviceClass.TEMPERATURE, "tempCO": SensorDeviceClass.TEMPERATURE, "boilerPower": SensorDeviceClass.POWER_FACTOR, + "boilerPowerKW": SensorDeviceClass.POWER, "fanPower": SensorDeviceClass.POWER_FACTOR, "tempFlueGas": SensorDeviceClass.TEMPERATURE, "mixerSetTemp1": SensorDeviceClass.TEMPERATURE, @@ -212,17 +213,7 @@ "tempUpperBuffer": SensorDeviceClass.TEMPERATURE, "tempLowerBuffer": SensorDeviceClass.TEMPERATURE, "signal": SensorDeviceClass.SIGNAL_STRENGTH, - "softVer": None, - "moduleASoftVer": None, - "moduleBSoftVer": None, - "modulePanelSoftVer": None, - "moduleLambdaSoftVer": None, - "protocolType": None, - "controllerID": None, - "valveMixer1": None, "servoMixer1": SensorDeviceClass.ENUM, - "Status_wifi": None, - "main_server": None, } ENTITY_NUMBER_SENSOR_DEVICE_CLASS_MAP = { @@ -238,15 +229,10 @@ ############################# # BINARY SENSORS ############################# - "lighter": BinarySensorDeviceClass.RUNNING, - "weatherControl": BinarySensorDeviceClass.RUNNING, - "unseal": BinarySensorDeviceClass.RUNNING, - "pumpCOWorks": BinarySensorDeviceClass.RUNNING, - "fanWorks": BinarySensorDeviceClass.RUNNING, - "additionalFeeder": BinarySensorDeviceClass.RUNNING, - "pumpFireplaceWorks": BinarySensorDeviceClass.RUNNING, - "pumpCWUWorks": BinarySensorDeviceClass.RUNNING, - "mixerPumpWorks": BinarySensorDeviceClass.RUNNING, + # By default all binary sensors device_class are RUNNING + "mainSrv": BinarySensorDeviceClass.CONNECTIVITY, + "wifi": BinarySensorDeviceClass.CONNECTIVITY, + "lan": BinarySensorDeviceClass.CONNECTIVITY, } """Add only keys where precision more than 0 needed""" @@ -263,25 +249,37 @@ "tempCWU": 1, "tempFlueGas": 1, "fanPower": 0, + "statusCWU": None, + "thermostat": None, + "lambdaStatus": None, + "mode": None, + "softVer": None, + "controllerID": None, + "moduleASoftVer": None, + "moduleBSoftVer": None, + "moduleCSoftVer": None, + "moduleLambdaSoftVer": None, + "modulePanelSoftVer": None, } ENTITY_ICON = { "mode": "mdi:sync", "fanPower": "mdi:fan", "temCO": "mdi:thermometer-lines", - "tempCOSet": "mdi:thermometer-chevron-up", - "tempCWUSet": "mdi:thermometer-chevron-up", "statusCWU": "mdi:water-boiler", "thermostat": "mdi:thermostat", "boilerPower": "mdi:gauge", + "boilerPowerKW": "mdi:gauge", "fuelLevel": "mdi:gas-station", "lambdaLevel": "mdi:lambda", "lambdaSet": "mdi:lambda", "lambdaStatus": "mdi:lambda", + "lighterWorks": "mdi:fire", "workAt100": "mdi:counter", "workAt50": "mdi:counter", "workAt30": "mdi:counter", "FeederWork": "mdi:counter", + "feederWorks": "mdi:screw-lag", "FiringUpCount": "mdi:counter", "quality": "mdi:signal", "pumpCOWorks": "mdi:pump", @@ -289,14 +287,21 @@ "additionalFeeder": "mdi:screw-lag", "pumpFireplaceWorks": "mdi:pump", "pumpCWUWorks": "mdi:pump", - "main_server": "mdi:server", "mixerPumpWorks": "mdi:pump", "mixerTemp": "mdi:thermometer", "mixerSetTemp": "mdi:thermometer", "valveMixer1": "mdi:valve", - "mixerSetTemp1": "mdi:thermometer-chevron-up", "servoMixer1": "mdi:valve", "mixerTemp1": "mdi:thermometer", + "mainSrv": "mdi:server-network", + "lan": "mdi:lan-connect", + "softVer": "mdi:alarm-panel-outline", + "controllerID": "mdi:alarm-panel-outline", + "moduleASoftVer": "mdi:raspberry-pi", + "moduleBSoftVer": "mdi:raspberry-pi", + "moduleCSoftVer": "mdi:raspberry-pi", + "moduleLambdaSoftVer": "mdi:raspberry-pi", + "modulePanelSoftVer": "mdi:alarm-panel-outline", } ENTITY_ICON_OFF = { @@ -306,6 +311,9 @@ "pumpFireplaceWorks": "mdi:pump-off", "pumpCWUWorks": "mdi:pump-off", "statusCWU": "mdi:water-boiler-off", + "mainSrv": "mdi:server-network-off", + "lan": "mdi:lan-disconnect", + "lighterWorks": "mdi:fire-off", } NO_CWU_TEMP_SET_STATUS_CODE = 128 @@ -319,8 +327,6 @@ else ("start" if x == 1 else ("working" if x == 2 else STATE_UNKNOWN)) ) ), - "status_wifi": lambda x: "Connected" if x == 1 else "Disconnected", - "main_server": lambda x: "Server available" if x == 1 else "Server not available", "statusCWU": lambda x: "Not set" if x == NO_CWU_TEMP_SET_STATUS_CODE else "Set", "thermostat": lambda x: "ON" if x == 1 else "OFF", } @@ -336,13 +342,10 @@ "moduleLambdaSoftVer": EntityCategory.DIAGNOSTIC, "protocolType": EntityCategory.DIAGNOSTIC, "controllerID": EntityCategory.DIAGNOSTIC, - "status_wifi": EntityCategory.DIAGNOSTIC, - "main_server": EntityCategory.DIAGNOSTIC, - "workAt100": EntityCategory.DIAGNOSTIC, - "workAt50": EntityCategory.DIAGNOSTIC, - "workAt30": EntityCategory.DIAGNOSTIC, - "FeederWork": EntityCategory.DIAGNOSTIC, - "FiringUpCount": EntityCategory.DIAGNOSTIC, + "moduleCSoftVer": EntityCategory.DIAGNOSTIC, + "mainSrv": EntityCategory.DIAGNOSTIC, + "wifi": EntityCategory.DIAGNOSTIC, + "lan": EntityCategory.DIAGNOSTIC, } ENTITY_MIN_VALUE = { @@ -359,8 +362,3 @@ "tempCOSet": 1, "tempCWUSet": 1, } - -ENTITY_VISIBLE = { - "tempCOSet": True, - "tempCWUSet": True, -} diff --git a/custom_components/econet300/entity.py b/custom_components/econet300/entity.py index 6beb722..acbaa69 100644 --- a/custom_components/econet300/entity.py +++ b/custom_components/econet300/entity.py @@ -10,6 +10,7 @@ from .common import EconetDataCoordinator from .const import ( DEVICE_INFO_CONTROLLER_NAME, + DEVICE_INFO_LAMBDA_NAME, DEVICE_INFO_MANUFACTURER, DEVICE_INFO_MIXER_NAME, DEVICE_INFO_MODEL, @@ -79,9 +80,9 @@ async def async_added_to_hass(self): sys_params = self.coordinator.data.get("sysParams", {}) reg_params = self.coordinator.data.get("regParams", {}) params_edits = self.coordinator.data.get("paramsEdits", {}) - _LOGGER.debug("sysParams: %s", sys_params) - _LOGGER.debug("regParams: %s", reg_params) - _LOGGER.debug("paramsEdits: %s", params_edits) + _LOGGER.debug("async_sysParams: %s", sys_params) + _LOGGER.debug("async_regParams: %s", reg_params) + _LOGGER.debug("async_paramsEdits: %s", params_edits) # Check if the coordinator has a 'data' attributes if "data" not in dir(self.coordinator): @@ -155,3 +156,28 @@ def device_info(self) -> DeviceInfo | None: sw_version=self.api.sw_rev, via_device=(DOMAIN, self.api.uid), ) + + +class LambdaEntity(EconetEntity): + """Represents EcosterEntity.""" + + def __init__( + self, + description: EntityDescription, + coordinator: EconetDataCoordinator, + api: Econet300Api, + ): + super().__init__(description, coordinator, api) + + @property + def device_info(self) -> DeviceInfo | None: + """Return device info of the entity.""" + return DeviceInfo( + identifiers={(DOMAIN, f"{self.api.uid}lambda")}, + name=f"{DEVICE_INFO_LAMBDA_NAME}", + manufacturer=DEVICE_INFO_MANUFACTURER, + model=DEVICE_INFO_MODEL, + configuration_url=self.api.host, + sw_version=self.api.sw_rev, + via_device=(DOMAIN, self.api.uid), + ) diff --git a/custom_components/econet300/manifest.json b/custom_components/econet300/manifest.json index 8482bae..65a5076 100644 --- a/custom_components/econet300/manifest.json +++ b/custom_components/econet300/manifest.json @@ -12,6 +12,6 @@ "issue_tracker": "https://github.com/jontofront/ecoNET-300-Home-Assistant-Integration/issues", "requirements": [], "ssdp": [], - "version": "v1.0.6", + "version": "v1.0.11", "zeroconf": [] } \ No newline at end of file diff --git a/custom_components/econet300/number.py b/custom_components/econet300/number.py index 524b992..208f1ab 100644 --- a/custom_components/econet300/number.py +++ b/custom_components/econet300/number.py @@ -19,7 +19,6 @@ ENTITY_NUMBER_SENSOR_DEVICE_CLASS_MAP, ENTITY_STEP, ENTITY_UNIT_MAP, - ENTITY_VISIBLE, NUMBER_MAP, SERVICE_API, SERVICE_COORDINATOR, @@ -129,10 +128,17 @@ async def async_set_native_value(self, value: float) -> None: def can_add(key: str, coordinator: EconetDataCoordinator): """Check if a given entity can be added based on the availability of data in the coordinator.""" - return coordinator.has_param_edit_data(key) and coordinator.data["paramsEdits"][key] + try: + return ( + coordinator.has_param_edit_data(key) + and coordinator.data["paramsEdits"][key] + ) + except KeyError as e: + _LOGGER.error("KeyError in can_add: %s", e) + return False -def apply_limits(desc: EconetNumberEntityDescription, limits: Limits): +def apply_limits(desc: EconetNumberEntityDescription, limits: Limits) -> None: """Set the native minimum and maximum values for the given entity description.""" desc.native_min_value = limits.min desc.native_max_value = limits.max @@ -149,7 +155,6 @@ def create_number_entity_description(key: str) -> EconetNumberEntityDescription: icon=ENTITY_ICON.get(map_key), device_class=ENTITY_NUMBER_SENSOR_DEVICE_CLASS_MAP.get(map_key), native_unit_of_measurement=ENTITY_UNIT_MAP.get(map_key), - entity_registry_visible_default=ENTITY_VISIBLE.get(map_key, True), min_value=ENTITY_MIN_VALUE.get(map_key), max_value=ENTITY_MAX_VALUE.get(map_key), native_step=ENTITY_STEP.get(map_key, 1), diff --git a/custom_components/econet300/sensor.py b/custom_components/econet300/sensor.py index 46f045d..ee7fc3c 100644 --- a/custom_components/econet300/sensor.py +++ b/custom_components/econet300/sensor.py @@ -5,7 +5,11 @@ import logging from typing import Any -from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.components.sensor import ( + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -26,7 +30,7 @@ SERVICE_COORDINATOR, STATE_CLASS_MAP, ) -from .entity import EconetEntity, MixerEntity +from .entity import EconetEntity, LambdaEntity, MixerEntity _LOGGER = logging.getLogger(__name__) @@ -55,13 +59,15 @@ def __init__( self._attr_native_value = None super().__init__(coordinator) - def _sync_state(self, value): + def _sync_state(self, value) -> None: """Synchronize the state of the sensor entity.""" self._attr_native_value = self.entity_description.process_val(value) self.async_write_ha_state() -# TODO: Add MixerSensor check class +# Add MixerSensor check class Mypy warning +# Definition of "entity_description" in base class "EconetEntity" is incompatible with definition in base +# class "SensorEntity" class MixerSensor(MixerEntity, EconetSensor): """Mixer sensor class.""" @@ -76,6 +82,19 @@ def __init__( super().__init__(description, coordinator, api, idx) +class LambdaSensors(LambdaEntity, EconetSensor): + """Lambda sensor class.""" + + def __init__( + self, + description: EconetSensorEntityDescription, + coordinator: EconetDataCoordinator, + api: Econet300Api, + ): + """Initialize a new instance of the EconetSensor class.""" + super().__init__(description, coordinator, api) + + def create_sensor_entity_description(key: str) -> EconetSensorEntityDescription: """Create ecoNET300 sensor entity based on supplied key.""" _LOGGER.debug("Creating sensor entity description for key: %s", key) @@ -86,35 +105,54 @@ def create_sensor_entity_description(key: str) -> EconetSensorEntityDescription: translation_key=camel_to_snake(key), icon=ENTITY_ICON.get(key, None), native_unit_of_measurement=ENTITY_UNIT_MAP.get(key, None), - state_class=STATE_CLASS_MAP.get(key, None), - suggested_display_precision=ENTITY_PRECISION.get(key, None), + state_class=STATE_CLASS_MAP.get(key, SensorStateClass.MEASUREMENT), + suggested_display_precision=ENTITY_PRECISION.get(key, 0), process_val=ENTITY_VALUE_PROCESSOR.get(key, lambda x: x), ) _LOGGER.debug("Created sensor entity description: %s", entity_description) return entity_description -def create_controller_sensors(coordinator: EconetDataCoordinator, api: Econet300Api): +def create_controller_sensors( + coordinator: EconetDataCoordinator, api: Econet300Api +) -> list[EconetSensor]: """Create controller sensor entities.""" entities: list[EconetSensor] = [] - coordinator_data = coordinator.data.get("regParams", {}) + + data_regParams = coordinator.data.get("regParams", {}) + data_sysParams = coordinator.data.get("sysParams", {}) + for data_key in SENSOR_MAP_KEY["_default"]: - if data_key in coordinator_data: - entities.append( - EconetSensor( - create_sensor_entity_description(data_key), coordinator, api + _LOGGER.debug( + "Processing entity sensor data_key: %s from regParams & sysParams", data_key + ) + if data_key in data_regParams: + entity = EconetSensor( + create_sensor_entity_description(data_key), coordinator, api + ) + entities.append(entity) + _LOGGER.debug( + "Created and appended sensor entity from regParams: %s", entity + ) + elif data_key in data_sysParams: + if data_sysParams.get(data_key) is None: + _LOGGER.warning( + "%s in sysParams is null, sensor will not be created.", data_key ) + continue + entity = EconetSensor( + create_sensor_entity_description(data_key), coordinator, api ) + entities.append(entity) _LOGGER.debug( - "Key: %s mapped, sensor entity will be added", + "Created and appended sensor entity from sysParams: %s", entity + ) + else: + _LOGGER.warning( + "Key: %s is not mapped in regParams or sysParams, sensor entity will not be added.", data_key, ) - continue - _LOGGER.warning( - "Key: %s is not mapped, sensor entity will not be added", - data_key, - ) - + _LOGGER.info("Total sensor entities created: %d", len(entities)) return entities @@ -139,7 +177,7 @@ def create_mixer_sensor_entity_description(key: str) -> EconetSensorEntityDescri translation_key=camel_to_snake(key), icon=ENTITY_ICON.get(key, None), native_unit_of_measurement=ENTITY_UNIT_MAP.get(key, None), - state_class=STATE_CLASS_MAP.get(key, None), + state_class=STATE_CLASS_MAP.get(key, SensorStateClass.MEASUREMENT), device_class=ENTITY_SENSOR_DEVICE_CLASS_MAP.get(key, None), suggested_display_precision=ENTITY_PRECISION.get(key, 0), process_val=ENTITY_VALUE_PROCESSOR.get(key, lambda x: x), @@ -155,7 +193,6 @@ def create_mixer_sensors( entities: list[MixerSensor] = [] for key, mixer_keys in SENSOR_MIXER_KEY.items(): - # Check if all required mixer keys have valid (non-null) values if any( coordinator.data.get("regParams", {}).get(mixer_key) is None @@ -173,6 +210,58 @@ def create_mixer_sensors( return entities +# Create Lambda sensor entity description and Lambda sensor + + +def create_lambda_sensor_entity_description(key: str) -> EconetSensorEntityDescription: + """Create a sensor entity description for a Lambda.""" + _LOGGER.debug("Creating Lambda entity sensor description for key: %s", key) + entity_description = EconetSensorEntityDescription( + key=key, + translation_key=camel_to_snake(key), + icon=ENTITY_ICON.get(key, None), + native_unit_of_measurement=ENTITY_UNIT_MAP.get(key, None), + state_class=STATE_CLASS_MAP.get(key, None), + device_class=ENTITY_SENSOR_DEVICE_CLASS_MAP.get(key, None), + suggested_display_precision=ENTITY_PRECISION.get(key, 0), + process_val=ENTITY_VALUE_PROCESSOR.get(key, lambda x: x / 10), + ) + _LOGGER.debug("Created LambdaSensors entity description: %s", entity_description) + return entity_description + + +def create_lambda_sensors(coordinator: EconetDataCoordinator, api: Econet300Api): + """Create controller sensor entities.""" + entities: list[LambdaSensors] = [] + sys_params = coordinator.data.get("sysParams", {}) + + # Check if moduleLambdaSoftVer is None + if sys_params.get("moduleLambdaSoftVer") is None: + _LOGGER.info("moduleLambdaSoftVer is None, no lambda sensors will be created") + return entities + + coordinator_data = coordinator.data.get("regParams", {}) + + for data_key in SENSOR_MAP_KEY["lambda"]: + if data_key in coordinator_data: + entities.append( + LambdaSensors( + create_lambda_sensor_entity_description(data_key), coordinator, api + ) + ) + _LOGGER.debug( + "Key: %s mapped, lamda sensor entity will be added", + data_key, + ) + continue + _LOGGER.warning( + "Key: %s is not mapped, lamda sensor entity will not be added", + data_key, + ) + + return entities + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, @@ -185,5 +274,7 @@ async def async_setup_entry( entities: list[EconetSensor] = [] entities.extend(create_controller_sensors(coordinator, api)) entities.extend(create_mixer_sensors(coordinator, api)) + entities.extend(create_lambda_sensors(coordinator, api)) - return async_add_entities(entities) + async_add_entities(entities) + return True diff --git a/custom_components/econet300/strings.json b/custom_components/econet300/strings.json index 6dd248e..f8fd89c 100644 --- a/custom_components/econet300/strings.json +++ b/custom_components/econet300/strings.json @@ -20,7 +20,7 @@ }, "entity": { "binary_sensor": { - "lighter": { + "lighter_works": { "name": "Lighter" }, "weather_control": { @@ -38,6 +38,9 @@ "fan_works": { "name": "Fan" }, + "feeder_works": { + "name": "Feeder" + }, "additional_feeder": { "name": "Additional feeder" }, @@ -52,12 +55,24 @@ }, "boiler_pump": { "name": "Boiler pump" + }, + "main_srv": { + "name": "Econet24.com server" + }, + "wifi": { + "name": "Wi-Fi" + }, + "lan": { + "name": "LAN" } }, "sensor": { "boiler_power": { "name": "Boiler output" }, + "boiler_power_kw": { + "name": "Boiler power" + }, "fuel_level": { "name": "Fuel level" }, @@ -97,8 +112,26 @@ "firing_up_count": { "name": "Firing up count" }, - "main_server": { - "name": "Main server" + "soft_ver": { + "name": "Module ecoNET version" + }, + "controller_id": { + "name": "Controller name" + }, + "module_a_soft_ver": { + "name": "Module A version" + }, + "module_b_soft_ver": { + "name": "Module B version" + }, + "module_c_soft_ver": { + "name": "Module C version" + }, + "module_lambda_soft_ver": { + "name": "Module Lambda version" + }, + "module_panel_soft_ver": { + "name": "Module Panel version" }, "mode": { "name": "Boiler mode", @@ -135,15 +168,15 @@ "pump_fireplace_works": { "name": "Boiler pump" }, - "signal": { + "quality": { "name": "Signal quality" }, + "signal": { + "name": "Signal strength" + }, "servo_mixer1": { "name": "Mixer 1 servo position" }, - "status_wifi": { - "name": "Wireless network connected" - }, "temp_co": { "name": "Heating temperature" }, diff --git a/custom_components/econet300/translations/en.json b/custom_components/econet300/translations/en.json index ae9ac0a..ef135bf 100644 --- a/custom_components/econet300/translations/en.json +++ b/custom_components/econet300/translations/en.json @@ -20,7 +20,7 @@ }, "entity": { "binary_sensor": { - "lighter": { + "lighter_works": { "name": "Lighter" }, "weather_control": { @@ -52,12 +52,24 @@ }, "boiler_pump": { "name": "Boiler pump" + }, + "main_srv": { + "name": "Econet24.com server" + }, + "wifi": { + "name": "Wi-Fi" + }, + "lan": { + "name": "LAN" } }, "sensor": { "boiler_power": { "name": "Boiler output" }, + "boiler_power_kw": { + "name": "Boiler power" + }, "fuel_level": { "name": "Fuel level" }, @@ -82,9 +94,6 @@ "lambda_level": { "name": "Lambda level" }, - "main_server": { - "name": "Main server" - }, "work_at100": { "name": "Work at 100%" }, @@ -97,6 +106,9 @@ "feeder_work": { "name": "Feeder work" }, + "feeder_works": { + "name": "Feeder" + }, "firing_up_count": { "name": "Firing up count" }, @@ -135,15 +147,36 @@ "pump_fireplace_works": { "name": "Boiler pump" }, - "signal": { + "quality": { "name": "Signal quality" }, + "signal": { + "name": "Signal strength" + }, + "soft_ver": { + "name": "Module ecoNET version" + }, + "controller_id": { + "name": "Controller name" + }, + "module_a_soft_ver": { + "name": "Module A version" + }, + "module_b_soft_ver": { + "name": "Module B version" + }, + "module_c_soft_ver": { + "name": "Module C version" + }, + "module_lambda_soft_ver": { + "name": "Module Lambda version" + }, + "module_panel_soft_ver": { + "name": "Module Panel version" + }, "servo_mixer1": { "name": "Mixer 1 servo position" }, - "status_wifi": { - "name": "Wireless network connected" - }, "temp_co": { "name": "Heating temperature" }, diff --git a/custom_components/econet300/translations/pl.json b/custom_components/econet300/translations/pl.json index da6cb41..bc30b98 100644 --- a/custom_components/econet300/translations/pl.json +++ b/custom_components/econet300/translations/pl.json @@ -10,33 +10,69 @@ } }, "error": { - "cannot_connect": "B艂膮d po艂膮czenia", - "invalid_auth": "B艂膮d autoryzacji", - "unknown": "Niespodziewany b艂膮d" + "cannot_connect": "B艂膮d po艂膮czenia", + "invalid_auth": "B艂膮d autoryzacji", + "unknown": "Niespodziewany b艂膮d" }, "abort": { "already_configured": "Urz膮dzenie zosta艂o ju偶 skonfigutowane" } }, - "entity":{ + "entity": { "binary_sensor": { - "weather_control": {"name": "Weather control the boiler"}, - "unseal": { "name": "Unseal" }, - "thermostat": { "name": "Boiler thermostat" }, - "pump_co_works": { "name": "Pump CO" }, - "fan_works": { "name": "Fan" }, - "aditional_feeder": { "name": "Aditional feeder" }, - "pump_fireplace_works": { "name": "Boiler Pump" }, - "pump_cwu_works": { "name": "Pump CWU" }, - "mixer_pump1": { "name": "Mixer 1 pump" }, - "boiler_pump": { "name": "Boiler pump" } + "weather_control": { + "name": "Weather control the boiler" + }, + "unseal": { + "name": "Unseal" + }, + "thermostat": { + "name": "Boiler thermostat" + }, + "pump_co_works": { + "name": "Pump CO" + }, + "fan_works": { + "name": "Fan" + }, + "aditional_feeder": { + "name": "Aditional feeder" + }, + "pump_fireplace_works": { + "name": "Boiler Pump" + }, + "pump_cwu_works": { + "name": "Pump CWU" + }, + "mixer_pump1": { + "name": "Mixer 1 pump" + }, + "boiler_pump": { + "name": "Boiler pump" + }, + "main_srv": { + "name": "Econet24.com server" + }, + "wifi": { + "name": "Wi-Fi" + } }, "sensor": { - "boiler_power": { "name": "Moc kot艂a" }, - "burner_power": { "name": "Burner output" }, - "fuel_level": { "name": "Poziom paliwa" }, - "fan_works": { "name": "Fan" }, - "fan_power": { "name": "Moc nadmuchu" }, + "boiler_power": { + "name": "Moc kot艂a" + }, + "burner_power": { + "name": "Burner output" + }, + "fuel_level": { + "name": "Poziom paliwa" + }, + "fan_works": { + "name": "Fan" + }, + "fan_power": { + "name": "Moc nadmuchu" + }, "lambda_status": { "name": "Lambda status", "state": { @@ -46,11 +82,18 @@ "unknown": "Unknown" } }, - "lambda_set": { "name": "Lambda set" }, - "lambda_level": { "name": "Czujnik Lambda" }, - "mixer_temp1": { "name": "Temp. mieszacza 1" }, - "mixer_set_temp1": { "name": "Temp. zadana mieszacza 1" }, - "main_server": { "name": "G艂贸wny serwer" }, + "lambda_set": { + "name": "Lambda set" + }, + "lambda_level": { + "name": "Czujnik Lambda" + }, + "mixer_temp1": { + "name": "Temp. mieszacza 1" + }, + "mixer_set_temp1": { + "name": "Temp. zadana mieszacza 1" + }, "mode": { "name": "Tryb pracy", "state": { @@ -70,23 +113,54 @@ "unknown": "Unknown" } }, - "pump_cwu_works": { "name": "HUW pump" }, - "pump_fireplace_works": { "name": "Boiler pump" }, - "valve_mixer1": { "name": "Zaw贸r mieszacza 1" }, - "servo_Mixer1": { "name": "Si艂ownik mieszacza 1" }, - "signal": { "name": "Si艂a sygna艂u" }, - "status_wifi": { "name": "Po艂膮czenie z sieci膮 bezprzewodow膮" }, - "temp_co_set": { "name": "Heating target temperature" }, - "temp_co": { "name": "Temperatura kot艂a" }, - "temp_cwu": { "name": "Temperatura CWU" }, - "temp_upper_buffer": { "name": "Temperatura bufora g贸rna" }, - "temp_lower_buffer": { "name": "Temperatura bufora dolna" }, - "temp_flue_gas": { "name": "Temperatura spalin" }, - "temp_external_sensor": { "name": "Temperatura zewn臋trzna" }, - "temp_feeder": {"name": "Temperatura podajnika"}, - "unseal": { "name": "Unseal" }, - "thermostat": { "name": "Thermostat" }, - "weather_control": {"name": "Weather control the boiler"} + "pump_cwu_works": { + "name": "HUW pump" + }, + "pump_fireplace_works": { + "name": "Boiler pump" + }, + "valve_mixer1": { + "name": "Zaw贸r mieszacza 1" + }, + "servo_Mixer1": { + "name": "Si艂ownik mieszacza 1" + }, + "signal": { + "name": "Si艂a sygna艂u" + }, + "temp_co_set": { + "name": "Heating target temperature" + }, + "temp_co": { + "name": "Temperatura kot艂a" + }, + "temp_cwu": { + "name": "Temperatura CWU" + }, + "temp_upper_buffer": { + "name": "Temperatura bufora g贸rna" + }, + "temp_lower_buffer": { + "name": "Temperatura bufora dolna" + }, + "temp_flue_gas": { + "name": "Temperatura spalin" + }, + "temp_external_sensor": { + "name": "Temperatura zewn臋trzna" + }, + "temp_feeder": { + "name": "Temperatura podajnika" + }, + "unseal": { + "name": "Unseal" + }, + "thermostat": { + "name": "Thermostat" + }, + "weather_control": { + "name": "Weather control the boiler" + } } } -} +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1d48776..353b9cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ colorlog==6.8.2 -homeassistant>=2024.9.2 -ruff==0.8.1 +homeassistant>=2024.10.2 +ruff==0.8.3 codespell==2.3.0 \ No newline at end of file