Skip to content

Commit

Permalink
Implement selection of sub profiles in config flow
Browse files Browse the repository at this point in the history
  • Loading branch information
bramstroker committed Aug 7, 2022
1 parent 3d1919b commit a39cd99
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 3 deletions.
55 changes: 54 additions & 1 deletion custom_components/powercalc/config_flow.py
Expand Up @@ -49,6 +49,7 @@
CONF_START_TIME,
CONF_STATES_POWER,
CONF_SUB_GROUPS,
CONF_SUB_PROFILE,
CONF_UPDATE_FREQUENCY,
CONF_VALUE,
CONF_VALUE_TEMPLATE,
Expand All @@ -60,7 +61,6 @@
from .errors import StrategyConfigurationError
from .power_profile.library import ModelInfo, ProfileLibrary
from .power_profile.model_discovery import autodiscover_model
from .power_profile.power_profile import PowerProfile
from .sensors.daily_energy import DEFAULT_DAILY_UPDATE_FREQUENCY
from .strategy.factory import PowerCalculatorStrategyFactory
from .strategy.strategy_interface import PowerCalculationStrategyInterface
Expand Down Expand Up @@ -385,6 +385,16 @@ async def async_step_lut_model(
errors = {}
if user_input is not None:
self.sensor_config.update({CONF_MODEL: user_input.get(CONF_MODEL)})
library = ProfileLibrary(self.hass)
profile = await library.get_profile(
ModelInfo(
self.sensor_config.get(CONF_MANUFACTURER),
self.sensor_config.get(CONF_MODEL)
)
)
sub_profiles = await library.get_subprofile_listing(profile)
if sub_profiles:
return await self.async_step_lut_subprofile()
errors = await self.validate_strategy_config()
if not errors:
return self.create_config_entry()
Expand All @@ -397,6 +407,31 @@ async def async_step_lut_model(
errors=errors,
)

async def async_step_lut_subprofile(
self, user_input: dict[str, str] = None
) -> FlowResult:
errors = {}
if user_input is not None:
# Append the sub profile to the model
model = f"{self.sensor_config.get(CONF_MODEL)}/{user_input.get(CONF_SUB_PROFILE)}"
self.sensor_config[CONF_MODEL] = model
errors = await self.validate_strategy_config()
if not errors:
return self.create_config_entry()

model_info = ModelInfo(
self.sensor_config.get(CONF_MANUFACTURER),
self.sensor_config.get(CONF_MODEL)
)
return self.async_show_form(
step_id="lut_subprofile",
data_schema=await _create_lut_schema_subprofile(
self.hass,
model_info
),
errors=errors,
)

async def validate_strategy_config(self) -> dict:
strategy_name = self.sensor_config.get(CONF_MODE)
strategy = await _create_strategy_object(
Expand Down Expand Up @@ -632,6 +667,24 @@ def _create_lut_schema_model(hass: HomeAssistant, manufacturer: str) -> vol.Sche
}
)

async def _create_lut_schema_subprofile(hass: HomeAssistant, model_info: ModelInfo) -> vol.Schema:
"""Create LUT schema"""
library = ProfileLibrary(hass)
profile = await library.get_profile(model_info)
sub_profiles = [
selector.SelectOptionDict(value=sub_profile, label=sub_profile)
for sub_profile in await library.get_subprofile_listing(profile)
]
return vol.Schema(
{
vol.Required(CONF_SUB_PROFILE): selector.SelectSelector(
selector.SelectSelectorConfig(
options=sub_profiles, mode=selector.SelectSelectorMode.DROPDOWN
)
)
}
)


def _build_strategy_config(
strategy: str, source_entity_id: str, user_input: dict[str, str] = None
Expand Down
1 change: 1 addition & 0 deletions custom_components/powercalc/const.py
Expand Up @@ -69,6 +69,7 @@
CONF_ON_TIME = "on_time"
CONF_TEMPLATE = "template"
CONF_SENSOR_TYPE = "sensor_type"
CONF_SUB_PROFILE = "sub_profile"
CONF_UPDATE_FREQUENCY = "update_frequency"
CONF_VALUE = "value"
CONF_VALUE_TEMPLATE = "value_template"
Expand Down
8 changes: 8 additions & 0 deletions custom_components/powercalc/power_profile/library.py
Expand Up @@ -61,6 +61,14 @@ def get_model_listing(self, manufacturer: str) -> list[str]:
continue
models.extend(os.listdir(manufacturer_dir))
return sorted(models)

async def get_subprofile_listing(self, profile: PowerProfile) -> list[str]:
"""Get listing op possible sub profiles"""
return sorted(list(
next(
os.walk(profile.get_model_directory())
)[1]
))

async def get_profile(
self, model_info: ModelInfo, custom_directory: str | None = None
Expand Down
2 changes: 1 addition & 1 deletion custom_components/powercalc/power_profile/power_profile.py
Expand Up @@ -55,7 +55,7 @@ def load_sub_profile(self, sub_profile: str) -> None:

self.sub_profile = sub_profile

def get_lut_directory(self) -> str:
def get_model_directory(self) -> str:
if self.linked_lut:
return os.path.join(os.path.dirname(__file__), "../data", self.linked_lut)

Expand Down
2 changes: 1 addition & 1 deletion custom_components/powercalc/strategy/lut.py
Expand Up @@ -77,7 +77,7 @@ async def get_lookup_dictionary(
return lookup_dict

def get_lut_file(self, power_profile: PowerProfile, color_mode: str):
path = os.path.join(power_profile.get_lut_directory(), f"{color_mode}.csv")
path = os.path.join(power_profile.get_model_directory(), f"{color_mode}.csv")

gzip_path = f"{path}.gz"
if os.path.exists(gzip_path):
Expand Down
7 changes: 7 additions & 0 deletions custom_components/powercalc/strings.json
Expand Up @@ -121,6 +121,13 @@
"data": {
"model": "Model ID"
}
},
"lut_subprofile": {
"title": "LUT config",
"description": "This model has multiple sub profiles. Select one that suites your device",
"data": {
"sub_profile": "Sub profile"
}
}
},
"error": {
Expand Down
7 changes: 7 additions & 0 deletions custom_components/powercalc/translations/en.json
Expand Up @@ -99,6 +99,13 @@
"description": "Select the device model",
"title": "LUT config"
},
"lut_subprofile": {
"data": {
"sub_profile": "Sub profile"
},
"description": "This model has multiple sub profiles. Select one that suites your device",
"title": "LUT config"
},
"user": {
"data": {
"sensor_type": "Sensor type"
Expand Down
7 changes: 7 additions & 0 deletions custom_components/powercalc/translations/nl.json
Expand Up @@ -99,6 +99,13 @@
"description": "Select het model",
"title": "LUT configuratie"
},
"lut_subprofile": {
"data": {
"sub_profile": "Sub profiel"
},
"description": "Dit model heeft meerdere profielen. Selecteer de juiste voor je apparaat",
"title": "LUT config"
},
"user": {
"data": {
"sensor_type": "Sensor type"
Expand Down
11 changes: 11 additions & 0 deletions tests/power_profile/test_library.py
Expand Up @@ -18,6 +18,17 @@ async def test_model_listing(hass: HomeAssistant):
assert "LCT010" in models
assert "LCA007" in models

async def test_get_subprofile_listing(hass: HomeAssistant):
library = ProfileLibrary(hass)
profile = await library.get_profile(ModelInfo("yeelight", "YLDL01YL"))
sub_profiles = await library.get_subprofile_listing(profile)
assert sub_profiles == ["ambilight", "downlight"]

async def test_get_subprofile_listing_empty_list(hass: HomeAssistant):
library = ProfileLibrary(hass)
profile = await library.get_profile(ModelInfo("signify", "LCT010"))
sub_profiles = await library.get_subprofile_listing(profile)
assert sub_profiles == []

async def test_non_existing_manufacturer_returns_empty_model_list(hass: HomeAssistant):
library = ProfileLibrary(hass)
Expand Down
33 changes: 33 additions & 0 deletions tests/test_config_flow.py
Expand Up @@ -43,6 +43,7 @@
CONF_POWER_TEMPLATE,
CONF_SENSOR_TYPE,
CONF_STATES_POWER,
CONF_SUB_PROFILE,
CONF_UPDATE_FREQUENCY,
CONF_VALUE,
CONF_VOLTAGE,
Expand Down Expand Up @@ -280,6 +281,38 @@ async def test_lut_autodiscover_flow_not_confirmed(hass: HomeAssistant):
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["step_id"] == "lut_manufacturer"

async def test_lut_flow_with_sub_profiles(hass: HomeAssistant):
light_entity = MockLight("test", STATE_ON, DEFAULT_UNIQUE_ID)
await create_mock_light_entity(hass, light_entity)

result = await _goto_virtual_power_strategy_step(hass, CalculationStrategy.LUT)

result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_MANUFACTURER: "yeelight"}
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_MODEL: "YLDL01YL"}
)

assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["step_id"] == "lut_subprofile"
data_schema: vol.Schema = result["data_schema"]
model_select: SelectSelector = data_schema.schema["sub_profile"]
select_options = model_select.config["options"]
assert {"value": "ambilight", "label": "ambilight"} in select_options
assert {"value": "downlight", "label": "downlight"} in select_options

result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_SUB_PROFILE: "ambilight"}
)

assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
_assert_default_virtual_power_entry_data(
CalculationStrategy.LUT,
result["data"],
{CONF_MANUFACTURER: "yeelight", CONF_MODEL: "YLDL01YL/ambilight"},
)


async def test_daily_energy_mandatory_fields_not_supplied(hass: HomeAssistant):
result = await _select_sensor_type(hass, SensorType.DAILY_ENERGY)
Expand Down

0 comments on commit a39cd99

Please sign in to comment.