Skip to content

Commit

Permalink
Merge pull request #2708 from bramstroker/feat/unique_id_not_required…
Browse files Browse the repository at this point in the history
…_config_flow

Auto generate unique_id in config flow
  • Loading branch information
bramstroker authored Nov 23, 2024
2 parents 50d4aab + 1172e22 commit 386c8a4
Show file tree
Hide file tree
Showing 14 changed files with 54 additions and 83 deletions.
26 changes: 4 additions & 22 deletions custom_components/powercalc/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import copy
import logging
import uuid
from abc import ABC, abstractmethod
from datetime import timedelta
from enum import StrEnum
Expand Down Expand Up @@ -117,12 +118,10 @@
)
from .discovery import get_power_profile_by_source_entity
from .errors import ModelNotSupportedError, StrategyConfigurationError
from .helpers import get_or_create_unique_id
from .power_profile.factory import get_power_profile
from .power_profile.library import ModelInfo, ProfileLibrary
from .power_profile.power_profile import DOMAIN_DEVICE_TYPE, DeviceType, PowerProfile
from .sensors.daily_energy import DEFAULT_DAILY_UPDATE_FREQUENCY
from .sensors.group.factory import generate_unique_id
from .strategy.factory import PowerCalculatorStrategyFactory
from .strategy.wled import CONFIG_SCHEMA as SCHEMA_POWER_WLED

Expand Down Expand Up @@ -260,7 +259,6 @@ class Steps(StrEnum):
SCHEMA_DAILY_ENERGY = vol.Schema(
{
vol.Required(CONF_NAME): selector.TextSelector(),
vol.Optional(CONF_UNIQUE_ID): selector.TextSelector(),
**SCHEMA_UTILITY_METER_TOGGLE.schema,
},
).extend(SCHEMA_DAILY_ENERGY_OPTIONS.schema)
Expand All @@ -286,7 +284,6 @@ class Steps(StrEnum):
{
vol.Required(CONF_ENTITY_ID): selector.EntitySelector(),
vol.Optional(CONF_NAME): selector.TextSelector(),
vol.Optional(CONF_UNIQUE_ID): selector.TextSelector(),
},
)

Expand All @@ -308,7 +305,6 @@ class Steps(StrEnum):
SCHEMA_POWER_BASE = vol.Schema(
{
vol.Optional(CONF_NAME): selector.TextSelector(),
vol.Optional(CONF_UNIQUE_ID): selector.TextSelector(),
},
)

Expand Down Expand Up @@ -1155,6 +1151,7 @@ async def async_step_integration_discovery(

self.skip_advanced_step = True # We don't want to ask advanced options when discovered

await self.async_set_unique_id(str(uuid.uuid4()))
self.selected_sensor_type = SensorType.VIRTUAL_POWER
self.source_entity = discovery_info[DISCOVERY_SOURCE_ENTITY]
del discovery_info[DISCOVERY_SOURCE_ENTITY]
Expand All @@ -1164,10 +1161,6 @@ async def async_step_integration_discovery(
self.source_entity_id = self.source_entity.entity_id
self.name = self.source_entity.name

unique_id = get_or_create_unique_id(self.sensor_config, self.source_entity, self.selected_profile)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()

power_profiles: list[PowerProfile] = []
if DISCOVERY_POWER_PROFILES in discovery_info:
power_profiles = discovery_info[DISCOVERY_POWER_PROFILES]
Expand Down Expand Up @@ -1208,6 +1201,8 @@ async def async_step_user(
if not global_config_entry:
menu = {Steps.GLOBAL_CONFIGURATION: "Global configuration", **menu}

await self.async_set_unique_id(str(uuid.uuid4()))

return self.async_show_menu(step_id=Steps.USER, menu_options=menu)

async def async_step_menu_library(
Expand Down Expand Up @@ -1265,10 +1260,6 @@ async def async_step_virtual_power(
self.selected_sensor_type = SensorType.VIRTUAL_POWER
self.sensor_config.update(user_input)

unique_id = get_or_create_unique_id(self.sensor_config, self.source_entity, self.selected_profile)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()

return await self.forward_to_strategy_step(selected_strategy)

return self.async_show_form(
Expand Down Expand Up @@ -1300,9 +1291,6 @@ async def async_step_daily_energy(
if user_input is not None and not errors:
self.selected_sensor_type = SensorType.DAILY_ENERGY
self.name = user_input.get(CONF_NAME)
unique_id = user_input.get(CONF_UNIQUE_ID) or user_input.get(CONF_NAME)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()

self.sensor_config.update(self.build_daily_energy_config(user_input, schema))
if self.sensor_config.get(CONF_CREATE_UTILITY_METERS):
Expand Down Expand Up @@ -1392,9 +1380,6 @@ async def async_step_subtract_group(self, user_input: dict[str, Any] | None = No
async def async_handle_group_creation(self) -> FlowResult:
"""Handle the group creation."""
self.selected_sensor_type = SensorType.GROUP
unique_id = generate_unique_id(self.sensor_config)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()

if self.sensor_config.get(CONF_CREATE_UTILITY_METERS):
return await self.async_step_utility_meter_options()
Expand Down Expand Up @@ -1573,9 +1558,6 @@ async def async_step_real_power(
@callback
def persist_config_entry(self) -> FlowResult:
"""Create the config entry."""
if self.unique_id:
self.sensor_config.update({CONF_UNIQUE_ID: self.unique_id})

self.sensor_config.update({CONF_SENSOR_TYPE: self.selected_sensor_type})
self.sensor_config.update({CONF_NAME: self.name})
if self.source_entity_id:
Expand Down
15 changes: 0 additions & 15 deletions custom_components/powercalc/sensors/group/factory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_UNIQUE_ID
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType
Expand Down Expand Up @@ -48,17 +47,3 @@ async def create_group_sensors(
)

raise SensorConfigurationError(f"Group type {group_type} invalid") # pragma: no cover


def generate_unique_id(sensor_config: ConfigType) -> str:
"""Generate a unique ID for a group sensor."""
if CONF_UNIQUE_ID in sensor_config:
return str(sensor_config[CONF_UNIQUE_ID])

group_type: GroupType = sensor_config.get(CONF_GROUP_TYPE, GroupType.CUSTOM)
if group_type == GroupType.DOMAIN:
return domain_group.generate_unique_id(sensor_config)
if group_type == GroupType.SUBTRACT:
return subtract_group.generate_unique_id(sensor_config)

return custom_group.generate_unique_id(sensor_config)
11 changes: 3 additions & 8 deletions custom_components/powercalc/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"name": "Name",
"on_time": "On time",
"start_time": "Start time",
"unique_id": "Unique id",
"unit_of_measurement": "Unit of measurement",
"update_frequency": "Update frequency",
"value": "Value",
Expand Down Expand Up @@ -123,8 +122,7 @@
"hide_members": "Hide members",
"include_non_powercalc_sensors": "Include non powercalc sensors",
"name": "Name",
"sub_groups": "Sub groups",
"unique_id": "Unique id"
"sub_groups": "Sub groups"
},
"data_description": {
"area": "Adds all power sensors from the specified area",
Expand Down Expand Up @@ -263,7 +261,6 @@
"create_utility_meters": "Create utility meters",
"entity_id": "Base entity",
"name": "Name",
"unique_id": "Unique id",
"subtract_entities": "Subtract entities"
},
"data_description": {
Expand Down Expand Up @@ -326,17 +323,15 @@
"group": "Add to group",
"mode": "Calculation strategy",
"name": "Name",
"standby_power": "Standby power",
"unique_id": "Unique id"
"standby_power": "Standby power"
},
"data_description": {
"create_energy_sensor": "Whether powercalc needs to create a kWh sensor",
"create_utility_meters": "Let powercalc create utility meters, which cycle daily, hourly etc.",
"entity_id": "entity the virtual power is based on, the power sensor will listen to state changes of this entity to be updated",
"group": "Fill in a custom group name to create a new group",
"name": "Leaving blank will take the name from the source entity",
"standby_power": "Define the amount of power the device is consuming when in an OFF state",
"unique_id": "Specify a unique_id. This will allow to setup multiple power sensors for the same entity. When not specified it will take the unique_id of the source entity"
"standby_power": "Define the amount of power the device is consuming when in an OFF state"
},
"description": "See the readme for more information about the possible strategies and configuration options. Either source entity or name is required, or both.",
"title": "Create a virtual power sensor"
Expand Down
4 changes: 1 addition & 3 deletions tests/config_flow/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from homeassistant import config_entries, data_entry_flow
from homeassistant.const import CONF_ENTITY_ID, CONF_NAME, CONF_UNIQUE_ID
from homeassistant.const import CONF_ENTITY_ID, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.typing import ConfigType
Expand Down Expand Up @@ -155,7 +155,6 @@ async def goto_virtual_power_strategy_step(
user_input = {
CONF_ENTITY_ID: DEFAULT_ENTITY_ID,
CONF_MODE: strategy,
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
}
elif CONF_MODE not in user_input:
user_input[CONF_MODE] = strategy
Expand Down Expand Up @@ -226,7 +225,6 @@ def assert_default_virtual_power_entry_data(
CONF_CREATE_ENERGY_SENSOR: True,
CONF_CREATE_UTILITY_METERS: False,
CONF_NAME: "test",
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_ENERGY_INTEGRATION_METHOD: ENERGY_INTEGRATION_METHOD_LEFT,
}
| expected_strategy_options
Expand Down
7 changes: 0 additions & 7 deletions tests/config_flow/test_daily_energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_NAME,
CONF_UNIQUE_ID,
CONF_UNIT_OF_MEASUREMENT,
UnitOfEnergy,
UnitOfPower,
Expand All @@ -25,7 +24,6 @@
CONF_VALUE,
)
from tests.config_flow.common import (
DEFAULT_UNIQUE_ID,
create_mock_entry,
initialize_options_flow,
select_menu_item,
Expand All @@ -50,7 +48,6 @@ async def test_create_daily_energy_entry(hass: HomeAssistant) -> None:

user_input = {
CONF_NAME: "My daily energy sensor",
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_VALUE: 0.5,
CONF_UNIT_OF_MEASUREMENT: UnitOfPower.WATT,
CONF_CREATE_UTILITY_METERS: False,
Expand All @@ -70,7 +67,6 @@ async def test_create_daily_energy_entry(hass: HomeAssistant) -> None:
CONF_UNIT_OF_MEASUREMENT: UnitOfPower.WATT,
},
CONF_CREATE_UTILITY_METERS: False,
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
}

await hass.async_block_till_done()
Expand Down Expand Up @@ -105,7 +101,6 @@ async def test_on_time_option(hass: HomeAssistant) -> None:

user_input = {
CONF_NAME: "My daily energy sensor",
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_VALUE: 10,
CONF_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR,
CONF_ON_TIME: {
Expand All @@ -132,7 +127,6 @@ async def test_utility_meter_options(hass: HomeAssistant) -> None:

user_input = {
CONF_NAME: "My daily energy sensor",
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_VALUE: 10,
CONF_CREATE_UTILITY_METERS: True,
}
Expand Down Expand Up @@ -181,7 +175,6 @@ async def test_add_to_group(hass: HomeAssistant) -> None:

user_input = {
CONF_NAME: "My daily energy sensor",
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_VALUE: 10,
CONF_GROUP: group_entry.entry_id,
}
Expand Down
5 changes: 1 addition & 4 deletions tests/config_flow/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import voluptuous as vol
from homeassistant import config_entries, data_entry_flow
from homeassistant.const import CONF_ENTITY_ID, CONF_NAME, CONF_UNIQUE_ID
from homeassistant.const import CONF_ENTITY_ID, CONF_NAME
from homeassistant.core import HomeAssistant

from custom_components.powercalc.common import create_source_entity
Expand Down Expand Up @@ -56,7 +56,6 @@ async def test_discovery_flow(
CONF_MANUFACTURER: "signify",
CONF_MODEL: "LCT010",
CONF_NAME: "test",
CONF_UNIQUE_ID: f"pc_{DEFAULT_UNIQUE_ID}",
}


Expand Down Expand Up @@ -104,7 +103,6 @@ async def test_discovery_flow_with_subprofile_selection(
CONF_MANUFACTURER: "lifx",
CONF_MODEL: "LIFX Z/length_6",
CONF_NAME: "test",
CONF_UNIQUE_ID: f"pc_{DEFAULT_UNIQUE_ID}",
}


Expand Down Expand Up @@ -149,7 +147,6 @@ async def test_discovery_flow_multi_profiles(
CONF_MANUFACTURER: "signify",
CONF_MODEL: "LCT012",
CONF_NAME: "test",
CONF_UNIQUE_ID: f"pc_{DEFAULT_UNIQUE_ID}",
}


Expand Down
11 changes: 1 addition & 10 deletions tests/config_flow/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ async def test_create_group_entry_without_unique_id(hass: HomeAssistant) -> None
CONF_HIDE_MEMBERS: False,
CONF_GROUP_POWER_ENTITIES: ["sensor.balcony_power"],
CONF_FORCE_CALCULATE_GROUP_ENERGY: False,
CONF_UNIQUE_ID: "My group sensor",
CONF_INCLUDE_NON_POWERCALC_SENSORS: True,
CONF_CREATE_ENERGY_SENSOR: True,
CONF_CREATE_UTILITY_METERS: False,
Expand Down Expand Up @@ -193,7 +192,6 @@ async def test_group_include_area(
CONF_HIDE_MEMBERS: False,
CONF_FORCE_CALCULATE_GROUP_ENERGY: False,
CONF_AREA: area.id,
CONF_UNIQUE_ID: "My group sensor",
CONF_INCLUDE_NON_POWERCALC_SENSORS: True,
CONF_CREATE_ENERGY_SENSOR: True,
CONF_CREATE_UTILITY_METERS: True,
Expand Down Expand Up @@ -327,7 +325,6 @@ async def test_can_select_existing_powercalc_entry_as_group_member(
unique_id="abcdefg",
data={
CONF_SENSOR_TYPE: SensorType.VIRTUAL_POWER,
CONF_UNIQUE_ID: "abcdefg",
CONF_ENTITY_ID: "sensor.dummy",
CONF_MODE: CalculationStrategy.FIXED,
CONF_FIXED: {CONF_POWER: 50},
Expand All @@ -349,7 +346,6 @@ async def test_can_select_existing_powercalc_entry_as_group_member(

user_input = {
CONF_NAME: "My group sensor",
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_GROUP_MEMBER_SENSORS: [config_entry_1.entry_id],
}
result = await hass.config_entries.flow.async_configure(
Expand All @@ -364,7 +360,6 @@ async def test_can_select_existing_powercalc_entry_as_group_member(
CONF_FORCE_CALCULATE_GROUP_ENERGY: False,
CONF_HIDE_MEMBERS: False,
CONF_GROUP_MEMBER_SENSORS: [config_entry_1.entry_id],
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_INCLUDE_NON_POWERCALC_SENSORS: True,
CONF_CREATE_ENERGY_SENSOR: True,
CONF_CREATE_UTILITY_METERS: False,
Expand All @@ -389,7 +384,6 @@ async def test_real_power_entry_selectable_as_group_member(
unique_id="abcdefg",
data={
CONF_SENSOR_TYPE: SensorType.REAL_POWER,
CONF_UNIQUE_ID: "abcdefg",
CONF_ENTITY_ID: "sensor.real_power",
},
title="RealPower1",
Expand All @@ -409,7 +403,6 @@ async def test_real_power_entry_selectable_as_group_member(

user_input = {
CONF_NAME: "My group sensor",
CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID,
CONF_GROUP_MEMBER_SENSORS: [config_entry_1.entry_id, config_entry_2.entry_id],
}
await hass.config_entries.flow.async_configure(
Expand All @@ -428,7 +421,7 @@ async def test_real_power_entry_selectable_as_group_member(

async def test_group_error_mandatory(hass: HomeAssistant) -> None:
result = await select_menu_item(hass, Steps.MENU_GROUP, Steps.GROUP)
user_input = {CONF_NAME: "My group sensor", CONF_UNIQUE_ID: DEFAULT_UNIQUE_ID}
user_input = {CONF_NAME: "My group sensor"}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input,
Expand Down Expand Up @@ -583,7 +576,6 @@ async def test_no_group_created_when_group_null(hass: HomeAssistant) -> None:
hass,
{
CONF_SENSOR_TYPE: SensorType.VIRTUAL_POWER,
CONF_UNIQUE_ID: "abc",
CONF_ENTITY_ID: "light.my_light",
CONF_NAME: "Some light",
CONF_MODE: CalculationStrategy.FIXED,
Expand Down Expand Up @@ -616,7 +608,6 @@ async def test_domain_group_flow(hass: HomeAssistant) -> None:
CONF_GROUP_TYPE: GroupType.DOMAIN,
CONF_NAME: "My group sensor",
CONF_DOMAIN: Platform.LIGHT,
CONF_UNIQUE_ID: "powercalc_domaingroup_light",
CONF_CREATE_ENERGY_SENSOR: True,
CONF_CREATE_UTILITY_METERS: False,
}
Expand Down
2 changes: 0 additions & 2 deletions tests/config_flow/test_group_subtract.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
CONF_ENTITY_ID,
CONF_NAME,
CONF_SENSOR_TYPE,
CONF_UNIQUE_ID,
)
from homeassistant.core import HomeAssistant

Expand Down Expand Up @@ -48,7 +47,6 @@ async def test_subtract_group_flow(hass: HomeAssistant) -> None:
CONF_CREATE_UTILITY_METERS: False,
CONF_ENTITY_ID: "sensor.outlet_power",
CONF_SUBTRACT_ENTITIES: ["sensor.light_power"],
CONF_UNIQUE_ID: "pc_subtract_sensor.outlet_power",
}
config_entry: ConfigEntry = result["result"]

Expand Down
Loading

0 comments on commit 386c8a4

Please sign in to comment.