Skip to content

Commit

Permalink
Merge pull request #739 from canton7/feature/version-select
Browse files Browse the repository at this point in the history
Add the option to select your manager version
  • Loading branch information
canton7 authored Jan 22, 2025
2 parents 352fee1 + 0603c93 commit b5a7cf1
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 57 deletions.
55 changes: 32 additions & 23 deletions custom_components/foxess_modbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import uuid
from typing import Any

from homeassistant.components.energy import data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import UNDEFINED
Expand Down Expand Up @@ -135,21 +134,25 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->

_LOGGER.debug("Migrating from version %s", config_entry.version)

if config_entry.version == 1:
data = copy.deepcopy(dict(config_entry.data))
version = config_entry.version
new_options = UNDEFINED

if version == 1:
# Introduce adapter selection
new_data = {
INVERTERS: {},
CONFIG_SAVE_TIME: config_entry.data[CONFIG_SAVE_TIME],
CONFIG_SAVE_TIME: data[CONFIG_SAVE_TIME],
}
if config_entry.options:
inverter_options = {
POLL_RATE: config_entry.options[POLL_RATE],
MAX_READ: config_entry.options[MAX_READ],
}
options: dict[str, Any] = {INVERTERS: {}}
new_options: dict[str, Any] = {INVERTERS: {}}
else:
inverter_options = {}
options = UNDEFINED
new_options = UNDEFINED

for modbus_type, modbus_type_inverters in config_entry.data.items():
if modbus_type in [TCP, UDP, SERIAL]: # Didn't have RTU_OVER_TCP then
Expand All @@ -176,12 +179,12 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
inverter_id = str(uuid.uuid4())
new_data[INVERTERS][inverter_id] = inverter
if inverter_options:
options[INVERTERS][inverter_id] = inverter_options
new_options[INVERTERS][inverter_id] = inverter_options

hass.config_entries.async_update_entry(config_entry, data=new_data, options=options)
config_entry.version = 2
data = new_data
version = 2

if config_entry.version == 2:
if version == 2:
# Fix a badly-set-up energy dashboard
energy_manager = await data.async_get_manager(hass)
if energy_manager.data is not None:
Expand All @@ -199,45 +202,51 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
flow_to.setdefault("entity_energy_price", None)
flow_to.setdefault("number_energy_price", None)
await energy_manager.async_update(energy_data)
config_entry.version = 3

if config_entry.version == 3:
version = 3

if version == 3:
# Add entity ID prefix
for inverter in config_entry.data.get(INVERTERS, {}).values():
for inverter in data.get(INVERTERS, {}).values():
inverter[ENTITY_ID_PREFIX] = inverter[FRIENDLY_NAME]
config_entry.version = 4

if config_entry.version == 4:
version = 4

if version == 4:
# Old versions accidentally mutated ConfigEntry.data
for inverter in config_entry.data.get(INVERTERS, {}).values():
for inverter in data.get(INVERTERS, {}).values():
inverter.pop(POLL_RATE, None)
inverter.pop(MAX_READ, None)
if inverter[FRIENDLY_NAME] is None:
inverter[FRIENDLY_NAME] = ""
if inverter[ENTITY_ID_PREFIX] is None:
inverter[ENTITY_ID_PREFIX] = ""
config_entry.version = 5

if config_entry.version == 5:
version = 5

if version == 5:
# Having "TCP" / "UDP" / "SERIAL" in all-caps is annoying for translations in the config flow
# Also change "TCP+RTU" to "rtu_over_tcp" (to remove "+", which makes translations annoying)
for inverter in config_entry.data.get(INVERTERS, {}).values():
for inverter in data.get(INVERTERS, {}).values():
if inverter[MODBUS_TYPE] == "TCP+RTU":
inverter[MODBUS_TYPE] = "rtu_over_tcp"
else:
inverter[MODBUS_TYPE] = inverter[MODBUS_TYPE].lower()
config_entry.version = 6

if config_entry.version == 6:
version = 6

if version == 6:
# We still have users with entity ID prefixes which aren't valid in entity IDs. HA will automatically convert
# them, but this causes the charge period card to complain. Fix them once and for all.
for inverter in config_entry.data.get(INVERTERS, {}).values():
for inverter in data.get(INVERTERS, {}).values():
inverter[UNIQUE_ID_PREFIX] = inverter[ENTITY_ID_PREFIX]
if inverter[ENTITY_ID_PREFIX]:
inverter[ENTITY_ID_PREFIX] = slugify(inverter[ENTITY_ID_PREFIX], separator="_").rstrip("_")
config_entry.version = 7

_LOGGER.info("Migration to version %s successful", config_entry.version)
version = 7

_LOGGER.info("Migration from version %s to version %s successful", config_entry.version, version)
hass.config_entries.async_update_entry(entry=config_entry, data=data, options=new_options, version=version)
return True


Expand Down
3 changes: 3 additions & 0 deletions custom_components/foxess_modbus/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
INVERTER_MODEL = "inverter_model"
INVERTER_BASE = "inverter_base"
INVERTER_CONN = "inverter_conn"
# The inverter manager version to use. This is the version corresponding to InverterModelConnectionTypeProfile.versions,
# i.e. the upper bound of a range of versions we support. None means use the latest.
INVERTER_VERSION = "inverter_version"
INVERTERS = "inverters"

CONFIG_SAVE_TIME = "save_time"
Expand Down
32 changes: 32 additions & 0 deletions custom_components/foxess_modbus/flow/options_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

from ..const import ADAPTER_ID
from ..const import CONFIG_ENTRY_TITLE
from ..const import INVERTER_VERSION
from ..const import INVERTERS
from ..const import MAX_READ
from ..const import MODBUS_TYPE
from ..const import POLL_RATE
from ..const import ROUND_SENSOR_VALUES
from ..inverter_adapters import ADAPTERS
from ..inverter_profiles import inverter_connection_type_profile_from_config
from .adapter_flow_segment import AdapterFlowSegment
from .flow_handler_mixin import FlowHandlerMixin

Expand Down Expand Up @@ -117,15 +119,23 @@ async def async_step_inverter_advanced_options(self, user_input: dict[str, Any]
current_adapter = ADAPTERS[combined_config_options[ADAPTER_ID]]

async def body(user_input: dict[str, Any]) -> FlowResult:
version = user_input.get("version")
if version is None or version == "latest":
options.pop(INVERTER_VERSION, None)
else:
options[INVERTER_VERSION] = version

poll_rate = user_input.get("poll_rate")
if poll_rate is not None:
options[POLL_RATE] = poll_rate
else:
options.pop(POLL_RATE, None)

if user_input.get("round_sensor_values", False):
options[ROUND_SENSOR_VALUES] = True
else:
options.pop(ROUND_SENSOR_VALUES, None)

max_read = user_input.get("max_read")
if max_read is not None:
options[MAX_READ] = max_read
Expand All @@ -136,6 +146,28 @@ async def body(user_input: dict[str, Any]) -> FlowResult:

schema_parts: dict[Any, Any] = {}

versions = sorted(inverter_connection_type_profile_from_config(combined_config_options).versions.keys())
if len(versions) > 1:
version_options = []
prev_version = None
# The last element will be None, which means "latest"
for version in versions[:-1]:
label = f"Up to {version}" if prev_version is None else f"{prev_version} - {version}"
version_options.append({"label": label, "value": str(version)})
prev_version = version
version_options.append(
{"label": f"{versions[-2]} and higher", "value": "latest"}
) # hass can't cope with None

schema_parts[vol.Required("version", default=options.get(INVERTER_VERSION, "latest"))] = selector(
{
"select": {
"options": list(reversed(version_options)),
"mode": "dropdown",
}
}
)

schema_parts[vol.Required("round_sensor_values", default=options.get(ROUND_SENSOR_VALUES, False))] = selector(
{"boolean": {}}
)
Expand Down
Loading

0 comments on commit b5a7cf1

Please sign in to comment.