393 files
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user