diff --git a/README.md b/README.md index 5f212f6..3063106 100644 --- a/README.md +++ b/README.md @@ -117,19 +117,22 @@ depending on your preference. ### Battery -| Entity name | Unit | Description | -| ------------------------------ | ---- | ----------------------------------------------------------------------------- | -| Battery Power | W | the instantaneous power consumed from (`> 0`) or fed into (`< 0`) the battery | -| Battery Current | A | the instantaneous current flowing from or to the battery | -| Battery Voltage | V | the instantaneous voltage of the battery | -| Battery Temperature | °C | the instantaneous temperature of the battery | -| Battery Cycles | | the recorded full charge/discharge cycles of the battery | -| Battery State of Charge | % | the instantaneous state of charge of the battery | -| Battery State of Charge Target | % | the state of charge of the battery aimed for by the system | -| Battery State of Health | % | the estimated state of health of the battery | -| Battery Stored Energy | Wh | the cumulative energy fed into the battery | -| Battery Used Energy | Wh | the cumulative energy consumed from the battery | -| Battery Status | | the current battery status (incomplete) | +| Entity name | Unit | Description | +| ------------------------------- | ---- | ----------------------------------------------------------------------------- | +| Battery Power | W | the instantaneous power consumed from (`> 0`) or fed into (`< 0`) the battery | +| Battery Current | A | the instantaneous current flowing from or to the battery | +| Battery Voltage | V | the instantaneous voltage of the battery | +| Battery Temperature | °C | the instantaneous temperature of the battery | +| Battery Cycles | | the recorded full charge/discharge cycles of the battery | +| Battery State of Charge | % | the instantaneous state of charge of the battery | +| Battery State of Charge Target | % | the state of charge of the battery aimed for by the system | +| Battery Minimum State of Charge | % | the minimum state of charge of the battery to maintain as an island backup | +| Battery Maximum State of Charge | % | the maximum state of charge of the battery to aim for to improve battery life | +| Battery State of Health | % | the estimated state of health of the battery | +| Battery Stored Energy | Wh | the cumulative energy fed into the battery | +| Battery Used Energy | Wh | the cumulative energy consumed from the battery | +| Battery Status | | the current battery status (incomplete) | +| Next Battery Calibration Date | | the date and time of the next planned battery calibration | ### Household consumers and producers diff --git a/custom_components/rct_power/lib/entities.py b/custom_components/rct_power/lib/entities.py index 419a6ee..57fa7d7 100644 --- a/custom_components/rct_power/lib/entities.py +++ b/custom_components/rct_power/lib/entities.py @@ -13,6 +13,7 @@ from .state_helpers import available_battery_status from .state_helpers import get_first_api_reponse_value_as_absolute_state from .state_helpers import get_first_api_response_value_as_battery_status +from .state_helpers import get_first_api_response_value_as_timestamp from .state_helpers import sum_api_response_values_as_state @@ -158,6 +159,20 @@ def get_matching_names(expression: str): state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.BATTERY, ), + RctPowerSensorEntityDescription( + get_device_info=get_battery_device_info, + key="power_mng.soc_min_island", + name="Battery Minimum State of Charge", + update_priority=EntityUpdatePriority.FREQUENT, + state_class=SensorStateClass.MEASUREMENT, + ), + RctPowerSensorEntityDescription( + get_device_info=get_battery_device_info, + key="power_mng.soc_max", + name="Battery Maximum State of Charge", + update_priority=EntityUpdatePriority.FREQUENT, + state_class=SensorStateClass.MEASUREMENT, + ), RctPowerSensorEntityDescription( get_device_info=get_battery_device_info, key="battery.soc_target", @@ -193,6 +208,14 @@ def get_matching_names(expression: str): update_priority=EntityUpdatePriority.INFREQUENT, state_class=SensorStateClass.TOTAL_INCREASING, ), + RctPowerSensorEntityDescription( + get_device_info=get_battery_device_info, + key="power_mng.bat_next_calib_date", + name="Next Battery Calibration Date", + update_priority=EntityUpdatePriority.INFREQUENT, + device_class=SensorDeviceClass.TIMESTAMP, + get_native_value=get_first_api_response_value_as_timestamp, + ), ] inverter_sensor_entity_descriptions: List[RctPowerSensorEntityDescription] = [ diff --git a/custom_components/rct_power/lib/state_helpers.py b/custom_components/rct_power/lib/state_helpers.py index 3fc483f..bfcfc9b 100644 --- a/custom_components/rct_power/lib/state_helpers.py +++ b/custom_components/rct_power/lib/state_helpers.py @@ -1,9 +1,11 @@ +from datetime import datetime from typing import get_args from typing import Literal from typing import Optional from homeassistant.components.sensor import SensorEntity from homeassistant.helpers.typing import StateType +from homeassistant.util.dt import as_local from .api import ApiResponseValue from .const import BatteryStatusFlag @@ -122,3 +124,26 @@ def get_api_response_values_as_bitfield( values: list[Optional[ApiResponseValue]], ) -> StateType: return "".join(f"{value:b}" for value in values if isinstance(value, int)) + + +# +# Timestamp +# +def get_first_api_response_value_as_timestamp( + entity: SensorEntity, + values: list[Optional[ApiResponseValue]], +) -> StateType: + if len(values) <= 0: + return None + + return get_api_response_value_as_timestamp(entity=entity, value=values[0]) + + +def get_api_response_value_as_timestamp( + entity: SensorEntity, + value: Optional[ApiResponseValue], +) -> StateType: + if isinstance(value, int): + return as_local(datetime.fromtimestamp(value)) + + return None diff --git a/hacs.json b/hacs.json index 2d56312..42d1d7f 100644 --- a/hacs.json +++ b/hacs.json @@ -1,5 +1,6 @@ { "name": "RCT Power", "hacs": "1.6.0", - "homeassistant": "2024.1.2" + "homeassistant": "2024.1.2", + "render_readme": true } diff --git a/info.md b/info.md deleted file mode 100644 index 888a186..0000000 --- a/info.md +++ /dev/null @@ -1,209 +0,0 @@ -[![GitHub Release][releases-shield]][releases] -[![GitHub Activity][commits-shield]][commits] -[![License][license-shield]](LICENSE) - -[![pre-commit][pre-commit-shield]][pre-commit] -[![Black][black-shield]][black] - -[![hacs][hacsbadge]][hacs] -[![Project Maintenance][maintenance-shield]][user_profile] -[![BuyMeCoffee][buymecoffeebadge]][buymecoffee] - -[![Discord][discord-shield]][discord] -[![Community Forum][forum-shield]][forum] - -**This component will set up the following platforms.** - -| Platform | Description | -| -------- | ----------------------------- | -| `sensor` | Show info from RCT Power API. | - -{% if not installed %} - -## Installation - -### Via the HACS integration - -1. Install the "RCT Power" integration using [HACS]. -2. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "RCT Power" - -### Via manual download - -1. Using the tool of choice open the directory (folder) for your HA configuration (where you find `configuration.yaml`). -2. If you do not have a `custom_components` directory (folder) there, you need to create it. -3. In the `custom_components` directory (folder) create a new folder called `rct_power`. -4. Download _all_ the files from the `custom_components/rct_power/` directory (folder) in this repository. -5. Place the files you downloaded in the new directory (folder) you created. -6. Restart Home Assistant -7. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "RCT Power" - -{% endif %} - -## Configuration - -Upon installation the integration accepts the following configuration parameters: - -- `Host name`: The local IP address or host name of the inverter. -- `Port`: The port of the inverter's API, defaults to `8899`. -- `Name`: This name is used as a prefix to the entities created by this integration and can be used to disambiguate multiple inverters. - -After installation the integration allows for the following configuration parameters to be changed: - -- `Frequent polling interval`: The polling interval in seconds for entities updated frequently, defaults to `30`. -- `Infrequent polling interval`: The polling interval in seconds for entities updated infrequently, defaults to `180`. -- `Static polling interval`: The polling interval in seconds for entities updated seldomly, defaults to `3600`. - -## Usage with the built-in energy dashboard - -You can use the entities provided by this integration on Home Assistant's -built-in energy dashboard. For that purpose, navigate to "Configuration" -> -"Energy" and choose the following entities. - -Please note that the entity names in your installation are prefixed with the -inverter name chosen during installation of the integration, so interpret the -entity names mentioned below to be entity name suffixes. - -Also note that after configuration the energy dashboard might take several hours -to show any data, because Home Assistant calculates the underlying statistics -only on an hourly basis. - -### Electricity grid - -| Configuration item | Entity name | Note | -| ------------------ | --------------------------------------- | ------------------------------------------------------ | -| Grid consumption | `Grid Energy Consumption Total` | | -| Return to grid | `Grid Energy Production Absolute Total` | Make sure to use the "Absolute" variant of this entity | - -### Solar panels - -You can configure the two generator strings A and B separately or combined -depending on your preference. - -#### Separate generator strings - -| Configuration item | Entity name | -| ------------------ | ------------------------------------- | -| Solar Production | `Generator A Energy Production Total` | -| Solar Production | `Generator B Energy Production Total` | - -#### Combined generator strings - -| Configuration item | Entity name | -| ------------------ | ---------------------------------------- | -| Solar Production | `All Generators Energy Production Total` | - -### Home Battery Storage - -| Configuration item | Entity name | -| -------------------------------------------------- | ----------------------- | -| Battery systems - Energy going into the battery | `Battery Stored Energy` | -| Battery systems - Energy coming out of the battery | `Battery Used Energy` | - -## Most commonly used entities - -:warning: This list is incomplete and the interpretations might be incorrect due to a lack of official documentation. - -### Grid - -| Entity name | Unit | Description | -| -------------------------------------------- | ---- | -------------------------------------------------------------------------------- | -| Grid Frequency | Hz | the instantaneous overall grid frequency | -| Grid Frequency P1/P2/P3 | Hz | the instantaneous grid frequency on phase 1/2/3 | -| Grid Power | W | the instantaneous power consumed from (`> 0`) or fed into (`< 0`) the grid | -| Grid Power P1/P2/P3 | W | the instantaneous power consumed from or fed into the grid on phase 1/2/3 | -| Grid Voltage P1/P2/P3 | W | the instantaneous grid voltage on phase 1/2/3 | -| Grid Energy Consumption Day/Month/Year/Total | Wh | the cumulative energy consumed from the grid | -| Grid Energy Production Day/Month/Year/Total | Wh | the cumulative energy fed into the grid | -| Grid Energy Production Absolute Total | kWh | the absolute value of the cumulative energy fed into the grid since installation | -| Grid Maximum Feed Power | W | the maximum power the inverter is configured to feed into the grid | - -### Battery - -| Entity name | Unit | Description | -| ------------------------------ | ---- | ----------------------------------------------------------------------------- | -| Battery Power | W | the instantaneous power consumed from (`> 0`) or fed into (`< 0`) the battery | -| Battery Current | A | the instantaneous current flowing from or to the battery | -| Battery Voltage | V | the instantaneous voltage of the battery | -| Battery Temperature | °C | the instantaneous temperature of the battery | -| Battery Cycles | | the recorded full charge/discharge cycles of the battery | -| Battery State of Charge | % | the instantaneous state of charge of the battery | -| Battery State of Charge Target | % | the state of charge of the battery aimed for by the system | -| Battery State of Health | % | the estimated state of health of the battery | -| Battery Stored Energy | Wh | the cumulative energy fed into the battery | -| Battery Used Energy | Wh | the cumulative energy consumed from the battery | -| Battery Status | | the current battery status (incomplete) | - -### Household consumers and producers - -| Entity name | Unit | Description | -| ------------------------------------------------ | ---- | ---------------------------------------------------------------------- | -| Consumer Energy Consumption Day/Month/Year/Total | Wh | the cumulative energy consumed by the household (from any source) | -| Consumer Power | W | the instantaneous power consumed by the household | -| Consumer Power P1/P2/P3 | W | the instantaneous power consumed by the household on phase 1/2/3 | -| External Energy Production Day/Month/Year/Total | Wh | the cumulative energy produced by the external producer (if installed) | - -### Photovoltaic generators - -| Entity name | Unit | Description | -| -------------------------------------------------- | ---- | --------------------------------------------------------------------- | -| Generator A Energy Production Day/Month/Year/Total | Wh | the cumulative energy produced by generator string A | -| Generator B Energy Production Day/Month/Year/Total | Wh | the cumulative energy produced by generator string B | -| All Generators Energy Production Total | Wh | the sum of the cumulative energy produced by both generator strings | -| Generator A Power | W | the instantaneous power produced by generator string A | -| Generator B Power | W | the instantaneous power produced by generator string B | -| All Generators Power | W | the sum of the instantaneous power produced by both generator strings | -| Generator A Voltage | V | the instantaneous voltage produced by generator string A | -| Generator B Voltage | V | the instantaneous voltage produced by generator string B | -| Generator Maximum Power | W | the configured combined maximum power of both generator strings | -| Insulation Resistance | Ohm | | -| Insulation Resistance Positive/Negative Input | Ohm | | -| Minimum Insulation Resistance | Ohm | | - -### Inverter - -| Entity name | Unit | Description | -| ----------------------------------------------- | ---- | --------------------------------------------------------------- | -| Inverter AC Power | W | the instantaneous AC power transmitted by the inverter | -| Inverter Energy Production Day/Month/Year/Total | Wh | the cumulative energy produced by the inverter | -| Inverter Power P1/P2/P3 | W | the instantaneous power consumed or generated by the inverter | -| Inverter Serial Number | | the serial number of the inverter | -| Faults | | a bitmask of the failures reported by the inverter | -| Core Temperature | °C | the instantaneous temperature of the invertor core | -| Heat Sink Temperature | °C | the instantaneous temperature of the invertor heat sink | -| Heat Sink (battery actuator) Temperature | °C | the instantaneous temperature of the battery actuator heat sink | - -## Contributions are welcome! - -If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md) - -## Credits - -This would have been a lot more difficult if not for [@svalouch](https://github.com/svalouch)'s [`python-rctclient`](https://github.com/svalouch/python-rctclient) library. Thank you! - -This project was generated from [@oncleben31](https://github.com/oncleben31)'s [Home Assistant Custom Component Cookiecutter](https://github.com/oncleben31/cookiecutter-homeassistant-custom-component) template. - -Code template was mainly taken from [@Ludeeus](https://github.com/ludeeus)'s [integration_blueprint][integration_blueprint] template - ---- - -[integration_blueprint]: https://github.com/custom-components/integration_blueprint -[black]: https://github.com/psf/black -[black-shield]: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge -[buymecoffee]: https://www.buymeacoffee.com/weltenwort -[buymecoffeebadge]: https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?style=for-the-badge -[commits-shield]: https://img.shields.io/github/commit-activity/y/weltenwort/home-assistant-rct-power-integration.svg?style=for-the-badge -[commits]: https://github.com/weltenwort/home-assistant-rct-power-integration/commits/main -[hacs]: https://hacs.xyz -[hacsbadge]: https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge -[discord]: https://discord.gg/Qa5fW2R -[discord-shield]: https://img.shields.io/discord/330944238910963714.svg?style=for-the-badge -[exampleimg]: example.png -[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge -[forum]: https://community.home-assistant.io/ -[license-shield]: https://img.shields.io/github/license/weltenwort/home-assistant-rct-power-integration.svg?style=for-the-badge -[maintenance-shield]: https://img.shields.io/badge/maintainer-%40weltenwort-blue.svg?style=for-the-badge -[pre-commit]: https://github.com/pre-commit/pre-commit -[pre-commit-shield]: https://img.shields.io/badge/pre--commit-enabled-brightgreen?style=for-the-badge -[releases-shield]: https://img.shields.io/github/release/weltenwort/home-assistant-rct-power-integration.svg?style=for-the-badge -[releases]: https://github.com/weltenwort/home-assistant-rct-power-integration/releases -[user_profile]: https://github.com/weltenwort diff --git a/pyproject.toml b/pyproject.toml index 7f2abc4..acc2426 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,3 +28,6 @@ typeCheckingMode = "strict" reportMissingTypeStubs = "warning" reportImportCycles = "warning" useLibraryCodeForTypes = true + +[tool.pytest.ini_options] +asyncio_mode = "auto" diff --git a/setup.cfg b/setup.cfg index 957ca96..e180ee2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,14 +30,3 @@ force_sort_within_sections = true default_section = THIRDPARTY known_first_party = custom_components.rct_power, tests combine_as_imports = true - -[tool:pytest] -addopts = -qq --cov=custom_components.rct_power -console_output_style = count - -[coverage:run] -branch = False - -[coverage:report] -show_missing = true -fail_under = 100