Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto generate unique_id in config flow #2708

Merged
merged 3 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading