Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"features": {
"ghcr.io/devcontainers-extra/features/apt-packages:1": {
"version": "1.0.6",
"resolved": "ghcr.io/devcontainers-extra/features/apt-packages@sha256:55c54412112da81b9381e470cdbbe55278564950d1ff536ce925b1e8e096babd",
"integrity": "sha256:55c54412112da81b9381e470cdbbe55278564950d1ff536ce925b1e8e096babd"
},
"ghcr.io/devcontainers/features/git:1": {
"version": "1.3.5",
"resolved": "ghcr.io/devcontainers/features/git@sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251",
"integrity": "sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251"
},
"ghcr.io/devcontainers/features/node:2": {
"version": "2.0.0",
"resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f",
"integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f"
}
}
}
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{
"git.autofetch": true,
"python.createEnvironment.trigger": "off",
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.analysis.extraPaths": [
"${workspaceFolder}",
"${workspaceFolder}/custom_components"
],
"python.analysis.diagnosticSeverityOverrides": {
"reportUnsortedImports": "none"
},
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
Expand Down
10 changes: 5 additions & 5 deletions custom_components/periodic_min_max/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@
import voluptuous as vol
from awesomeversion.awesomeversion import AwesomeVersion

from homeassistant.core import HomeAssistant, callback
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_ENTITY_ID,
__version__ as HA_VERSION, # noqa: N812
)
from homeassistant.helpers import entity_registry as er, config_validation as cv
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import ConfigType
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, entity_registry as er
from homeassistant.helpers.helper_integration import (
async_handle_source_entity_changes,
async_remove_helper_config_entry_from_source_device,
)
from homeassistant.helpers.typing import ConfigType

from .const import (
DOMAIN,
LOGGER,
PLATFORMS,
MIN_HA_VERSION,
PLATFORMS,
)
from .services import async_setup_services

Expand Down
13 changes: 7 additions & 6 deletions custom_components/periodic_min_max/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

from __future__ import annotations

from typing import Any, cast
from collections.abc import Mapping
from typing import Any, cast

import voluptuous as vol

from homeassistant.const import CONF_TYPE, CONF_ENTITY_ID
from homeassistant.helpers import selector
from homeassistant.components.input_number import DOMAIN as INPUT_NUMBER_DOMAIN
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.input_number import DOMAIN as INPUT_NUMBER_DOMAIN
from homeassistant.const import CONF_ENTITY_ID, CONF_TYPE
from homeassistant.helpers import selector
from homeassistant.helpers.schema_config_entry_flow import (
SchemaFlowFormStep,
SchemaConfigFlowHandler,
SchemaFlowFormStep,
)

from .const import DOMAIN
from .const import CONF_EQUAL_UPDATES, DOMAIN

_STATISTIC_MEASURES = ["min", "max"]

Expand All @@ -35,6 +35,7 @@
options=_STATISTIC_MEASURES, translation_key=CONF_TYPE
),
),
vol.Required(CONF_EQUAL_UPDATES, default=False): selector.BooleanSelector(),
}
)

Expand Down
2 changes: 2 additions & 0 deletions custom_components/periodic_min_max/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@

PLATFORMS = [Platform.SENSOR]

CONF_EQUAL_UPDATES = "equal_updates"

ATTR_LAST_MODIFIED = "last_modified"
58 changes: 32 additions & 26 deletions custom_components/periodic_min_max/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,36 @@

from __future__ import annotations

from typing import Any
from datetime import datetime
from typing import Any

import voluptuous as vol

from homeassistant.core import Event, HomeAssistant, EventStateChangedData, callback
from homeassistant.util import dt as dt_util
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_TYPE,
STATE_UNKNOWN,
ATTR_UNIT_OF_MEASUREMENT,
CONF_ENTITY_ID,
CONF_TYPE,
STATE_UNAVAILABLE,
ATTR_UNIT_OF_MEASUREMENT,
STATE_UNKNOWN,
)
from homeassistant.core import Event, EventStateChangedData, HomeAssistant, callback
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.event import (
async_track_state_change_event,
async_track_entity_registry_updated_event,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import StateType
from homeassistant.components.sensor import (
SensorEntity,
SensorStateClass,
SensorDeviceClass,
async_track_state_change_event,
)
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util import dt as dt_util

from .const import (
LOGGER,
ATTR_LAST_MODIFIED,
)
from .const import ATTR_LAST_MODIFIED, CONF_EQUAL_UPDATES, LOGGER

ATTR_MIN_VALUE = "min_value"
ATTR_MAX_VALUE = "max_value"
Expand Down Expand Up @@ -91,6 +88,7 @@ async def async_setup_entry(
device_id = source_entity.device_id if source_entity else None

sensor_type = config_entry.options[CONF_TYPE]
equal_updates = config_entry.options.get(CONF_EQUAL_UPDATES, False)

async def async_registry_updated(
event: Event[er.EventEntityRegistryUpdatedData],
Expand Down Expand Up @@ -138,6 +136,7 @@ async def async_registry_updated(
source_entity_id,
config_entry.title,
sensor_type,
equal_updates,
config_entry.entry_id,
)
]
Expand All @@ -155,18 +154,20 @@ class PeriodicMinMaxSensor(SensorEntity, RestoreEntity):
_state_had_real_change = False
_attr_last_modified: str = dt_util.utcnow().isoformat()

def __init__(
def __init__( # noqa: PLR0913
self,
hass: HomeAssistant,
source_entity_id: str,
name: str | None,
sensor_type: str,
equal_updates: bool,
unique_id: str | None,
) -> None:
"""Initialize the min/max sensor."""
self._attr_unique_id = unique_id
self._source_entity_id = source_entity_id
self._sensor_type = sensor_type
self._equal_updates = equal_updates

if name:
self._attr_name = name
Expand Down Expand Up @@ -325,18 +326,23 @@ def _calc_values(self) -> None:
"""Calculate the values."""
self._state_had_real_change = False

"""Calculate min value, honoring unknown states."""
if self._state in [STATE_UNKNOWN, STATE_UNAVAILABLE]:
return

if self._sensor_attr == ATTR_MIN_VALUE:
if self._state not in [STATE_UNKNOWN, STATE_UNAVAILABLE] and (
self.min_value is None or self.min_value > self._state
if self.min_value is None or (
self.min_value >= self._state
if self._equal_updates
else self.min_value > self._state
):
self.min_value = self._state
self._state_had_real_change = True

"""Calculate max value, honoring unknown states."""
if self._sensor_attr == ATTR_MAX_VALUE:
if self._state not in [STATE_UNKNOWN, STATE_UNAVAILABLE] and (
self.max_value is None or self.max_value < self._state
if self.max_value is None or (
self.max_value <= self._state
if self._equal_updates
else self.max_value < self._state
):
self.max_value = self._state
self._state_had_real_change = True
Expand Down
2 changes: 1 addition & 1 deletion custom_components/periodic_min_max/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from __future__ import annotations

from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import service
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN

from .const import DOMAIN

Expand Down
20 changes: 16 additions & 4 deletions custom_components/periodic_min_max/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
"description": "Create a sensor that is the minimum or maximum of another sensor, resetttable via an action.",
"data": {
"entity_id": "Input entity",
"equal_updates": "Update on equal values",
"name": "Name",
"type": "Statistikmerkmal"
"type": "Statistic characteristic"
},
"data_description": {
"entity_id": "The entity that the sensor will track. The sensor will calculate the minimum or maximum value of this entity until reset via the `reset` action.",
"equal_updates": "If enabled, the sensor will update the `last_modified` attribute even if the input entity value is the same as the current min/max.",
"type": "Whether the sensor should track the minimum or maximum value of the input entity"
}
}
}
Expand All @@ -17,7 +23,13 @@
"init": {
"data": {
"entity_id": "Input entity",
"type": "Statistikmerkmal"
"equal_updates": "Update on equal values",
"type": "Statistic characteristic"
},
"data_description": {
"entity_id": "The entity that the sensor will track. The sensor will calculate the minimum or maximum value of this entity until reset via the `reset` action.",
"equal_updates": "If enabled, the sensor will update the `last_modified` attribute even if the input entity value is the same as the current min/max.",
"type": "Whether the sensor should track the minimum or maximum value of the input entity"
}
}
}
Expand All @@ -32,11 +44,11 @@
},
"services": {
"reset": {
"name": "Zurücksetzen",
"name": "Reset",
"description": "Reset a Periodic Min/Max sensor to the current input entity value.",
"fields": {
"entity_id": {
"name": "Entität",
"name": "Entity",
"description": "Select the Periodic Min/Max sensor."
}
}
Expand Down
12 changes: 12 additions & 0 deletions custom_components/periodic_min_max/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
"description": "Create a sensor that is the minimum or maximum of another sensor, resetttable via an action.",
"data": {
"entity_id": "Input entity",
"equal_updates": "Update on equal values",
"name": "Name",
"type": "Statistic characteristic"
},
"data_description": {
"entity_id": "The entity that the sensor will track. The sensor will calculate the minimum or maximum value of this entity until reset via the `reset` action.",
"equal_updates": "If enabled, the sensor will update the `last_modified` attribute even if the input entity value is the same as the current min/max.",
"type": "Whether the sensor should track the minimum or maximum value of the input entity"
}
}
}
Expand All @@ -17,7 +23,13 @@
"init": {
"data": {
"entity_id": "Input entity",
"equal_updates": "Update on equal values",
"type": "Statistic characteristic"
},
"data_description": {
"entity_id": "The entity that the sensor will track. The sensor will calculate the minimum or maximum value of this entity until reset via the `reset` action.",
"equal_updates": "If enabled, the sensor will update the `last_modified` attribute even if the input entity value is the same as the current min/max.",
"type": "Whether the sensor should track the minimum or maximum value of the input entity"
}
}
}
Expand Down
22 changes: 17 additions & 5 deletions custom_components/periodic_min_max/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
"description": "Create a sensor that is the minimum or maximum of another sensor, resetttable via an action.",
"data": {
"entity_id": "Input entity",
"name": "Nom",
"type": "Indicateur statistique"
"equal_updates": "Update on equal values",
"name": "Name",
"type": "Statistic characteristic"
},
"data_description": {
"entity_id": "The entity that the sensor will track. The sensor will calculate the minimum or maximum value of this entity until reset via the `reset` action.",
"equal_updates": "If enabled, the sensor will update the `last_modified` attribute even if the input entity value is the same as the current min/max.",
"type": "Whether the sensor should track the minimum or maximum value of the input entity"
}
}
}
Expand All @@ -17,7 +23,13 @@
"init": {
"data": {
"entity_id": "Input entity",
"type": "Indicateur statistique"
"equal_updates": "Update on equal values",
"type": "Statistic characteristic"
},
"data_description": {
"entity_id": "The entity that the sensor will track. The sensor will calculate the minimum or maximum value of this entity until reset via the `reset` action.",
"equal_updates": "If enabled, the sensor will update the `last_modified` attribute even if the input entity value is the same as the current min/max.",
"type": "Whether the sensor should track the minimum or maximum value of the input entity"
}
}
}
Expand All @@ -32,11 +44,11 @@
},
"services": {
"reset": {
"name": "Réinitialiser",
"name": "Reset",
"description": "Reset a Periodic Min/Max sensor to the current input entity value.",
"fields": {
"entity_id": {
"name": "Entité",
"name": "Entity",
"description": "Select the Periodic Min/Max sensor."
}
}
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ ignore = [
max-complexity = 25

[tool.ruff.lint.isort]
length-sort = true
length-sort = false
section-order = [
"future",
"standard-library",
Expand Down
8 changes: 4 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

from __future__ import annotations

from collections.abc import Generator
from typing import Any
from unittest.mock import AsyncMock, patch
from collections.abc import Generator

import pytest
from custom_components.periodic_min_max.const import DOMAIN
from pytest_homeassistant_custom_component.common import MockConfigEntry

from homeassistant.core import HomeAssistant
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import (
CONF_ENTITY_ID,
CONF_NAME,
CONF_TYPE,
CONF_ENTITY_ID,
)
from homeassistant.config_entries import SOURCE_USER
from homeassistant.core import HomeAssistant

from .test_sensor import VALUES_NUMERIC

Expand Down
Loading
Loading