393 files

This commit is contained in:
Home Assistant Version Control
2026-09-27 17:16:54 +00:00
parent b05b7a897e
commit 39d97ac2db
404 changed files with 54006 additions and 11034 deletions
@@ -25,6 +25,7 @@ from custom_components.powercalc.const import (
CONF_ENERGY_UPDATE_INTERVAL,
CONF_EXCLUDE_DEVICE_TYPES,
CONF_EXCLUDE_SELF_USAGE,
CONF_FOLLOW_DEVICE_NAME,
CONF_GROUP_ENERGY_UPDATE_INTERVAL,
CONF_GROUP_POWER_UPDATE_INTERVAL,
CONF_IGNORE_UNAVAILABLE_STATE,
@@ -60,6 +61,7 @@ def get_global_configuration(hass: HomeAssistant, config: ConfigType) -> ConfigT
# Default configuration values
default_config = {
CONF_ENABLE_ANALYTICS: False,
CONF_FOLLOW_DEVICE_NAME: False,
CONF_POWER_SENSOR_NAMING: DEFAULT_POWER_NAME_PATTERN,
CONF_POWER_SENSOR_PRECISION: DEFAULT_POWER_SENSOR_PRECISION,
CONF_POWER_SENSOR_CATEGORY: DEFAULT_ENTITY_CATEGORY,
+2
View File
@@ -52,6 +52,7 @@ ENTRY_GLOBAL_CONFIG_UNIQUE_ID = "powercalc_global_configuration"
PLACEHOLDER_ENTITY_BY_DEVICE_CLASS = "entity_by_device_class:"
PLACEHOLDER_ENTITY_BY_TRANSLATION_KEY = "entity_by_translation_key:"
PLACEHOLDER_ENTITY_BY_UNIQUE_ID_SUFFIX = "entity_by_unique_id_suffix:"
DUMMY_ENTITY_ID = "sensor.dummy"
@@ -117,6 +118,7 @@ CONF_FIXED = "fixed"
CONF_FLOOR = "floor"
CONF_FORCE_CALCULATE_GROUP_ENERGY = "force_calculate_group_energy"
CONF_FORCE_ENERGY_SENSOR_CREATION = "force_energy_sensor_creation"
CONF_FOLLOW_DEVICE_NAME = "follow_device_name"
CONF_GAMMA_CURVE = "gamma_curve"
CONF_GROUP = "group"
CONF_GROUP_ENERGY_ENTITIES = "group_energy_entities"
@@ -136,6 +136,43 @@ def get_related_devices(hass: HomeAssistant, device_id: str) -> list[DeviceEntry
return list(devices.values())
def get_profile_related_devices(hass: HomeAssistant, device_id: str) -> list[AnyDeviceEntry]:
"""Return native children and known docks that a profile may reference."""
device_reg = device_registry.async_get(hass)
device = device_reg.async_get(device_id)
if device is None:
return []
related: dict[str, AnyDeviceEntry] = {}
if _HAS_CHILD_DEVICES:
for child in device_registry.async_entries_for_parent_device(device_reg, device_id):
related[child.id] = child
for dock in _get_roborock_docks(hass, device):
related[dock.id] = dock
related.pop(device_id, None)
return list(related.values())
def _get_roborock_docks(hass: HomeAssistant, device: AnyDeviceEntry) -> list[AnyDeviceEntry]:
"""Match Roborock's dock identifier convention within the source's config entries."""
dock_identifiers = {
(domain, f"{identifier}_dock") for domain, identifier in device.identifiers if domain == "roborock"
}
if not dock_identifiers:
return []
device_reg = device_registry.async_get(hass)
docks: list[AnyDeviceEntry] = []
for config_entry_id in get_config_entry_ids(device):
candidates: list[AnyDeviceEntry] = list(get_devices_for_config_entry(hass, config_entry_id))
if _HAS_CHILD_DEVICES:
candidates.extend(device_registry.async_child_entries_for_config_entry(device_reg, config_entry_id))
docks.extend(candidate for candidate in candidates if candidate.identifiers & dock_identifiers)
return docks
def resolve_source_device(
hass: HomeAssistant,
sensor_config: ConfigType,
@@ -1,10 +1,14 @@
from collections.abc import Mapping
import re
from typing import cast
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.selector import selector
from homeassistant.helpers.selector import EntitySelector, selector
import voluptuous as vol
from custom_components.powercalc.common import SourceEntity
from custom_components.powercalc.power_profile.power_profile import PowerProfile
from custom_components.powercalc.power_profile.power_profile import EntityAutoSelectConfig, PowerProfile
def build_dynamic_field_schema(
@@ -12,20 +16,18 @@ def build_dynamic_field_schema(
profile: PowerProfile,
source_entity: SourceEntity | None,
) -> vol.Schema:
"""Build the profile's custom field schema, restricting entities to the source device when available.
Prefill entity fields with a unique auto-selection match unless an explicit default is set.
"""
schema = {}
for field in profile.custom_fields:
field_description = field.description
if not field_description:
field_description = field.label
if field.default is not None:
key = vol.Required(field.key, description=field_description, default=field.default)
else:
key = vol.Required(field.key, description=field_description)
field_description = field.description or field.label
field_selector = field.selector
device_entities: list[er.RegistryEntry] = []
if "entity" in field.selector and source_entity and source_entity.device_entry:
entity_reg = er.async_get(hass)
device_entities = list(entity_reg.entities.get_entries_for_device_id(source_entity.device_entry.id))
# Build a new selector dict instead of mutating field.selector, which is a reference
# into the (potentially cached) profile json_data.
field_selector = {
@@ -34,10 +36,74 @@ def build_dynamic_field_schema(
**field.selector["entity"],
"include_entities": [
entity.entity_id
for entity in entity_reg.entities.get_entries_for_device_id(source_entity.device_entry.id)
for entity in device_entities
if "include_entities" not in field.selector["entity"]
or entity.entity_id in field.selector["entity"]["include_entities"]
],
},
}
schema[key] = selector(field_selector)
entity_selector = selector(field_selector)
default = field.default
if default is None and field.auto_select and isinstance(entity_selector, EntitySelector):
default = find_auto_selected_entity(device_entities, entity_selector, field.auto_select)
key = vol.Required(field.key, description=field_description)
if default is not None:
key = vol.Required(field.key, description=field_description, default=default)
schema[key] = entity_selector
return vol.Schema(schema)
def find_auto_selected_entity(
entities: list[er.RegistryEntry],
entity_selector: EntitySelector,
auto_select: EntityAutoSelectConfig,
) -> str | None:
"""Return a single enabled match without guessing when the selection is ambiguous."""
matches = []
for entity in entities:
if entity.disabled or not matches_entity_filter(entity, auto_select):
continue
if not matches_entity_selector(entity, entity_selector):
continue
matches.append(entity.entity_id)
return matches[0] if len(matches) == 1 else None
def matches_entity_selector(entity: er.RegistryEntry, entity_selector: EntitySelector) -> bool:
"""Check the selector's allowed entities and metadata filters against the registry."""
config = entity_selector.config
included = config.get("include_entities")
if included is not None and entity.entity_id not in included:
return False
if entity.entity_id in config.get("exclude_entities", []):
return False
top_level_filters = {
key: value for key, value in config.items() if key in ["domain", "device_class", "integration"]
}
if not matches_entity_filter(entity, top_level_filters):
return False
# EntitySelector normalizes nested filters to a list of alternatives.
filters = cast(list[Mapping[str, object]], config.get("filter", [{}]))
return any(matches_entity_filter(entity, item) for item in filters)
def matches_entity_filter(entity: er.RegistryEntry, filters: Mapping[str, object]) -> bool:
"""Match registry metadata; leave filters requiring other data for manual selection."""
values = {
"domain": entity.domain,
"integration": entity.platform,
"device_class": entity.device_class or entity.original_device_class,
"translation_key": entity.translation_key,
}
for key, expected in filters.items():
if key == "unique_id_pattern":
if not re.search(cast(str, expected), entity.unique_id):
return False
elif key not in values or values[key] not in (expected if isinstance(expected, list) else [expected]):
return False
return True
@@ -29,6 +29,7 @@ from custom_components.powercalc.const import (
CONF_ENERGY_UPDATE_INTERVAL,
CONF_EXCLUDE_DEVICE_TYPES,
CONF_EXCLUDE_SELF_USAGE,
CONF_FOLLOW_DEVICE_NAME,
CONF_GROUP_ENERGY_UPDATE_INTERVAL,
CONF_GROUP_POWER_UPDATE_INTERVAL,
CONF_IGNORE_UNAVAILABLE_STATE,
@@ -72,6 +73,7 @@ SECTION_GLOBAL_ADVANCED = "advanced"
SCHEMA_GLOBAL_CONFIGURATION_POWER = vol.Schema(
{
vol.Optional(CONF_FOLLOW_DEVICE_NAME, default=False): selector.BooleanSelector(),
vol.Optional(CONF_POWER_SENSOR_NAMING): selector.TextSelector(),
vol.Optional(CONF_POWER_SENSOR_FRIENDLY_NAMING): selector.TextSelector(),
vol.Optional(CONF_POWER_SENSOR_CATEGORY): selector.SelectSelector(
@@ -319,6 +319,7 @@ class LibraryFlow:
next_step=Step.POST_LIBRARY,
validate_user_input=_process_user_input,
form_kwarg=form_kwarg,
form_data=self.flow.sensor_config.get(CONF_VARIABLES, {}),
),
user_input,
)
+54 -10
View File
@@ -18,8 +18,10 @@ from custom_components.powercalc.common import SourceEntity
from custom_components.powercalc.const import (
PLACEHOLDER_ENTITY_BY_DEVICE_CLASS,
PLACEHOLDER_ENTITY_BY_TRANSLATION_KEY,
PLACEHOLDER_ENTITY_BY_UNIQUE_ID_SUFFIX,
CalculationStrategy,
)
from custom_components.powercalc.device_binding import get_profile_related_devices
from custom_components.powercalc.power_profile.power_profile import PowerProfile
_LOGGER = logging.getLogger(__name__)
@@ -158,7 +160,7 @@ def replace_placeholders(
def iter_related_entity_placeholders(placeholders: Iterable[str]) -> Iterator[str]:
"""Yield placeholders that need lookup against entities on the same device."""
"""Yield placeholders that need lookup against related entities."""
for placeholder in placeholders:
if parse_related_entity_placeholder(placeholder):
yield placeholder
@@ -223,6 +225,11 @@ RELATED_ENTITY_PLACEHOLDER_DEFINITIONS = (
translation_key,
),
),
RelatedEntityPlaceholderDefinition(
PLACEHOLDER_ENTITY_BY_UNIQUE_ID_SUFFIX,
"unique ID suffix",
lambda hass, source_entity, suffix: get_related_entity_by_unique_id_suffix(hass, source_entity, suffix),
),
)
@@ -241,7 +248,7 @@ def get_related_entity_by_device_class(
source_entity: SourceEntity,
device_class: SensorDeviceClass | BinarySensorDeviceClass,
) -> str | None:
"""Get related entity from same device by device class."""
"""Find an entity by device class, preferring the source device."""
return _get_related_entity_for_device(
hass,
source_entity=source_entity,
@@ -256,7 +263,7 @@ def get_related_entity_by_translation_key(
source_entity: SourceEntity,
translation_key: str,
) -> str | None:
"""Get related entity from same device by translation key."""
"""Find an entity by translation key, preferring the source device."""
return _get_related_entity_for_device(
hass,
source_entity=source_entity,
@@ -266,6 +273,28 @@ def get_related_entity_by_translation_key(
)
def get_related_entity_by_unique_id_suffix(
hass: HomeAssistant,
source_entity: SourceEntity,
suffix: str,
) -> str | None:
"""Require one suffix match on the source device and integration."""
entity_reg = entity_registry.async_get(hass)
source_entry = entity_reg.async_get(source_entity.entity_id)
if not re.fullmatch(r"[A-Za-z_]\w*", suffix) or not source_entity.device_id or source_entry is None:
return None
matches = [
entry
for entry in entity_registry.async_entries_for_device(
entity_reg, source_entity.device_id, include_disabled_entities=True
)
if entry.platform == source_entry.platform and entry.unique_id.endswith(suffix)
]
if len(matches) != 1 or matches[0].disabled_by is not None:
return None
return matches[0].entity_id
def _get_related_entity_for_device(
hass: HomeAssistant,
source_entity: SourceEntity,
@@ -273,18 +302,23 @@ def _get_related_entity_for_device(
match_value: SensorDeviceClass | BinarySensorDeviceClass | str,
matcher: Callable[[RegistryEntry], bool],
) -> str | None:
"""Get the first related entity on the same device matching the given predicate."""
"""Prefer a source-device match, otherwise require one unambiguous linked-device match."""
entity_reg = entity_registry.async_get(hass)
device_id = source_entity.device_id
if not device_id:
_LOGGER.debug("No device_id available, cannot find related entity")
return None
related_entities = [
entity_entry.entity_id
for entity_entry in entity_registry.async_entries_for_device(entity_reg, device_id)
if matcher(entity_entry)
]
for entity_entry in entity_registry.async_entries_for_device(entity_reg, device_id):
if matcher(entity_entry):
return entity_entry.entity_id
related_entities: set[str] = set()
for device in get_profile_related_devices(hass, device_id):
for entity_entry in entity_registry.async_entries_for_device(entity_reg, device.id):
if matcher(entity_entry):
related_entities.add(entity_entry.entity_id)
if not related_entities:
_LOGGER.debug(
"No related entities found for device %s with %s %s",
@@ -294,4 +328,14 @@ def _get_related_entity_for_device(
)
return None
return related_entities[0]
if len(related_entities) > 1:
_LOGGER.warning(
"Ambiguous related entities for %s with %s %s: %s",
source_entity.entity_id,
match_label,
match_value,
", ".join(sorted(related_entities)),
)
return None
return next(iter(related_entities))
+1 -1
View File
@@ -22,5 +22,5 @@
"requirements": [
"numpy>=1.21.1"
],
"version": "v1.25.5"
"version": "v1.26.0"
}
@@ -1,6 +1,8 @@
import asyncio
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from functools import partial
import gzip
import json
from json import JSONDecodeError
import logging
@@ -28,6 +30,7 @@ from custom_components.powercalc.helpers import async_cache, clear_async_cache
from custom_components.powercalc.power_profile.error import LibraryLoadingError, ProfileDownloadError
from custom_components.powercalc.power_profile.loader.profile_cache import (
InstalledProfile,
compress_installed_profile_csv_files,
create_staging_directory,
install_profile,
read_installed_profile,
@@ -58,6 +61,15 @@ MAX_PROFILE_DOWNLOAD_SIZE = 25 * 1024 * 1024
DOWNLOAD_CHUNK_SIZE = 64 * 1024
@dataclass(frozen=True)
class RemoteResource:
"""A validated remote resource and its normalized local destination."""
url: str
destination: Path
compress: bool
def _is_library_repository_url(parsed_url: SplitResult) -> bool:
"""Check that a resource URL addresses the profile library of the Powercalc repository."""
segments = [segment for segment in unquote(parsed_url.path).split("/") if segment]
@@ -173,7 +185,7 @@ def _resolve_resource_path(storage_path: str, resource_path: object) -> Path:
return destination
def _validate_resources(resources: object, storage_path: str) -> list[tuple[str, Path]]:
def _validate_resources(resources: object, storage_path: str) -> list[RemoteResource]:
"""Validate all resources in a remote profile response."""
if not isinstance(resources, list) or not all(isinstance(resource, dict) for resource in resources):
raise ProfileDownloadError("Remote profile response contains invalid resources")
@@ -182,16 +194,35 @@ def _validate_resources(resources: object, storage_path: str) -> list[tuple[str,
f"Remote profile contains more than the maximum of {MAX_PROFILE_RESOURCES} resources",
)
return [
(_validate_resource_url(resource.get("url")), _resolve_resource_path(storage_path, resource.get("path")))
for resource in resources
]
resources_by_destination: dict[Path, RemoteResource] = {}
for resource in resources:
destination = _resolve_resource_path(storage_path, resource.get("path"))
compress = destination.suffix == ".csv"
if compress:
destination = destination.with_name(f"{destination.name}.gz")
validated_resource = RemoteResource(
url=_validate_resource_url(resource.get("url")),
destination=destination,
compress=compress,
)
existing_resource = resources_by_destination.get(destination)
if existing_resource is not None and existing_resource.compress == compress:
raise ProfileDownloadError(f"Remote profile contains duplicate resource path: {destination.name}")
if existing_resource is None or existing_resource.compress:
resources_by_destination[destination] = validated_resource
return list(resources_by_destination.values())
def _save_resources(resources: list[tuple[bytes, Path]]) -> None:
def _save_resources(resources: list[tuple[bytes, RemoteResource]]) -> None:
"""Save all downloaded resources after every response has completed successfully."""
for data, path in resources:
save_resource(data, path)
prepared_resources = [
(gzip.compress(data, mtime=0) if resource.compress else data, resource.destination)
for data, resource in resources
]
for data, destination in prepared_resources:
save_resource(data, destination)
class LibraryModel(TypedDict):
@@ -269,7 +300,7 @@ class RemoteLoader(Loader):
if (manufacturer, model) not in models:
continue
key = f"{manufacturer}/{model}"
profile = read_installed_profile(
profile = self._read_and_compress_installed_profile(
Path(self.get_storage_path(manufacturer, model)),
model,
self.profile_hashes.get(key),
@@ -279,6 +310,23 @@ class RemoteLoader(Loader):
installed[key] = profile
return installed
@staticmethod
def _read_and_compress_installed_profile(
storage: Path,
model_id: str,
legacy_hash: str | None,
version: AwesomeVersion,
) -> InstalledProfile | None:
"""Read an installed profile and migrate its plain CSV resources to gzip."""
installed = read_installed_profile(storage, model_id, legacy_hash, version)
if installed is None:
return None
try:
compress_installed_profile_csv_files(installed)
except (OSError, ValueError, KeyError, TypeError, JSONDecodeError) as err:
_LOGGER.warning("Could not compress cached CSV files for %s: %s", storage, err)
return installed
def get_discovery_low_priority_domains(self) -> set[str]:
"""Get the low priority discovery integration domains declared by library metadata."""
return set(self.library_contents.get(LIBRARY_DISCOVERY_LOW_PRIORITY_DOMAINS, []))
@@ -566,7 +614,7 @@ class RemoteLoader(Loader):
raise LibraryLoadingError(f"Profile {key} requires Powercalc {model_info['min_version']}")
storage_path = self.get_storage_path(manufacturer, model)
installed = await self.hass.async_add_executor_job(
read_installed_profile,
self._read_and_compress_installed_profile,
Path(storage_path),
model,
self.profile_hashes.get(key),
@@ -713,12 +761,12 @@ class RemoteLoader(Loader):
await self.hass.async_add_executor_job(lambda: os.makedirs(storage_path, exist_ok=True))
# Download the files
downloaded_resources: list[tuple[bytes, Path]] = []
downloaded_resources: list[tuple[bytes, RemoteResource]] = []
downloaded_size = 0
for url, destination in validated_resources:
async with session.get(url, allow_redirects=False) as resp:
for resource in validated_resources:
async with session.get(resource.url, allow_redirects=False) as resp:
if resp.status != 200:
raise ProfileDownloadError(f"Failed to download github URL: {url}")
raise ProfileDownloadError(f"Failed to download github URL: {resource.url}")
remaining_size = MAX_PROFILE_DOWNLOAD_SIZE - downloaded_size
if remaining_size <= 0:
@@ -729,7 +777,7 @@ class RemoteLoader(Loader):
contents = await _read_capped(
resp,
min(MAX_RESOURCE_SIZE, remaining_size),
f"Remote profile resource {url}",
f"Remote profile resource {resource.url}",
)
except ProfileDownloadError as err:
if remaining_size < MAX_RESOURCE_SIZE:
@@ -738,7 +786,7 @@ class RemoteLoader(Loader):
) from err
raise
downloaded_size += len(contents)
downloaded_resources.append((contents, destination))
downloaded_resources.append((contents, resource))
await self.hass.async_add_executor_job(_save_resources, downloaded_resources)
except (TimeoutError, aiohttp.ClientError) as e:
@@ -2,10 +2,11 @@ from collections import defaultdict
from collections.abc import Mapping
from copy import deepcopy
from dataclasses import dataclass
from decimal import Decimal, InvalidOperation
from enum import StrEnum
import logging
import os
from typing import Any, cast
from typing import Any, TypedDict, cast
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
@@ -32,6 +33,7 @@ from custom_components.powercalc.const import (
CONF_ENERGY_SENSOR_NAMING,
CONF_MAX_POWER,
CONF_MIN_POWER,
CONF_MULTIPLY_FACTOR,
CONF_POWER,
CONF_POWER_SENSOR_NAMING,
DEFAULT_SELF_USAGE_ENERGY_NAME_PATTERN,
@@ -42,6 +44,7 @@ from custom_components.powercalc.const import (
)
from custom_components.powercalc.errors import (
ModelNotSupportedError,
StrategyConfigurationError,
UnsupportedStrategyError,
)
from custom_components.powercalc.power_profile.sub_profile_selector import SubProfileSelectConfig
@@ -80,6 +83,12 @@ class DiscoveryBy(StrEnum):
MANUAL = "manual"
class EntityAutoSelectConfig(TypedDict, total=False):
integration: str
translation_key: str
unique_id_pattern: str
@dataclass(frozen=True)
class CustomField:
key: str
@@ -87,6 +96,7 @@ class CustomField:
selector: dict[str, Any]
description: str | None = None
default: Any = None
auto_select: EntityAutoSelectConfig | None = None
DEVICE_TYPE_DOMAIN: dict[DeviceType, str | set[str]] = {
@@ -212,6 +222,21 @@ class PowerProfile:
return self.standby_power
return standby_power_on or 0
@property
def multiply_factor(self) -> Decimal | None:
"""Get the default multiplier after profile variables have been substituted."""
value = self._json_data.get(CONF_MULTIPLY_FACTOR)
if value is None:
return None
message = f"Invalid multiply_factor {value!r} for {self.manufacturer}/{self.model}: expected a finite number"
try:
factor = Decimal(str(value))
except InvalidOperation as err:
raise StrategyConfigurationError(message) from err
if not factor.is_finite():
raise StrategyConfigurationError(message)
return factor
@property
def calculation_strategy(self) -> CalculationStrategy:
"""Get the calculation strategy this profile provides"""
+20 -1
View File
@@ -90,6 +90,7 @@ from .device_binding import (
assign_device_to_entities,
resolve_source_device,
)
from .device_naming import should_follow_device_name
from .errors import (
PowercalcSetupError,
SensorAlreadyConfiguredError,
@@ -98,6 +99,7 @@ from .errors import (
from .group_include.filter import FilterOperator, create_composite_filter
from .group_include.include import find_entities
from .measure import MeasureAppCoordinator
from .sensors.abstract import BaseEntity
from .sensors.cost import CostSensor, create_cost_sensor_for_energy_entity
from .sensors.daily_energy import (
create_daily_fixed_energy_power_sensor,
@@ -701,12 +703,23 @@ async def create_individual_sensors(
if sensor_config.get(CONF_CREATE_STANDBY_ENERGY_SENSOR) and isinstance(power_sensor, VirtualPowerSensor):
entities_to_add.append(create_standby_energy_sensor(hass, sensor_config, power_sensor, source_entity))
follow_device_name = should_follow_device_name(hass, sensor_config, config_entry, source_entity)
if energy_sensor:
entities_to_add.extend(
create_energy_related_sensors(hass, sensor_config, energy_sensor, source_entity, config_entry),
create_energy_related_sensors(
hass,
sensor_config,
energy_sensor,
source_entity,
config_entry,
follow_device_name=follow_device_name,
),
)
assign_device_to_entities(hass, config_entry, entities_to_add, source_entity, sensor_config)
if follow_device_name:
_enable_device_naming(entities_to_add)
hass.data[DOMAIN][DATA_CONFIGURED_ENTITIES].update(
{source_entity.entity_id: [(entity, context.is_yaml) for entity in entities_to_add]},
)
@@ -721,6 +734,12 @@ async def create_individual_sensors(
return EntitiesBucket(new=entities_to_add, existing=[])
def _enable_device_naming(entities: list[Entity]) -> None:
for entity in entities:
if isinstance(entity, BaseEntity):
entity.enable_device_naming()
async def _create_daily_fixed_energy_sensors(
hass: HomeAssistant,
sensor_config: ConfigType,
@@ -25,6 +25,7 @@ from custom_components.powercalc.const import (
DOMAIN,
)
from custom_components.powercalc.device_binding import bind_entity_to_registry_metadata
from custom_components.powercalc.device_naming import DeviceName
ENTITY_ID_FORMAT = SENSOR_DOMAIN + ".{}"
@@ -32,6 +33,17 @@ _LOGGER = logging.getLogger(__name__)
class BaseEntity(Entity):
device_name: DeviceName | None = None
def enable_device_naming(self) -> None:
"""Use a translated relative name after legacy names and IDs have been consumed."""
if self.device_name is None:
return
self._attr_has_entity_name = True
self._attr_translation_key = self.device_name.translation_key
self._attr_translation_placeholders = self.device_name.placeholders or {}
del self._attr_name
async def async_added_to_hass(self) -> None:
"""Bind configured registry metadata."""
await super().async_added_to_hass()
+7 -1
View File
@@ -30,6 +30,7 @@ from custom_components.powercalc.const import (
DOMAIN,
DOMAIN_CONFIG,
)
from custom_components.powercalc.device_naming import DeviceName
from custom_components.powercalc.unit import convert_to_decimal, parse_decimal
from .abstract import (
@@ -199,7 +200,7 @@ def create_cost_sensor(
price_config,
)
return CostSensor(
cost_sensor = CostSensor(
hass=hass,
source_energy_entity=energy_sensor.entity_id,
entity_id=entity_id,
@@ -209,6 +210,10 @@ def create_cost_sensor(
price_config=price_config,
reset_on_source_reset=reset_on_source_reset,
)
if isinstance(energy_sensor, VirtualUtilityMeter):
meter_name = energy_sensor.device_name
cost_sensor.device_name = DeviceName(f"{meter_name.translation_key}_cost", meter_name.placeholders)
return cost_sensor
def create_cost_sensor_for_energy_entity(hass: HomeAssistant, sensor_config: ConfigType) -> CostSensor | None:
@@ -234,6 +239,7 @@ def create_cost_sensor_for_energy_entity(hass: HomeAssistant, sensor_config: Con
class CostSensor(BaseEntity, RestoreEntity, SensorEntity):
"""Cost sensor, accumulating the cost of the energy consumed at price-at-consumption."""
device_name = DeviceName("cost")
_attr_device_class = SensorDeviceClass.MONETARY
_attr_state_class = SensorStateClass.TOTAL
_attr_should_poll = False
@@ -14,6 +14,7 @@ from homeassistant.const import (
CONF_NAME,
CONF_UNIQUE_ID,
CONF_UNIT_OF_MEASUREMENT,
EntityCategory,
UnitOfEnergy,
UnitOfPower,
)
@@ -181,7 +182,9 @@ class DailyEnergySensor(EnergySensor, RestoreEntity, SensorEntity):
self._hass = hass
self._attr_name = name
self._state: Decimal = Decimal(0)
self._attr_entity_category = sensor_config.get(CONF_ENERGY_SENSOR_CATEGORY)
entity_category = sensor_config.get(CONF_ENERGY_SENSOR_CATEGORY)
if entity_category:
self._attr_entity_category = EntityCategory(entity_category)
self._value = value
self._user_unit_of_measurement = user_unit_of_measurement
self._update_frequency = update_frequency
+26 -1
View File
@@ -4,7 +4,7 @@ import inspect
import logging
from typing import Any
from homeassistant.components.integration.sensor import IntegrationSensor
from homeassistant.components.integration.sensor import IntegrationSensor, IntegrationSensorExtraStoredData
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, SensorStateClass
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
@@ -39,6 +39,7 @@ from custom_components.powercalc.const import (
UNAVAILABLE_STATES,
UnitPrefix,
)
from custom_components.powercalc.device_naming import DeviceName
from custom_components.powercalc.errors import SensorConfigurationError
from custom_components.powercalc.filter.outlier import OutlierFilter
@@ -282,9 +283,20 @@ class EnergySensor(BaseEntity):
"""Class which all energy sensors should extend from."""
class VirtualEnergyExtraStoredData(IntegrationSensorExtraStoredData):
"""Preserve zero totals which HA's integration serializer treats as missing."""
def as_dict(self) -> dict[str, Any]:
data = super().as_dict()
data["last_valid_state"] = str(self.last_valid_state) if self.last_valid_state is not None else None
return data
class VirtualEnergySensor(IntegrationSensor, EnergySensor):
"""Virtual energy sensor, totalling kWh."""
device_name = DeviceName("energy")
_attr_state_class = SensorStateClass.TOTAL_INCREASING
_unrecorded_attributes = frozenset({ATTR_SOURCE_DOMAIN, ATTR_SOURCE_ENTITY})
@@ -438,6 +450,15 @@ class VirtualEnergySensor(IntegrationSensor, EnergySensor):
attrs.update(super_attrs)
return attrs
@property
def extra_restore_state_data(self) -> VirtualEnergyExtraStoredData:
return VirtualEnergyExtraStoredData(
self.native_value,
self.native_unit_of_measurement,
self._source_entity,
self._last_valid_state,
)
@property
def icon(self) -> str:
return ENERGY_ICON
@@ -446,17 +467,21 @@ class VirtualEnergySensor(IntegrationSensor, EnergySensor):
def async_reset(self) -> None:
_LOGGER.debug("%s: Reset energy sensor", self.entity_id)
self._state = Decimal(0)
self._last_valid_state = self._state
self.async_write_ha_state()
async def async_calibrate(self, value: str) -> None:
_LOGGER.debug("%s: Calibrate energy sensor to: %s", self.entity_id, value)
self._state = Decimal(value)
self._last_valid_state = self._state
self.async_write_ha_state()
class VirtualStandbyEnergySensor(VirtualEnergySensor):
"""Energy sensor integrating only the standby portion of a virtual power sensor."""
device_name = DeviceName("standby_energy")
def __init__(
self,
hass: HomeAssistant,
@@ -5,6 +5,7 @@ from homeassistant.helpers.typing import ConfigType
from custom_components.powercalc.common import SourceEntity
from custom_components.powercalc.const import CONF_CREATE_COST_SENSOR
from custom_components.powercalc.device_binding import get_device_entry
from .cost import create_cost_sensor
from .energy import EnergySensor
@@ -19,6 +20,7 @@ def create_energy_related_sensors(
config_entry: ConfigEntry | None = None,
utility_meter_config: ConfigType | None = None,
cost_name: str | None = None,
follow_device_name: bool = False,
) -> list[Entity]:
"""Create optional utility meters and cost sensor for an energy sensor.
@@ -27,7 +29,15 @@ def create_energy_related_sensors(
"""
entities: list[Entity] = []
meter_config = sensor_config if utility_meter_config is None else utility_meter_config
utility_meters = create_utility_meters(hass, energy_sensor, meter_config, config_entry)
device_entry = get_device_entry(hass, sensor_config, source_entity, config_entry)
utility_meters = create_utility_meters(
hass,
energy_sensor,
meter_config,
config_entry,
device_entry=device_entry,
follow_device_name=follow_device_name,
)
entities.extend(utility_meters)
cost_sensor = create_cost_sensor_if_needed(hass, sensor_config, energy_sensor, source_entity, cost_name)
@@ -24,6 +24,7 @@ from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
EntityCategory,
UnitOfEnergy,
UnitOfPower,
)
@@ -59,6 +60,7 @@ from custom_components.powercalc.const import (
CONF_CREATE_ENERGY_SENSOR,
CONF_CREATE_GROUP,
CONF_DISABLE_EXTENDED_ATTRIBUTES,
CONF_ENERGY_SENSOR_CATEGORY,
CONF_ENERGY_SENSOR_PRECISION,
CONF_ENERGY_SENSOR_UNIT_PREFIX,
CONF_EXCLUDE_ENTITIES,
@@ -75,6 +77,7 @@ from custom_components.powercalc.const import (
CONF_HIDE_MEMBERS,
CONF_IGNORE_UNAVAILABLE_STATE,
CONF_INCLUDE_NON_POWERCALC_SENSORS,
CONF_POWER_SENSOR_CATEGORY,
CONF_POWER_SENSOR_PRECISION,
CONF_SENSOR_TYPE,
CONF_SUB_GROUPS,
@@ -428,6 +431,7 @@ def create_grouped_energy_sensor(
name=name,
unique_id=energy_unique_id,
sensor_config=sensor_config,
entity_category=sensor_config.get(CONF_ENERGY_SENSOR_CATEGORY),
unit_prefix=sensor_config.get(CONF_ENERGY_SENSOR_UNIT_PREFIX, UnitPrefix.NONE),
)
@@ -484,6 +488,10 @@ class GroupedSensor(BaseEntity, SensorEntity):
sensor_config.get(CONF_GROUP_POWER_UPDATE_INTERVAL, DEFAULT_GROUP_POWER_UPDATE_INTERVAL),
)
self._attr_suggested_display_precision = self._rounding_digits
category_key = CONF_ENERGY_SENSOR_CATEGORY if self._is_energy_sensor else CONF_POWER_SENSOR_CATEGORY
entity_category = sensor_config.get(category_key)
if entity_category:
self._attr_entity_category = EntityCategory(entity_category)
if unique_id:
self._attr_unique_id = unique_id
self._native_value_exact = Decimal(0)
@@ -7,7 +7,7 @@ from homeassistant.components.sensor import (
SensorEntity,
SensorStateClass,
)
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, UnitOfPower
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, EntityCategory, UnitOfPower
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
@@ -18,6 +18,7 @@ from custom_components.powercalc.const import (
ATTR_MEMBERS,
ATTR_STATE,
CONF_CREATE_ENERGY_SENSORS,
CONF_POWER_SENSOR_CATEGORY,
CONF_POWER_SENSOR_PRECISION,
DATA_STANDBY_POWER_SENSORS,
DEFAULT_POWER_SENSOR_PRECISION,
@@ -40,6 +41,7 @@ def create_general_standby_sensors(
power_sensor = StandbyPowerSensor(
hass,
rounding_digits=int(config.get(CONF_POWER_SENSOR_PRECISION, DEFAULT_POWER_SENSOR_PRECISION)),
entity_category=config.get(CONF_POWER_SENSOR_CATEGORY),
)
sensors.append(power_sensor)
if config.get(CONF_CREATE_ENERGY_SENSORS):
@@ -66,9 +68,16 @@ class StandbyPowerSensor(PowerSensor, SensorEntity):
_attr_unique_id = "powercalc_standby_group"
_attr_name = "All standby power"
def __init__(self, hass: HomeAssistant, rounding_digits: int = 2) -> None:
def __init__(
self,
hass: HomeAssistant,
rounding_digits: int = 2,
entity_category: str | None = None,
) -> None:
self.standby_sensors: dict[str, Decimal] = hass.data[DOMAIN][DATA_STANDBY_POWER_SENSORS]
self._rounding_digits = rounding_digits
if entity_category:
self._attr_entity_category = EntityCategory(entity_category)
async def async_added_to_hass(self) -> None:
"""Register state listeners."""
+7 -2
View File
@@ -88,6 +88,7 @@ from custom_components.powercalc.const import (
CalculationStrategy,
PowerProfileSource,
)
from custom_components.powercalc.device_naming import DeviceName
from custom_components.powercalc.discovery import get_discovery_manager
from custom_components.powercalc.errors import (
ModelNotSupportedError,
@@ -372,6 +373,8 @@ class PowerSensor(BaseEntity):
class VirtualPowerSensor(PowerSensor, SensorEntity):
"""Virtual power sensor."""
device_name = DeviceName("power")
_attr_device_class = SensorDeviceClass.POWER
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_native_unit_of_measurement = UnitOfPower.WATT
@@ -406,7 +409,9 @@ class VirtualPowerSensor(PowerSensor, SensorEntity):
self._attr_force_update = True
self._attr_unique_id = unique_id
multiply_factor = sensor_config.get(CONF_MULTIPLY_FACTOR)
self._multiply_factor: Decimal | None = Decimal(multiply_factor) if multiply_factor else None
self._multiply_factor: Decimal | None = Decimal(multiply_factor) if multiply_factor is not None else None
if multiply_factor is None and power_profile:
self._multiply_factor = power_profile.multiply_factor
self._multiply_factor_standby = bool(sensor_config.get(CONF_MULTIPLY_FACTOR_STANDBY, False))
self._ignore_unavailable_state = bool(sensor_config.get(CONF_IGNORE_UNAVAILABLE_STATE, False))
self._rounding_digits = int(sensor_config.get(CONF_POWER_SENSOR_PRECISION, DEFAULT_POWER_SENSOR_PRECISION))
@@ -722,7 +727,7 @@ class VirtualPowerSensor(PowerSensor, SensorEntity):
def _apply_multiply_factor(self, power: Decimal) -> Decimal:
"""Apply the configured multiply factor to a power value."""
return power * self._multiply_factor if self._multiply_factor else power
return power * self._multiply_factor if self._multiply_factor is not None else power
def _apply_standby_multiply_factor(self, power: Decimal) -> Decimal:
"""Apply the multiply factor to a standby power value, only when enabled for standby."""
@@ -18,6 +18,7 @@ import homeassistant.helpers.entity_registry as er
from homeassistant.helpers.typing import ConfigType, StateType
from homeassistant.util import slugify
from custom_components.powercalc.common import AnyDeviceEntry
from custom_components.powercalc.const import (
CONF_CREATE_UTILITY_METERS,
CONF_ENERGY_SENSOR_PRECISION,
@@ -29,6 +30,7 @@ from custom_components.powercalc.const import (
DEFAULT_ENERGY_SENSOR_PRECISION,
DOMAIN,
)
from custom_components.powercalc.device_naming import DeviceName
from custom_components.powercalc.select import DATA_PENDING_SELECT_ENTITIES, SIGNAL_CREATE_SELECT_ENTITIES
from .abstract import BaseEntity
@@ -44,6 +46,8 @@ def create_utility_meters(
energy_sensor: EnergySensor,
sensor_config: ConfigType,
config_entry: ConfigEntry | None = None,
follow_device_name: bool = False,
device_entry: AnyDeviceEntry | None = None,
) -> list[VirtualUtilityMeter]:
"""Create the utility meters."""
if not sensor_config.get(CONF_CREATE_UTILITY_METERS):
@@ -68,6 +72,8 @@ def create_utility_meters(
unique_id,
meter_type,
tariffs,
device_entry=device_entry,
follow_device_name=follow_device_name,
),
)
@@ -104,6 +110,8 @@ def create_meters_for_type(
unique_id: str | None,
meter_type: str,
tariffs: list[str],
follow_device_name: bool = False,
device_entry: AnyDeviceEntry | None = None,
) -> list[VirtualUtilityMeter]:
"""Create meters for a specific meter type."""
name = f"{energy_sensor.name} {meter_type}"
@@ -138,6 +146,8 @@ def create_meters_for_type(
meter_type,
unique_id,
tariffs,
device_entry=device_entry,
follow_device_name=follow_device_name,
)
tariff_sensors.extend(new_tariff_sensors)
utility_meters.extend(new_tariff_sensors)
@@ -156,10 +166,21 @@ def create_tariff_meters(
meter_type: str,
unique_id: str | None,
tariffs: list[str],
follow_device_name: bool = False,
device_entry: AnyDeviceEntry | None = None,
) -> list[VirtualUtilityMeter]:
"""Create utility meters for specific tariffs."""
filtered_tariffs = [t for t in tariffs if t != GENERAL_TARIFF]
tariff_select = create_tariff_select(config_entry, filtered_tariffs, hass, name, unique_id)
tariff_select = create_tariff_select(
config_entry,
filtered_tariffs,
hass,
name,
unique_id,
device_entry=device_entry,
follow_device_name=follow_device_name,
meter_type=meter_type,
)
tariff_sensors = []
for tariff in filtered_tariffs:
@@ -185,6 +206,9 @@ def create_tariff_select(
hass: HomeAssistant,
name: str,
unique_id: str | None,
follow_device_name: bool = False,
meter_type: str = "",
device_entry: AnyDeviceEntry | None = None,
) -> TariffSelect:
"""Create tariff selection entity."""
_LOGGER.debug("Creating utility_meter tariff select: %s", name)
@@ -193,12 +217,21 @@ def create_tariff_select(
if unique_id:
select_unique_id = f"{unique_id}_select"
tariff_select = TariffSelect(
tariff_select = VirtualTariffSelect(
name,
tariffs,
unique_id=select_unique_id,
)
tariff_select.entity_id = async_generate_entity_id("select.{}", name, hass=hass)
existing_entity_id = None
if select_unique_id:
existing_entity_id = er.async_get(hass).async_get_entity_id("select", DOMAIN, select_unique_id)
tariff_select.entity_id = existing_entity_id or async_generate_entity_id("select.{}", name, hass=hass)
# Platform entities (YAML and group selects) cannot attach a device directly.
if config_entry:
tariff_select.device_entry = device_entry
if follow_device_name:
tariff_select.device_name = DeviceName("utility_meter_cycle", {"period": meter_type})
tariff_select.enable_device_naming()
key = config_entry.entry_id if config_entry else ""
pending = hass.data[DOMAIN].setdefault(DATA_PENDING_SELECT_ENTITIES, {}).setdefault(key, [])
@@ -261,11 +294,19 @@ def create_utility_meter(
)
utility_meter._sensor_config = sensor_config # noqa: SLF001
utility_meter.entity_id = entity_id
utility_meter.device_name = DeviceName("utility_meter_cycle", {"period": meter_type})
if tariff:
utility_meter.device_name = DeviceName("utility_meter_tariff", {"period": meter_type, "tariff": tariff})
return utility_meter
class VirtualTariffSelect(BaseEntity, TariffSelect):
"""Tariff selector supporting opt-in device naming."""
class VirtualUtilityMeter(BaseEntity, UtilityMeterSensor):
device_name = DeviceName("utility_meter_cycle")
rounding_digits: int = DEFAULT_ENERGY_SENSOR_PRECISION
_sensor_config: ConfigType
@@ -300,7 +300,11 @@ class PowerCalculatorStrategyFactory:
def _create_multi_switch(self, config: ConfigType, power_profile: PowerProfile | None) -> MultiSwitchStrategy:
"""Create instance of multi switch strategy."""
multi_switch_config: ConfigType = {}
if power_profile and power_profile.multi_switch_config:
if (
power_profile
and power_profile.is_strategy_supported(CalculationStrategy.MULTI_SWITCH)
and power_profile.multi_switch_config
):
# Copy to avoid mutating the (potentially cached) profile config with the user's config below.
multi_switch_config = dict(power_profile.multi_switch_config)
multi_switch_config.update(config.get(CONF_MULTI_SWITCH, {}))
@@ -319,7 +323,7 @@ class PowerCalculatorStrategyFactory:
self._hass,
entities,
on_power=Decimal(on_power),
off_power=Decimal(off_power) if off_power else None,
off_power=Decimal(off_power) if off_power is not None else None,
)
def _resolve_template(self, value: Any) -> Any: # noqa: ANN401
@@ -1,5 +1,6 @@
from decimal import Decimal
from homeassistant.const import STATE_OFF
from homeassistant.core import State
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import TrackTemplate
@@ -77,3 +78,7 @@ class FixedStrategy(PowerCalculationStrategyInterface):
)
return track_templates
def can_calculate_standby(self) -> bool:
"""Return whether an explicit off-state power value is configured."""
return self._per_state_power is not None and STATE_OFF in self._per_state_power
@@ -41,10 +41,12 @@ class MultiSwitchStrategy(PowerCalculationStrategyInterface):
async def calculate(self, entity_state: State) -> Decimal | None:
if self.known_states is None:
self.known_states = {
entity_id: (state.state if (state := self.hass.states.get(entity_id)) else STATE_UNAVAILABLE)
for entity_id in self.switch_entities
}
self.known_states = dict.fromkeys(self.switch_entities, STATE_UNAVAILABLE)
for entity_id in self.switch_entities:
state = self.hass.states.get(entity_id)
if state is not None:
self.known_states[entity_id] = state.state
if entity_state.entity_id != DUMMY_ENTITY_ID and entity_state.entity_id in self.switch_entities:
self.known_states[entity_state.entity_id] = entity_state.state
@@ -108,7 +108,11 @@
"power_sensor_naming": "Vzor názvu senzoru výkonu",
"power_sensor_friendly_naming": "Vzor přívětivého názvu senzoru výkonu",
"power_sensor_category": "Kategorie senzoru výkonu",
"power_sensor_precision": "Přesnost senzoru výkonu"
"power_sensor_precision": "Přesnost senzoru výkonu",
"follow_device_name": "Používat název zařízení"
},
"data_description": {
"follow_device_name": "Použít název přiřazeného zařízení pro generované entity podporovaných virtuálních senzorů výkonu nakonfigurovaných v uživatelském rozhraní. ID entit a vlastní názvy entit se nezmění. Vypnutím se obnoví nakonfigurované názvy. Vlastní vzory zobrazovaných názvů, včetně globálních vzorů, mají přednost: dotčené položky si ponechají nakonfigurované názvy."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Výkon"
},
"energy": {
"name": "Energie"
},
"standby_energy": {
"name": "Energie v pohotovostním režimu"
},
"cost": {
"name": "Náklady"
},
"utility_meter_cycle": {
"name": "Energie {period}"
},
"utility_meter_tariff": {
"name": "Energie {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energie {period} náklady"
},
"utility_meter_tariff_cost": {
"name": "Energie {period} {tariff} náklady"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energie {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Vzor názvu senzoru výkonu",
"power_sensor_friendly_naming": "Vzor přívětivého názvu senzoru výkonu",
"power_sensor_category": "Kategorie senzoru výkonu",
"power_sensor_precision": "Přesnost senzoru výkonu"
"power_sensor_precision": "Přesnost senzoru výkonu",
"follow_device_name": "Používat název zařízení"
},
"data_description": {
"follow_device_name": "Použít název přiřazeného zařízení pro generované entity podporovaných virtuálních senzorů výkonu nakonfigurovaných v uživatelském rozhraní. ID entit a vlastní názvy entit se nezmění. Vypnutím se obnoví nakonfigurované názvy. Vlastní vzory zobrazovaných názvů, včetně globálních vzorů, mají přednost: dotčené položky si ponechají nakonfigurované názvy."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Navnemønster for effektsensor",
"power_sensor_friendly_naming": "Navnemønster for effektsensorens visningsnavn",
"power_sensor_category": "Effektsensorkategori",
"power_sensor_precision": "Effektsensorens præcision"
"power_sensor_precision": "Effektsensorens præcision",
"follow_device_name": "Følg enhedens navn"
},
"data_description": {
"follow_device_name": "Brug navnet på den tilknyttede enhed til genererede entiteter for understøttede virtuelle effektsensorer konfigureret i brugergrænsefladen. Entitets-id’er og brugerdefinerede entitetsnavne ændres ikke. De konfigurerede navne gendannes, når indstillingen slås fra. Brugerdefinerede mønstre for visningsnavne, herunder globale mønstre, har forrang: berørte poster beholder deres konfigurerede navne."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Effekt"
},
"energy": {
"name": "Energi"
},
"standby_energy": {
"name": "Standbyenergi"
},
"cost": {
"name": "Omkostninger"
},
"utility_meter_cycle": {
"name": "Energi {period}"
},
"utility_meter_tariff": {
"name": "Energi {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energi {period} omkostninger"
},
"utility_meter_tariff_cost": {
"name": "Energi {period} {tariff} omkostninger"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energi {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Navnemønster for effektsensor",
"power_sensor_friendly_naming": "Navnemønster for effektsensorens visningsnavn",
"power_sensor_category": "Effektsensorkategori",
"power_sensor_precision": "Effektsensorens præcision"
"power_sensor_precision": "Effektsensorens præcision",
"follow_device_name": "Følg enhedens navn"
},
"data_description": {
"follow_device_name": "Brug navnet på den tilknyttede enhed til genererede entiteter for understøttede virtuelle effektsensorer konfigureret i brugergrænsefladen. Entitets-id’er og brugerdefinerede entitetsnavne ændres ikke. De konfigurerede navne gendannes, når indstillingen slås fra. Brugerdefinerede mønstre for visningsnavne, herunder globale mønstre, har forrang: berørte poster beholder deres konfigurerede navne."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Muster für den Namen des Leistungssensors",
"power_sensor_friendly_naming": "Muster für den freundlichen Namen des Leistungssensors",
"power_sensor_category": "Leistungssensor-Kategorie",
"power_sensor_precision": "Genauigkeit des Leistungssensors"
"power_sensor_precision": "Genauigkeit des Leistungssensors",
"follow_device_name": "Gerätenamen übernehmen"
},
"data_description": {
"follow_device_name": "Den Namen des zugewiesenen Geräts für erzeugte Entitäten unterstützter virtueller Leistungssensoren verwenden, die über die Benutzeroberfläche konfiguriert wurden. Entitäts-IDs und benutzerdefinierte Entitätsnamen bleiben unverändert. Beim Ausschalten werden die konfigurierten Namen wiederhergestellt. Benutzerdefinierte Muster für Anzeigenamen, einschließlich globaler Muster, haben Vorrang: Betroffene Einträge behalten ihre konfigurierten Namen."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Leistung"
},
"energy": {
"name": "Energie"
},
"standby_energy": {
"name": "Standby-Energie"
},
"cost": {
"name": "Kosten"
},
"utility_meter_cycle": {
"name": "Energie {period}"
},
"utility_meter_tariff": {
"name": "Energie {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energie {period} Kosten"
},
"utility_meter_tariff_cost": {
"name": "Energie {period} {tariff} Kosten"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energie {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Muster für den Namen des Leistungssensors",
"power_sensor_friendly_naming": "Muster für den freundlichen Namen des Leistungssensors",
"power_sensor_category": "Leistungssensor-Kategorie",
"power_sensor_precision": "Genauigkeit des Leistungssensors"
"power_sensor_precision": "Genauigkeit des Leistungssensors",
"follow_device_name": "Gerätenamen übernehmen"
},
"data_description": {
"follow_device_name": "Den Namen des zugewiesenen Geräts für erzeugte Entitäten unterstützter virtueller Leistungssensoren verwenden, die über die Benutzeroberfläche konfiguriert wurden. Entitäts-IDs und benutzerdefinierte Entitätsnamen bleiben unverändert. Beim Ausschalten werden die konfigurierten Namen wiederhergestellt. Benutzerdefinierte Muster für Anzeigenamen, einschließlich globaler Muster, haben Vorrang: Betroffene Einträge behalten ihre konfigurierten Namen."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Power sensor name pattern",
"power_sensor_friendly_naming": "Power sensor friendly name pattern",
"power_sensor_category": "Power sensor category",
"power_sensor_precision": "Power sensor precision"
"power_sensor_precision": "Power sensor precision",
"follow_device_name": "Follow device name"
},
"data_description": {
"follow_device_name": "Use the assigned device name for generated entities of supported virtual power sensors configured in the UI. Entity IDs and custom entity names stay unchanged. Turning this off restores the configured names. Custom display-name patterns, including global patterns, take precedence: affected entries keep their configured names."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Power"
},
"energy": {
"name": "Energy"
},
"standby_energy": {
"name": "Standby energy"
},
"cost": {
"name": "Cost"
},
"utility_meter_cycle": {
"name": "Energy {period}"
},
"utility_meter_tariff": {
"name": "Energy {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energy {period} cost"
},
"utility_meter_tariff_cost": {
"name": "Energy {period} {tariff} cost"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energy {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Power sensor name pattern",
"power_sensor_friendly_naming": "Power sensor friendly name pattern",
"power_sensor_category": "Power sensor category",
"power_sensor_precision": "Power sensor precision"
"power_sensor_precision": "Power sensor precision",
"follow_device_name": "Follow device name"
},
"data_description": {
"follow_device_name": "Use the assigned device name for generated entities of supported virtual power sensors configured in the UI. Entity IDs and custom entity names stay unchanged. Turning this off restores the configured names. Custom display-name patterns, including global patterns, take precedence: affected entries keep their configured names."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Patrón de nombre de sensor",
"power_sensor_friendly_naming": "Patrón de nombre amigable del sensor de energía",
"power_sensor_category": "Categoría de sensor de energía",
"power_sensor_precision": "Precisión del sensor de potencia"
"power_sensor_precision": "Precisión del sensor de potencia",
"follow_device_name": "Usar el nombre del dispositivo"
},
"data_description": {
"follow_device_name": "Usar el nombre del dispositivo asignado para las entidades generadas de los sensores virtuales de potencia compatibles configurados en la interfaz. Los ID de entidad y los nombres personalizados no cambian. Al desactivar esta opción se restauran los nombres configurados. Los patrones personalizados de nombres visibles, incluidos los globales, tienen prioridad: las entradas afectadas conservan sus nombres configurados."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Potencia"
},
"energy": {
"name": "Energía"
},
"standby_energy": {
"name": "Energía en espera"
},
"cost": {
"name": "Coste"
},
"utility_meter_cycle": {
"name": "Energía {period}"
},
"utility_meter_tariff": {
"name": "Energía {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energía {period} coste"
},
"utility_meter_tariff_cost": {
"name": "Energía {period} {tariff} coste"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energía {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Patrón de nombre de sensor",
"power_sensor_friendly_naming": "Patrón de nombre amigable de sensor de energía",
"power_sensor_category": "Categoría del sensor de potencia",
"power_sensor_precision": "Precisión del sensor de potencia"
"power_sensor_precision": "Precisión del sensor de potencia",
"follow_device_name": "Usar el nombre del dispositivo"
},
"data_description": {
"follow_device_name": "Usar el nombre del dispositivo asignado para las entidades generadas de los sensores virtuales de potencia compatibles configurados en la interfaz. Los ID de entidad y los nombres personalizados no cambian. Al desactivar esta opción se restauran los nombres configurados. Los patrones personalizados de nombres visibles, incluidos los globales, tienen prioridad: las entradas afectadas conservan sus nombres configurados."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Tehoanturin nimimalli",
"power_sensor_friendly_naming": "Tehoanturin näyttönimen malli",
"power_sensor_category": "Tehoanturin luokka",
"power_sensor_precision": "Tehoanturin tarkkuus"
"power_sensor_precision": "Tehoanturin tarkkuus",
"follow_device_name": "Käytä laitteen nimeä"
},
"data_description": {
"follow_device_name": "Käytä liitetyn laitteen nimeä käyttöliittymässä määritettyjen tuettujen virtuaalisten tehoanturien luoduille entiteeteille. Entiteettitunnukset ja mukautetut entiteettien nimet eivät muutu. Asetuksen poistaminen käytöstä palauttaa määritetyt nimet. Mukautetut näyttönimimallit, myös yleiset mallit, ovat etusijalla: niitä käyttävät määritykset säilyttävät määritetyt nimensä."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Teho"
},
"energy": {
"name": "Energia"
},
"standby_energy": {
"name": "Valmiustilan energia"
},
"cost": {
"name": "Kustannukset"
},
"utility_meter_cycle": {
"name": "Energia {period}"
},
"utility_meter_tariff": {
"name": "Energia {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energia {period} kustannukset"
},
"utility_meter_tariff_cost": {
"name": "Energia {period} {tariff} kustannukset"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energia {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Tehoanturin nimimalli",
"power_sensor_friendly_naming": "Tehoanturin näyttönimen malli",
"power_sensor_category": "Tehoanturin luokka",
"power_sensor_precision": "Tehoanturin tarkkuus"
"power_sensor_precision": "Tehoanturin tarkkuus",
"follow_device_name": "Käytä laitteen nimeä"
},
"data_description": {
"follow_device_name": "Käytä liitetyn laitteen nimeä käyttöliittymässä määritettyjen tuettujen virtuaalisten tehoanturien luoduille entiteeteille. Entiteettitunnukset ja mukautetut entiteettien nimet eivät muutu. Asetuksen poistaminen käytöstä palauttaa määritetyt nimet. Mukautetut näyttönimimallit, myös yleiset mallit, ovat etusijalla: niitä käyttävät määritykset säilyttävät määritetyt nimensä."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Modèle de nom du capteur de puissance",
"power_sensor_friendly_naming": "Modèle de nom convivial du capteur de puissance",
"power_sensor_category": "Catégorie du capteur de puissance",
"power_sensor_precision": "Précision du capteur de puissance"
"power_sensor_precision": "Précision du capteur de puissance",
"follow_device_name": "Utiliser le nom de l’appareil"
},
"data_description": {
"follow_device_name": "Utiliser le nom de l’appareil associé pour les entités générées des capteurs de puissance virtuels pris en charge configurés dans l’interface. Les identifiants et les noms personnalisés des entités restent inchangés. Désactiver cette option rétablit les noms configurés. Les modèles de noms d’affichage personnalisés, y compris les modèles globaux, sont prioritaires : les entrées concernées conservent leurs noms configurés."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Puissance"
},
"energy": {
"name": "Énergie"
},
"standby_energy": {
"name": "Énergie en veille"
},
"cost": {
"name": "Coût"
},
"utility_meter_cycle": {
"name": "Énergie {period}"
},
"utility_meter_tariff": {
"name": "Énergie {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Énergie {period} coût"
},
"utility_meter_tariff_cost": {
"name": "Énergie {period} {tariff} coût"
}
},
"select": {
"utility_meter_cycle": {
"name": "Énergie {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Modèle de nom du capteur de puissance",
"power_sensor_friendly_naming": "Modèle de nom convivial du capteur de puissance",
"power_sensor_category": "Catégorie du capteur de puissance",
"power_sensor_precision": "Précision du capteur de puissance"
"power_sensor_precision": "Précision du capteur de puissance",
"follow_device_name": "Utiliser le nom de l’appareil"
},
"data_description": {
"follow_device_name": "Utiliser le nom de l’appareil associé pour les entités générées des capteurs de puissance virtuels pris en charge configurés dans l’interface. Les identifiants et les noms personnalisés des entités restent inchangés. Désactiver cette option rétablit les noms configurés. Les modèles de noms d’affichage personnalisés, y compris les modèles globaux, sont prioritaires : les entrées concernées conservent leurs noms configurés."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Teljesítményérzékelő névmintája",
"power_sensor_friendly_naming": "Teljesítményérzékelő megjelenítési névmintája",
"power_sensor_category": "Teljesítményérzékelő kategóriája",
"power_sensor_precision": "Teljesítményérzékelő pontossága"
"power_sensor_precision": "Teljesítményérzékelő pontossága",
"follow_device_name": "Eszköznév követése"
},
"data_description": {
"follow_device_name": "A hozzárendelt eszköz nevének használata a felületen beállított, támogatott virtuális teljesítményérzékelők létrehozott entitásaihoz. Az entitásazonosítók és az egyéni entitásnevek változatlanok maradnak. Kikapcsoláskor visszaállnak a beállított nevek. Az egyéni megjelenítési névminták, beleértve a globális mintákat is, elsőbbséget élveznek: az érintett bejegyzések megtartják a beállított neveiket."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Teljesítmény"
},
"energy": {
"name": "Energia"
},
"standby_energy": {
"name": "Készenléti energia"
},
"cost": {
"name": "Költség"
},
"utility_meter_cycle": {
"name": "Energia {period}"
},
"utility_meter_tariff": {
"name": "Energia {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energia {period} költség"
},
"utility_meter_tariff_cost": {
"name": "Energia {period} {tariff} költség"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energia {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "A teljesítményérzékelő névmintája",
"power_sensor_friendly_naming": "Teljesítményérzékelő megjelenítési nevének mintája",
"power_sensor_category": "Teljesítményérzékelő kategória",
"power_sensor_precision": "Teljesítményérzékelő pontossága"
"power_sensor_precision": "Teljesítményérzékelő pontossága",
"follow_device_name": "Eszköznév követése"
},
"data_description": {
"follow_device_name": "A hozzárendelt eszköz nevének használata a felületen beállított, támogatott virtuális teljesítményérzékelők létrehozott entitásaihoz. Az entitásazonosítók és az egyéni entitásnevek változatlanok maradnak. Kikapcsoláskor visszaállnak a beállított nevek. Az egyéni megjelenítési névminták, beleértve a globális mintákat is, elsőbbséget élveznek: az érintett bejegyzések megtartják a beállított neveiket."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Nome per gli schemi del Sensore di potenza",
"power_sensor_friendly_naming": "Nome amichevole per gli schemi del Sensore di potenza ",
"power_sensor_category": "Categoria sensore di potenza",
"power_sensor_precision": "Precisione del sensore di potenza"
"power_sensor_precision": "Precisione del sensore di potenza",
"follow_device_name": "Usa il nome del dispositivo"
},
"data_description": {
"follow_device_name": "Usa il nome del dispositivo assegnato per le entità generate dei sensori di potenza virtuali supportati configurati tramite l’interfaccia. Gli ID delle entità e i nomi personalizzati rimangono invariati. Disattivando questa opzione vengono ripristinati i nomi configurati. I modelli personalizzati dei nomi visualizzati, inclusi quelli globali, hanno la precedenza: le voci interessate mantengono i nomi configurati."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Potenza"
},
"energy": {
"name": "Energia"
},
"standby_energy": {
"name": "Energia in standby"
},
"cost": {
"name": "Costo"
},
"utility_meter_cycle": {
"name": "Energia {period}"
},
"utility_meter_tariff": {
"name": "Energia {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energia {period} costo"
},
"utility_meter_tariff_cost": {
"name": "Energia {period} {tariff} costo"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energia {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Modello di nome del sensore di potenza",
"power_sensor_friendly_naming": "Modello di nome descrittivo del sensore di potenza",
"power_sensor_category": "Categoria sensore di potenza",
"power_sensor_precision": "Precisione del sensore di potenza"
"power_sensor_precision": "Precisione del sensore di potenza",
"follow_device_name": "Usa il nome del dispositivo"
},
"data_description": {
"follow_device_name": "Usa il nome del dispositivo assegnato per le entità generate dei sensori di potenza virtuali supportati configurati tramite l’interfaccia. Gli ID delle entità e i nomi personalizzati rimangono invariati. Disattivando questa opzione vengono ripristinati i nomi configurati. I modelli personalizzati dei nomi visualizzati, inclusi quelli globali, hanno la precedenza: le voci interessate mantengono i nomi configurati."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Navnemønster for effektsensor",
"power_sensor_friendly_naming": "Mønster for visningsnavn på effektsensor",
"power_sensor_category": "Kategori for effektsensor",
"power_sensor_precision": "Presisjon for effektsensor"
"power_sensor_precision": "Presisjon for effektsensor",
"follow_device_name": "Følg enhetsnavnet"
},
"data_description": {
"follow_device_name": "Bruk navnet på den tilordnede enheten for genererte entiteter til støttede virtuelle effektsensorer konfigurert i brukergrensesnittet. Entitets-ID-er og egendefinerte entitetsnavn forblir uendret. Når innstillingen slås av, gjenopprettes de konfigurerte navnene. Egendefinerte mønstre for visningsnavn, inkludert globale mønstre, har forrang: berørte oppføringer beholder de konfigurerte navnene."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Effekt"
},
"energy": {
"name": "Energi"
},
"standby_energy": {
"name": "Standbyenergi"
},
"cost": {
"name": "Kostnad"
},
"utility_meter_cycle": {
"name": "Energi {period}"
},
"utility_meter_tariff": {
"name": "Energi {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energi {period} kostnad"
},
"utility_meter_tariff_cost": {
"name": "Energi {period} {tariff} kostnad"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energi {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Navnemønster for effektsensor",
"power_sensor_friendly_naming": "Mønster for visningsnavn på effektsensor",
"power_sensor_category": "Strømsensorkategori",
"power_sensor_precision": "Kraftsensor presisjon"
"power_sensor_precision": "Kraftsensor presisjon",
"follow_device_name": "Følg enhetsnavnet"
},
"data_description": {
"follow_device_name": "Bruk navnet på den tilordnede enheten for genererte entiteter til støttede virtuelle effektsensorer konfigurert i brukergrensesnittet. Entitets-ID-er og egendefinerte entitetsnavn forblir uendret. Når innstillingen slås av, gjenopprettes de konfigurerte navnene. Egendefinerte mønstre for visningsnavn, inkludert globale mønstre, har forrang: berørte oppføringer beholder de konfigurerte navnene."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Patroon voor naam vermogenssensor",
"power_sensor_friendly_naming": "Patroon voor vriendelijke naam vermogenssensor",
"power_sensor_category": "Vermogenssensor categorie",
"power_sensor_precision": "Precisie van vermogenssensor"
"power_sensor_precision": "Precisie van vermogenssensor",
"follow_device_name": "Apparaatnaam volgen"
},
"data_description": {
"follow_device_name": "Gebruik de naam van het gekoppelde apparaat voor gegenereerde entiteiten van ondersteunde virtuele vermogenssensoren die via de gebruikersinterface zijn ingesteld. Entiteits-ID’s en aangepaste entiteitsnamen blijven ongewijzigd. Uitschakelen herstelt de ingestelde namen. Aangepaste patronen voor weergavenamen, inclusief globale patronen, hebben voorrang: de betreffende configuraties behouden hun ingestelde namen."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Mislukt",
"resumable": "Hervatbaar"
}
},
"power": {
"name": "Vermogen"
},
"energy": {
"name": "Energie"
},
"standby_energy": {
"name": "Stand-by-energie"
},
"cost": {
"name": "Kosten"
},
"utility_meter_cycle": {
"name": "Energie {period}"
},
"utility_meter_tariff": {
"name": "Energie {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energie {period} kosten"
},
"utility_meter_tariff_cost": {
"name": "Energie {period} {tariff} kosten"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energie {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Vermogensensor naampatroon",
"power_sensor_friendly_naming": "Vermogensensor vriendelijke naampatroon",
"power_sensor_category": "Vermogensensor categorie",
"power_sensor_precision": "Precisie van vermogenssensor"
"power_sensor_precision": "Precisie van vermogenssensor",
"follow_device_name": "Apparaatnaam volgen"
},
"data_description": {
"follow_device_name": "Gebruik de naam van het gekoppelde apparaat voor gegenereerde entiteiten van ondersteunde virtuele vermogenssensoren die via de gebruikersinterface zijn ingesteld. Entiteits-ID’s en aangepaste entiteitsnamen blijven ongewijzigd. Uitschakelen herstelt de ingestelde namen. Aangepaste patronen voor weergavenamen, inclusief globale patronen, hebben voorrang: de betreffende configuraties behouden hun ingestelde namen."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Wzorzec nazwy sensora mocy",
"power_sensor_friendly_naming": "Wzorzec przyjaznej nazwy sensora mocy",
"power_sensor_category": "Kategoria sensorów mocy",
"power_sensor_precision": "Dokładność czujnika mocy"
"power_sensor_precision": "Dokładność czujnika mocy",
"follow_device_name": "Używaj nazwy urządzenia"
},
"data_description": {
"follow_device_name": "Używaj nazwy przypisanego urządzenia dla generowanych encji obsługiwanych wirtualnych czujników mocy skonfigurowanych w interfejsie. Identyfikatory encji i własne nazwy encji pozostają bez zmian. Wyłączenie przywraca skonfigurowane nazwy. Własne wzorce nazw wyświetlanych, w tym wzorce globalne, mają pierwszeństwo: wpisy, których to dotyczy, zachowują skonfigurowane nazwy."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Moc"
},
"energy": {
"name": "Energia"
},
"standby_energy": {
"name": "Energia w trybie czuwania"
},
"cost": {
"name": "Koszt"
},
"utility_meter_cycle": {
"name": "Energia {period}"
},
"utility_meter_tariff": {
"name": "Energia {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energia {period} koszt"
},
"utility_meter_tariff_cost": {
"name": "Energia {period} {tariff} koszt"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energia {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Wzorzec nazwy sensora mocy",
"power_sensor_friendly_naming": "Wzorzec przyjaznej nazwy sensora mocy",
"power_sensor_category": "Kategoria czujników mocy",
"power_sensor_precision": "Dokładność czujnika mocy"
"power_sensor_precision": "Dokładność czujnika mocy",
"follow_device_name": "Używaj nazwy urządzenia"
},
"data_description": {
"follow_device_name": "Używaj nazwy przypisanego urządzenia dla generowanych encji obsługiwanych wirtualnych czujników mocy skonfigurowanych w interfejsie. Identyfikatory encji i własne nazwy encji pozostają bez zmian. Wyłączenie przywraca skonfigurowane nazwy. Własne wzorce nazw wyświetlanych, w tym wzorce globalne, mają pierwszeństwo: wpisy, których to dotyczy, zachowują skonfigurowane nazwy."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Padrão de nome do sensor de energia",
"power_sensor_friendly_naming": "Padrão de nome amigável para sensor de potência",
"power_sensor_category": "Categoria do sensor de energia",
"power_sensor_precision": "Precisão do sensor de potência"
"power_sensor_precision": "Precisão do sensor de potência",
"follow_device_name": "Usar o nome do dispositivo"
},
"data_description": {
"follow_device_name": "Usar o nome do dispositivo atribuído para as entidades geradas dos sensores virtuais de potência compatíveis configurados pela interface. Os IDs das entidades e os nomes personalizados permanecem inalterados. Desativar esta opção restaura os nomes configurados. Os padrões personalizados de nomes de exibição, incluindo os globais, têm prioridade: as entradas afetadas mantêm os nomes configurados."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Potência"
},
"energy": {
"name": "Energia"
},
"standby_energy": {
"name": "Energia em espera"
},
"cost": {
"name": "Custo"
},
"utility_meter_cycle": {
"name": "Energia {period}"
},
"utility_meter_tariff": {
"name": "Energia {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energia {period} custo"
},
"utility_meter_tariff_cost": {
"name": "Energia {period} {tariff} custo"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energia {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Padrão de nome do sensor de potência",
"power_sensor_friendly_naming": "Padrão de nome amigável para sensor de potência",
"power_sensor_category": "Categoria do sensor de potência",
"power_sensor_precision": "Precisão do sensor de potência"
"power_sensor_precision": "Precisão do sensor de potência",
"follow_device_name": "Usar o nome do dispositivo"
},
"data_description": {
"follow_device_name": "Usar o nome do dispositivo atribuído para as entidades geradas dos sensores virtuais de potência compatíveis configurados pela interface. Os IDs das entidades e os nomes personalizados permanecem inalterados. Desativar esta opção restaura os nomes configurados. Os padrões personalizados de nomes de exibição, incluindo os globais, têm prioridade: as entradas afetadas mantêm os nomes configurados."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Padrão de nome do sensor de potência",
"power_sensor_friendly_naming": "Padrão de nome amigável do sensor de potência",
"power_sensor_category": "Categoria do sensor de potência",
"power_sensor_precision": "Precisão do sensor de potência"
"power_sensor_precision": "Precisão do sensor de potência",
"follow_device_name": "Usar o nome do dispositivo"
},
"data_description": {
"follow_device_name": "Usar o nome do dispositivo atribuído para as entidades geradas dos sensores virtuais de potência suportados configurados na interface. Os IDs das entidades e os nomes personalizados permanecem inalterados. Desativar esta opção repõe os nomes configurados. Os padrões personalizados de nomes de apresentação, incluindo os globais, têm prioridade: as entradas afetadas mantêm os nomes configurados."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Potência"
},
"energy": {
"name": "Energia"
},
"standby_energy": {
"name": "Energia em espera"
},
"cost": {
"name": "Custo"
},
"utility_meter_cycle": {
"name": "Energia {period}"
},
"utility_meter_tariff": {
"name": "Energia {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energia {period} custo"
},
"utility_meter_tariff_cost": {
"name": "Energia {period} {tariff} custo"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energia {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Padrão de nome do sensor de potência",
"power_sensor_friendly_naming": "Padrão de nome amigável do sensor de potência",
"power_sensor_category": "Categoria do sensor de potência",
"power_sensor_precision": "Precisão do sensor de potência"
"power_sensor_precision": "Precisão do sensor de potência",
"follow_device_name": "Usar o nome do dispositivo"
},
"data_description": {
"follow_device_name": "Usar o nome do dispositivo atribuído para as entidades geradas dos sensores virtuais de potência suportados configurados na interface. Os IDs das entidades e os nomes personalizados permanecem inalterados. Desativar esta opção repõe os nomes configurados. Os padrões personalizados de nomes de apresentação, incluindo os globais, têm prioridade: as entradas afetadas mantêm os nomes configurados."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Modelul numelui senzorului de putere",
"power_sensor_friendly_naming": "Model de nume prietenos pentru senzorul de putere",
"power_sensor_category": "Categoria senzorului de putere",
"power_sensor_precision": "Precizia senzorului de putere"
"power_sensor_precision": "Precizia senzorului de putere",
"follow_device_name": "Folosește numele dispozitivului"
},
"data_description": {
"follow_device_name": "Folosește numele dispozitivului asociat pentru entitățile generate ale senzorilor virtuali de putere acceptați, configurați în interfață. ID-urile entităților și numele personalizate rămân neschimbate. Dezactivarea restabilește numele configurate. Modelele personalizate pentru numele afișate, inclusiv cele globale, au prioritate: intrările afectate își păstrează numele configurate."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Putere"
},
"energy": {
"name": "Energie"
},
"standby_energy": {
"name": "Energie în standby"
},
"cost": {
"name": "Cost"
},
"utility_meter_cycle": {
"name": "Energie {period}"
},
"utility_meter_tariff": {
"name": "Energie {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energie {period} cost"
},
"utility_meter_tariff_cost": {
"name": "Energie {period} {tariff} cost"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energie {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Modelul numelui senzorului de putere",
"power_sensor_friendly_naming": "Model de nume prietenos pentru senzorul de putere",
"power_sensor_category": "Categoria senzorului de putere",
"power_sensor_precision": "Precizia senzorului de putere"
"power_sensor_precision": "Precizia senzorului de putere",
"follow_device_name": "Folosește numele dispozitivului"
},
"data_description": {
"follow_device_name": "Folosește numele dispozitivului asociat pentru entitățile generate ale senzorilor virtuali de putere acceptați, configurați în interfață. ID-urile entităților și numele personalizate rămân neschimbate. Dezactivarea restabilește numele configurate. Modelele personalizate pentru numele afișate, inclusiv cele globale, au prioritate: intrările afectate își păstrează numele configurate."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Шаблон имени сенсора мощности",
"power_sensor_friendly_naming": "Шаблон понятного имени сенсора мощности",
"power_sensor_category": "Категория сенсора мощности",
"power_sensor_precision": "Точность датчика мощности"
"power_sensor_precision": "Точность датчика мощности",
"follow_device_name": "Использовать имя устройства"
},
"data_description": {
"follow_device_name": "Использовать имя назначенного устройства для создаваемых сущностей поддерживаемых виртуальных датчиков мощности, настроенных через интерфейс. Идентификаторы и пользовательские имена сущностей не изменяются. При отключении восстанавливаются настроенные имена. Пользовательские шаблоны отображаемых имён, включая глобальные, имеют приоритет: затронутые записи сохраняют настроенные имена."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Мощность"
},
"energy": {
"name": "Энергия"
},
"standby_energy": {
"name": "Энергия в режиме ожидания"
},
"cost": {
"name": "Стоимость"
},
"utility_meter_cycle": {
"name": "Энергия {period}"
},
"utility_meter_tariff": {
"name": "Энергия {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Энергия {period} стоимость"
},
"utility_meter_tariff_cost": {
"name": "Энергия {period} {tariff} стоимость"
}
},
"select": {
"utility_meter_cycle": {
"name": "Энергия {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Шаблон имени сенсора мощности",
"power_sensor_friendly_naming": "Шаблон понятного имени сенсора мощности",
"power_sensor_category": "Категория сенсора мощности",
"power_sensor_precision": "Точность датчика мощности"
"power_sensor_precision": "Точность датчика мощности",
"follow_device_name": "Использовать имя устройства"
},
"data_description": {
"follow_device_name": "Использовать имя назначенного устройства для создаваемых сущностей поддерживаемых виртуальных датчиков мощности, настроенных через интерфейс. Идентификаторы и пользовательские имена сущностей не изменяются. При отключении восстанавливаются настроенные имена. Пользовательские шаблоны отображаемых имён, включая глобальные, имеют приоритет: затронутые записи сохраняют настроенные имена."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Vzor názvu snímača výkonu",
"power_sensor_friendly_naming": "Vzor názvu priateľského snímača výkonu",
"power_sensor_category": "Kategória snímača výkonu",
"power_sensor_precision": "Presnosť senzora výkonu"
"power_sensor_precision": "Presnosť senzora výkonu",
"follow_device_name": "Používať názov zariadenia"
},
"data_description": {
"follow_device_name": "Použiť názov priradeného zariadenia pre generované entity podporovaných virtuálnych snímačov výkonu nakonfigurovaných v používateľskom rozhraní. ID entít a vlastné názvy entít sa nezmenia. Vypnutím sa obnovia nakonfigurované názvy. Vlastné vzory zobrazovaných názvov vrátane globálnych vzorov majú prednosť: dotknuté položky si ponechajú nakonfigurované názvy."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Výkon"
},
"energy": {
"name": "Energia"
},
"standby_energy": {
"name": "Energia v pohotovostnom režime"
},
"cost": {
"name": "Náklady"
},
"utility_meter_cycle": {
"name": "Energia {period}"
},
"utility_meter_tariff": {
"name": "Energia {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energia {period} náklady"
},
"utility_meter_tariff_cost": {
"name": "Energia {period} {tariff} náklady"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energia {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Vzor názvu snímača výkonu",
"power_sensor_friendly_naming": "Vzor názvu priateľského snímača výkonu",
"power_sensor_category": "Kategória snímača výkonu",
"power_sensor_precision": "Presnosť senzora výkonu"
"power_sensor_precision": "Presnosť senzora výkonu",
"follow_device_name": "Používať názov zariadenia"
},
"data_description": {
"follow_device_name": "Použiť názov priradeného zariadenia pre generované entity podporovaných virtuálnych snímačov výkonu nakonfigurovaných v používateľskom rozhraní. ID entít a vlastné názvy entít sa nezmenia. Vypnutím sa obnovia nakonfigurované názvy. Vlastné vzory zobrazovaných názvov vrátane globálnych vzorov majú prednosť: dotknuté položky si ponechajú nakonfigurované názvy."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "Namnmönster för effektsensor",
"power_sensor_friendly_naming": "Strömsensorvänligt namnmönster",
"power_sensor_category": "Kategori för strömsensor",
"power_sensor_precision": "Effektsensorns precision"
"power_sensor_precision": "Effektsensorns precision",
"follow_device_name": "Följ enhetens namn"
},
"data_description": {
"follow_device_name": "Använd namnet på den tilldelade enheten för genererade entiteter till virtuella effektsensorer som stöds och har konfigurerats i användargränssnittet. Entitets-ID:n och anpassade entitetsnamn förblir oförändrade. När inställningen stängs av återställs de konfigurerade namnen. Anpassade mönster för visningsnamn, inklusive globala mönster, har företräde: berörda poster behåller sina konfigurerade namn."
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "Effekt"
},
"energy": {
"name": "Energi"
},
"standby_energy": {
"name": "Standbyenergi"
},
"cost": {
"name": "Kostnad"
},
"utility_meter_cycle": {
"name": "Energi {period}"
},
"utility_meter_tariff": {
"name": "Energi {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "Energi {period} kostnad"
},
"utility_meter_tariff_cost": {
"name": "Energi {period} {tariff} kostnad"
}
},
"select": {
"utility_meter_cycle": {
"name": "Energi {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "Namnmönster för effektsensor",
"power_sensor_friendly_naming": "Strömsensorvänligt namnmönster",
"power_sensor_category": "Effektsensorkategori",
"power_sensor_precision": "Effektsensorns precision"
"power_sensor_precision": "Effektsensorns precision",
"follow_device_name": "Följ enhetens namn"
},
"data_description": {
"follow_device_name": "Använd namnet på den tilldelade enheten för genererade entiteter till virtuella effektsensorer som stöds och har konfigurerats i användargränssnittet. Entitets-ID:n och anpassade entitetsnamn förblir oförändrade. När inställningen stängs av återställs de konfigurerade namnen. Anpassade mönster för visningsnamn, inklusive globala mönster, har företräde: berörda poster behåller sina konfigurerade namn."
}
},
"features": {
@@ -108,7 +108,11 @@
"power_sensor_naming": "功率传感器名称模式",
"power_sensor_friendly_naming": "功率传感器友好名称模式",
"power_sensor_category": "功率传感器类别",
"power_sensor_precision": "功率传感器精度"
"power_sensor_precision": "功率传感器精度",
"follow_device_name": "跟随设备名称"
},
"data_description": {
"follow_device_name": "对于通过用户界面配置且受支持的虚拟功率传感器,使用所关联设备的名称来命名生成的实体。实体 ID 和自定义实体名称保持不变。关闭此选项将恢复配置的名称。自定义显示名称模式(包括全局模式)优先:受影响的配置项将保留其配置的名称。"
}
},
"features": {
@@ -623,6 +627,35 @@
"failed": "Failed",
"resumable": "Resumable"
}
},
"power": {
"name": "功率"
},
"energy": {
"name": "电能"
},
"standby_energy": {
"name": "待机电能"
},
"cost": {
"name": "费用"
},
"utility_meter_cycle": {
"name": "电能 {period}"
},
"utility_meter_tariff": {
"name": "电能 {period} {tariff}"
},
"utility_meter_cycle_cost": {
"name": "电能 {period} 费用"
},
"utility_meter_tariff_cost": {
"name": "电能 {period} {tariff} 费用"
}
},
"select": {
"utility_meter_cycle": {
"name": "电能 {period}"
}
}
},
@@ -774,7 +807,11 @@
"power_sensor_naming": "功率传感器名称模式",
"power_sensor_friendly_naming": "功率传感器友好名称模式",
"power_sensor_category": "功率传感器类别",
"power_sensor_precision": "功率传感器精度"
"power_sensor_precision": "功率传感器精度",
"follow_device_name": "跟随设备名称"
},
"data_description": {
"follow_device_name": "对于通过用户界面配置且受支持的虚拟功率传感器,使用所关联设备的名称来命名生成的实体。实体 ID 和自定义实体名称保持不变。关闭此选项将恢复配置的名称。自定义显示名称模式(包括全局模式)优先:受影响的配置项将保留其配置的名称。"
}
},
"features": {