Skip to content

Commit

Permalink
Implement alternative config flow
Browse files Browse the repository at this point in the history
  • Loading branch information
bramstroker committed Dec 3, 2022
1 parent e8f956a commit c8a29e8
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 91 deletions.
37 changes: 20 additions & 17 deletions custom_components/powercalc/config_flow.py
Expand Up @@ -439,13 +439,7 @@ async def async_step_library(self, user_input: dict[str, str] = None) -> FlowRes
CONF_MODEL: self.power_profile.model,
}
)
if (
self.power_profile.has_sub_profiles
and not self.power_profile.sub_profile_select
):
return await self.async_step_sub_profile()

return await self.async_step_power_advanced()
return await self.async_step_post_library(user_input)

return await self.async_step_manufacturer()

Expand Down Expand Up @@ -497,11 +491,8 @@ async def async_step_model(self, user_input: dict[str, str] = None) -> FlowResul
self.sensor_config.get(CONF_MODEL),
)
)
if profile.has_sub_profiles:
return await self.async_step_sub_profile()
errors = await self.validate_strategy_config()
if not errors:
return await self.async_step_power_advanced()
self.power_profile = profile
return await self.async_step_post_library()

return self.async_show_form(
step_id="model",
Expand All @@ -514,6 +505,19 @@ async def async_step_model(self, user_input: dict[str, str] = None) -> FlowResul
errors=errors,
)

async def async_step_post_library(self, user_input: dict[str, str] = None):
"""Handles the logic after the user either selected manufacturer/model himself or confirmed autodiscovered"""
if (
self.power_profile.has_sub_profiles
and not self.power_profile.sub_profile_select
):
return await self.async_step_sub_profile()

if self.power_profile.needs_fixed_config:
return await self.async_step_fixed()

return await self.async_step_power_advanced()

async def async_step_sub_profile(
self, user_input: dict[str, str] = None
) -> FlowResult:
Expand Down Expand Up @@ -549,9 +553,9 @@ async def async_step_power_advanced(
)

async def validate_strategy_config(self) -> dict:
strategy_name = self.sensor_config.get(CONF_MODE)
strategy_name = self.sensor_config.get(CONF_MODE) or self.power_profile.supported_strategies[0]
strategy = await _create_strategy_object(
self.hass, strategy_name, self.sensor_config, self.source_entity
self.hass, strategy_name, self.sensor_config, self.source_entity, self.power_profile
)
try:
await strategy.validate_config()
Expand Down Expand Up @@ -687,12 +691,11 @@ def build_options_schema(self) -> vol.Schema:


async def _create_strategy_object(
hass: HomeAssistant, strategy: str, config: dict, source_entity: SourceEntity
hass: HomeAssistant, strategy: str, config: dict, source_entity: SourceEntity, power_profile: PowerProfile | None = None
) -> PowerCalculationStrategyInterface:
"""Create the calculation strategy object"""
factory = PowerCalculatorStrategyFactory(hass)
power_profile: PowerProfile | None = None
if strategy == CalculationStrategy.LUT:
if power_profile is None and CONF_MANUFACTURER in config:
power_profile = await ProfileLibrary.factory(hass).get_profile(
ModelInfo(config.get(CONF_MANUFACTURER), config.get(CONF_MODEL))
)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/powercalc/power_profile/power_profile.py
Expand Up @@ -162,7 +162,7 @@ def needs_fixed_config(self) -> bool:
Used for smart switches which only provides standby power values.
This indicates the user must supply the power values in the config flow
"""
return self.is_strategy_supported(CalculationStrategy.FIXED) and not self.fixed_mode_config
return self.is_strategy_supported(CalculationStrategy.FIXED) and not self._json_data.get("fixed_config")

@property
def device_type(self) -> DeviceType:
Expand Down
2 changes: 1 addition & 1 deletion tests/common.py
Expand Up @@ -80,7 +80,7 @@ def create_discoverable_light(
return light


async def run_powercalc_setup_yaml_config(
async def run_powercalc_setup(
hass: HomeAssistant,
sensor_config: list[ConfigType] | ConfigType,
domain_config: ConfigType | None = None,
Expand Down
4 changes: 2 additions & 2 deletions tests/power_profile/device_types/test_infrared_light.py
Expand Up @@ -13,7 +13,7 @@
CONF_MODEL,
)
from custom_components.test.light import MockLight
from tests.common import get_test_profile_dir, run_powercalc_setup_yaml_config
from tests.common import get_test_profile_dir, run_powercalc_setup

from ...common import create_mock_light_entity

Expand All @@ -33,7 +33,7 @@ async def test_infrared_light(hass: HomeAssistant):

await create_mock_light_entity(hass, light_mock)

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_ENTITY_ID: light_id,
Expand Down
4 changes: 2 additions & 2 deletions tests/power_profile/device_types/test_media_player.py
Expand Up @@ -14,7 +14,7 @@
CONF_MANUFACTURER,
CONF_MODEL,
)
from tests.common import get_test_profile_dir, run_powercalc_setup_yaml_config
from tests.common import get_test_profile_dir, run_powercalc_setup


async def test_media_player(hass: HomeAssistant):
Expand Down Expand Up @@ -47,7 +47,7 @@ async def test_media_player(hass: HomeAssistant):

power_sensor_id = "sensor.nest_mini_power"

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_ENTITY_ID: entity_id,
Expand Down
75 changes: 70 additions & 5 deletions tests/power_profile/device_types/test_smart_switch.py
Expand Up @@ -6,15 +6,16 @@
mock_device_registry,
mock_registry,
)

from custom_components.powercalc.config_flow import CONF_CONFIRM_AUTODISCOVERED_MODEL
from custom_components.powercalc.const import (
CONF_CUSTOM_MODEL_DIRECTORY,
CONF_FIXED,
CONF_MANUFACTURER,
CONF_MODEL,
CONF_POWER,
DOMAIN,
)
from tests.common import get_test_profile_dir, run_powercalc_setup_yaml_config
from tests.common import get_test_profile_dir, run_powercalc_setup


async def test_smart_switch(hass: HomeAssistant):
Expand Down Expand Up @@ -47,7 +48,7 @@ async def test_smart_switch(hass: HomeAssistant):

power_sensor_id = "sensor.oven_device_power"

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_ENTITY_ID: switch_id,
Expand Down Expand Up @@ -96,15 +97,15 @@ async def test_smart_switch_power_input_yaml(hass: HomeAssistant):
mock_device_registry(
hass,
{
"ikea-device": DeviceEntry(
"ikea-device-id": DeviceEntry(
id="ikea-device-id", manufacturer=manufacturer, model=model
)
},
)

power_sensor_id = "sensor.heater_device_power"

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_ENTITY_ID: switch_id,
Expand All @@ -130,3 +131,67 @@ async def test_smart_switch_power_input_yaml(hass: HomeAssistant):
await hass.async_block_till_done()

assert hass.states.get(power_sensor_id).state == "0.52"


async def test_smart_switch_power_input_gui_config_flow(hass: HomeAssistant):
"""
Test a smart switch can be setup with GUI and a fixed power value for the appliance configured by the user
The values for standby power on and off should be taken from the power profile library.
The fixed power value from the user should be added to the total power consumption. standby_power_on + power
"""
switch_id = "switch.heater"
manufacturer = "IKEA"
model = "TRADFRI control outlet"

mock_registry(
hass,
{
switch_id: RegistryEntry(
entity_id=switch_id,
unique_id="1234",
platform="switch",
device_id="ikea-device-id",
),
},
)
mock_device_registry(
hass,
{
"ikea-device-id": DeviceEntry(
id="ikea-device-id", manufacturer=manufacturer, model=model
)
},
)

power_sensor_id = "sensor.heater_device_power"

await run_powercalc_setup(hass, {})

# Retrieve the discovery flow
flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN)
flow = flows[0]

assert flow["step_id"] == "library"
result = await hass.config_entries.flow.async_configure(
flow["flow_id"], {CONF_CONFIRM_AUTODISCOVERED_MODEL: True}
)

# After confirming the manufacturer/model we must be directed to the fixed config step
assert result["step_id"] == "fixed"
await hass.config_entries.flow.async_configure(
flow["flow_id"], {CONF_POWER: 50}
)

power_state = hass.states.get(power_sensor_id)
assert power_state
assert power_state.state == "unavailable"

hass.states.async_set(switch_id, STATE_ON)
await hass.async_block_till_done()

assert hass.states.get(power_sensor_id).state == "50.80"

hass.states.async_set(switch_id, STATE_OFF)
await hass.async_block_till_done()

assert hass.states.get(power_sensor_id).state == "0.40"
20 changes: 10 additions & 10 deletions tests/sensors/test_daily_energy.py
Expand Up @@ -47,7 +47,7 @@
assert_entity_state,
create_input_boolean,
create_input_number,
run_powercalc_setup_yaml_config,
run_powercalc_setup,
)


Expand Down Expand Up @@ -91,7 +91,7 @@ async def test_create_daily_energy_sensor_unit_prefix_watt(


async def test_daily_energy_sensor_from_kwh_value(hass: HomeAssistant):
await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_PLATFORM: DOMAIN,
Expand Down Expand Up @@ -130,7 +130,7 @@ async def test_daily_energy_sensor_also_creates_power_sensor(hass: HomeAssistant
When the user configured the value in W and the on_time is always on,
then a power sensor should also be created
"""
await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_NAME: "IP camera upstairs",
Expand All @@ -153,7 +153,7 @@ async def test_daily_energy_sensor_also_creates_power_sensor(hass: HomeAssistant


async def test_power_sensor_not_created_when_not_on_whole_day(hass: HomeAssistant):
await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_NAME: "IP camera upstairs",
Expand Down Expand Up @@ -241,7 +241,7 @@ async def test_template_value(hass: HomeAssistant):
await create_input_number(hass, "test", 50)

update_frequency = 1800
await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_NAME: "Router",
Expand Down Expand Up @@ -308,7 +308,7 @@ async def test_config_flow_decimal_value(hass: HomeAssistant):


async def test_reset_service(hass: HomeAssistant):
await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_NAME: "IP camera upstairs",
Expand Down Expand Up @@ -354,7 +354,7 @@ async def test_restore_state(hass: HomeAssistant):
],
)

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_NAME: "My daily",
Expand All @@ -378,7 +378,7 @@ async def test_restore_state_catches_decimal_conversion_exception(hass: HomeAssi
],
)

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_NAME: "My daily",
Expand All @@ -392,7 +392,7 @@ async def test_restore_state_catches_decimal_conversion_exception(hass: HomeAssi


async def test_small_update_frequency_updates_correctly(hass: HomeAssistant):
await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_NAME: "Router",
Expand All @@ -414,7 +414,7 @@ async def test_name_and_entity_id_can_be_inherited_from_source_entity(
hass: HomeAssistant,
):
await create_input_boolean(hass, "test")
await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_ENTITY_ID: "input_boolean.test",
Expand Down
8 changes: 4 additions & 4 deletions tests/sensors/test_energy.py
Expand Up @@ -23,7 +23,7 @@
from ..common import (
create_input_boolean,
get_simple_fixed_config,
run_powercalc_setup_yaml_config,
run_powercalc_setup,
)


Expand Down Expand Up @@ -63,7 +63,7 @@ async def test_related_energy_sensor_is_used_for_existing_power_sensor(

await hass.async_block_till_done()

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_CREATE_GROUP: "TestGroup",
Expand Down Expand Up @@ -94,7 +94,7 @@ async def test_related_energy_sensor_is_used_for_existing_power_sensor(
async def test_disable_extended_attributes(hass: HomeAssistant) -> None:
await create_input_boolean(hass)

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
get_simple_fixed_config("input_boolean.test"),
{CONF_DISABLE_EXTENDED_ATTRIBUTES: True},
Expand Down Expand Up @@ -122,7 +122,7 @@ async def test_real_energy_sensor(hass: HomeAssistant) -> None:

await hass.async_block_till_done()

await run_powercalc_setup_yaml_config(
await run_powercalc_setup(
hass,
{
CONF_CREATE_GROUP: "TestGroup",
Expand Down

0 comments on commit c8a29e8

Please sign in to comment.