Skip to content

Commit

Permalink
Merge branch 'main' into next_reset_dst
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldotnl authored Mar 16, 2024
2 parents 4dab9a7 + 78c6127 commit 1c7b7d2
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 45 deletions.
6 changes: 5 additions & 1 deletion .devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true,
"python.experiments.optOutFrom": ["pythonTestAdapter"]
"python.experiments.optOutFrom": ["pythonTestAdapter"],
"python.testing.pytestArgs": ["tests"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.experiments.enabled": false
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ jobs:
zip measureit.zip -r ./
- name: "Upload the ZIP file to the release"
uses: softprops/[email protected].1
uses: softprops/[email protected].4
with:
files: ${{ github.workspace }}/custom_components/measureit/measureit.zip
1 change: 1 addition & 0 deletions custom_components/measureit/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
selector.SelectOptionDict(value="month", label="month"),
selector.SelectOptionDict(value="year", label="year"),
selector.SelectOptionDict(value="noreset", label="noreset"),
selector.SelectOptionDict(value="session", label="session"),
]

DAY_OPTIONS = [
Expand Down
1 change: 1 addition & 0 deletions custom_components/measureit/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"month": "0 0 1 * *",
"year": "0 0 1 1 *",
"noreset": "noreset",
"session": "session",
}


Expand Down
10 changes: 5 additions & 5 deletions custom_components/measureit/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ async def async_setup_entry(
for sensor in config_entry.options[CONF_SENSOR]:
unique_id = sensor.get(CONF_UNIQUE_ID)
sensor_name = f"{config_name}_{sensor[CONF_SENSOR_NAME]}"
reset_pattern = (
sensor.get(CONF_CRON) if sensor.get(CONF_CRON) not in ["noreset", "forever", "none"] else None
)
reset_pattern = sensor.get(CONF_CRON)
state_class = sensor.get(CONF_STATE_CLASS)
device_class = sensor.get(CONF_DEVICE_CLASS)
uom = sensor.get(CONF_UNIT_OF_MEASUREMENT)
Expand Down Expand Up @@ -350,13 +348,12 @@ def schedule_next_reset(self, next_reset: datetime | None = None):
self.reset()
return
elif not next_reset:
if self._reset_pattern:
if self._reset_pattern not in [None, "noreset", "forever", "none", "session"]:
# we have a known issue with croniter that does not correctly determine the end of the month/week reset when DST is involved
# https://github.com/kiorky/croniter/issues/1
next_reset = dt_util.as_local(croniter(self._reset_pattern, tznow.replace(tzinfo=None)).get_next(datetime))
if not tz.datetime_exists(next_reset):
next_reset = dt_util.as_local(croniter(self._reset_pattern, next_reset.replace(tzinfo=None)).get_next(datetime))

else:
self._next_reset = None
return
Expand Down Expand Up @@ -411,6 +408,9 @@ def _on_sensor_state_update(
self.meter.start()
if old_state == SensorState.MEASURING:
self.meter.stop()
self._async_write_ha_state()
if self._reset_pattern == "session":
self.reset()

@property
def extra_restore_state_data(self) -> MeasureItSensorStoredData:
Expand Down
11 changes: 6 additions & 5 deletions custom_components/measureit/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
},
"sensors": {
"title": "Configure the sensors (how)",
"description": "Configure the sensors. When in doubt, stick to the defaults. Individual sensor settings can be adjusted after this setup via 'configure'.\n\n**Periods:** Select the periods you want to measure (when the meter will reset). Each period becomes a separate sensor.\n**Value template:** A template that is applied on the output of the sensor. Use `value` to refer to the sensor state.\n**Unit of measurement:** The unit of what your are measuring. E.g.: m3\n**Device class:** Find more about device classes [here](https://www.home-assistant.io/integrations/sensor/#device-class).\n**State class**: Find more about state classes [here](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes).",
"description": "Configure the sensors. When in doubt, stick to the defaults. Individual sensor settings can be adjusted after this setup via 'configure'.\n\n**Reset periods:** Select the periods you want to measure (when the meter will reset). Each period becomes a separate sensor.\n**Value template:** A template that is applied on the output of the sensor. Use `value` to refer to the sensor state.\n**Unit of measurement:** The unit of what your are measuring. E.g.: m3\n**Device class:** Find more about device classes [here](https://www.home-assistant.io/integrations/sensor/#device-class).\n**State class**: Find more about state classes [here](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes).",
"data": {
"periods": "Periods:",
"periods": "Reset periods:",
"unit_of_measurement": "Unit of measurement",
"value_template": "Value template:",
"state_class": "State class",
Expand Down Expand Up @@ -76,9 +76,9 @@
},
"add_sensors": {
"title": "Add sensor(s)",
"description": "Add and configure one or more sensors. When in doubt, stick to the defaults.\n\n**Periods:** Select the periods you want to measure (when the meter will reset). Each period becomes a separate sensor.\n**Value template:** A template that is applied on the output of the sensor. Use `value` to refer to the sensor state.\n**Unit of measurement:** The unit of what your are measuring. E.g.: m3\n**Device class:** Find more about device classes [here](https://www.home-assistant.io/integrations/sensor/#device-class).\n**State class**: Find more about state classes [here](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes).",
"description": "Add and configure one or more sensors. When in doubt, stick to the defaults.\n\n**Reset periods:** Select the periods you want to measure (when the meter will reset). Each period becomes a separate sensor.\n**Value template:** A template that is applied on the output of the sensor. Use `value` to refer to the sensor state.\n**Unit of measurement:** The unit of what your are measuring. E.g.: m3\n**Device class:** Find more about device classes [here](https://www.home-assistant.io/integrations/sensor/#device-class).\n**State class**: Find more about state classes [here](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes).",
"data": {
"period": "Periods:",
"period": "Reset periods:",
"unit_of_measurement": "Unit of measurement",
"value_template": "Value template:",
"state_class": "State class",
Expand Down Expand Up @@ -138,7 +138,8 @@
"week": "Week",
"month": "Month",
"year": "Year",
"noreset": "Manual/no reset"
"noreset": "Manual/no reset",
"session": "After each session"
}
}
},
Expand Down
65 changes: 41 additions & 24 deletions custom_components/measureit/translations/sk.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
"description": "Ďakujeme, že ste nastavili MeasureIt! Ak potrebujete pomoc s konfiguráciou, pozrite sa sem: https://github.com/danieldotnl/ha-measureit.\nČas meria trvanie, zatiaľ čo podmienka (šablóna) je pravdivá. Zdroj bude merať zmeny hodnoty inej entity, pričom podmienka (šablóna) sa rovná true.",
"menu_options": {
"time": "Čas",
"source": "Zdrojový snímač"
"source": "Zdrojový snímač",
"count": "Počítadlo"
}
},
"time": {
"title": "Konfigurácia merača času",
"description": "Nakonfigurujte názov konfigurácie (používaný pre názvy snímačov a protokolovanie) a jednotku merania.",
"data": {
"name": "Názov konfigurácie"
"config_name": "Názov konfigurácie"
}
},
"source": {
Expand All @@ -24,6 +25,14 @@
"source_entity": "Zdrojová entita"
}
},
"count": {
"title": "Nakonfigurujte počítadlo (čo)",
"description": "Nakonfigurujte názov konfigurácie (používaný pre názvy snímačov a protokolovanie) a šablónu počítadla.",
"data": {
"config_name": "Názov konfigurácie",
"counter_template": "Šablóna počítadla:"
}
},
"when": {
"title": "Kedy chcete merať?",
"description": "Nakonfigurujte voliteľnú podmienku (šablónu) a/alebo dni a čas, kedy chcete merať. Predvolené: vždy merať. Ak je čas od neskorší ako čas do, predpokladá sa, že časové okno prekročí polnoc.",
Expand All @@ -37,14 +46,6 @@
"sensors": {
"title": "Nakonfigurujte obdobia na meranie",
"description": "Vyberte periódy, ktoré chcete merať (keď sa glukomer vynuluje) a voliteľne zadajte ďalšie polia. Každá perióda sa stáva senzorom.",
"menu_options": {
"5m": "5 minút",
"hour": "Hodina",
"day": "Deň",
"week": "Týždeň",
"month": "Mesiac",
"year": "Rok"
},
"data": {
"period": "Obdobia",
"unit_of_measurement": "Jednotka merania",
Expand All @@ -59,9 +60,7 @@
}
},
"error": {
"auth": "Používateľské meno/heslo je nesprávne.",
"connection": "Nedá sa pripojiť k serveru.",
"unknown": "Vyskytla sa neznáma chyba."
"tw_days_minimum": "Vyberte aspoň jeden deň na meranie."
}
},
"options": {
Expand All @@ -78,14 +77,6 @@
"add_sensors": {
"title": "Pridajte senzory",
"description": "Vyberte periódy, ktoré chcete merať (keď sa glukomer vynuluje) a voliteľne zadajte ďalšie polia. Každá perióda sa stáva senzorom.",
"menu_options": {
"5m": "5 Minút",
"hour": "Hodina",
"day": "Deň",
"week": "Týždeň",
"month": "Mesiac",
"year": "Rok"
},
"data": {
"period": "Obdobia",
"unit_of_measurement": "Jednotka merania",
Expand All @@ -97,7 +88,6 @@
"edit_main": {
"title": "Upravte hlavnú konfiguráciu",
"data": {
"config_name": "Názov",
"condition": "Šablóna stavu",
"when_days": "Dni:",
"when_from": "Od času",
Expand All @@ -112,7 +102,6 @@
"title": "Upraviť senzor",
"description": "Upravte konfiguráciu vybraného snímača.",
"data": {
"sensor_name": "Názov",
"unit_of_measurement": "Jednotka merania",
"value_template": "Šablóna hodnoty",
"state_class": "Trieda stavu",
Expand All @@ -123,10 +112,38 @@
"title": "Odstráňte senzor(y)",
"description": "Vyberte senzory, ktoré chcete odstrániť."
}
},
"error": {
"tw_days_minimum": "Vyberte aspoň jeden deň na meranie.",
"uom_with_device_class_update": "Aktualizácia mernej jednotky nie je povolená, keď je nastavená trieda zariadenia. Vyberte snímač a pridajte nový."
}
},
"selector": {
"day_selector": {
"options": {
"0": "Pondelok",
"1": "Utorok",
"2": "Streda",
"3": "Štvrtok",
"4": "Piatok",
"5": "Sobota",
"6": "Nedeľa"
}
},
"period_selector": {
"options": {
"5m": "5 minút",
"hour": "Hodina",
"day": "Deň",
"week": "Týždeň",
"month": "Mesiac",
"year": "Rok",
"noreset": "Manuálne/bez resetovania"
}
}
},
"services": {
"reset_sensor": {
"reset": {
"name": "Resetujte senzor MeasureIt",
"description": "Resetujte senzor v danom čase. Ak nie je daný čas, snímač sa okamžite resetuje.",
"fields": {
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
colorlog==6.8.2
pip>=24,<25
ruff==0.3.2
pytest-homeassistant-custom-component==0.13.107
ruff==0.3.3
pytest-homeassistant-custom-component==0.13.108
croniter==2.0.2
isort==5.13.2
83 changes: 76 additions & 7 deletions tests/integration/test_time_meter_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

from datetime import datetime
from decimal import Decimal

from freezegun import freeze_time
from pytest_homeassistant_custom_component.common import (
MockConfigEntry,
async_fire_time_changed,
async_fire_time_changed_exact,
)
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from custom_components.measureit.const import DOMAIN, SensorState
from pytest_homeassistant_custom_component.common import (
MockConfigEntry, async_fire_time_changed, async_fire_time_changed_exact)

from custom_components.measureit.const import (DOMAIN, PREDEFINED_PERIODS,
SensorState)
from tests import setup_with_mock_config, unload_with_mock_config

TIME_ENTRY = MockConfigEntry(
Expand Down Expand Up @@ -51,6 +50,15 @@
"cron": "0 0 * * 1",
"period": "week",
},
{
"unit_of_measurement": "s",
"state_class": "total",
"device_class": "duration",
"unique_id": "ca1009aa-b6bb-11ee-923e-0242ac110000",
"sensor_name": "session",
"cron": "session",
"period": "session",
}
],
},
)
Expand Down Expand Up @@ -79,11 +87,19 @@
"period": "day",
"value_template": "{{ (value | float / 3600) | round(3) }}",
},
{
"unit_of_measurement": "s",
"state_class": "total",
"device_class": "duration",
"unique_id": "ca1009aa-b6bb-11ee-923e-0242ac110002",
"sensor_name": "month",
"cron": PREDEFINED_PERIODS["month"],
"period": "month",
},
],
},
)


async def test_time_meter_setup(hass: HomeAssistant):
"""Test MeasureIt setup for source meter."""
hass.states.async_set("switch.test_switch", "on")
Expand Down Expand Up @@ -328,3 +344,56 @@ async def test_format_time_with_template(hass: HomeAssistant):
state = hass.states.get(sensor)
assert state.attributes["status"] == SensorState.MEASURING
assert state.state == "13.397"

async def test_reset_after_session(hass: HomeAssistant):
"""Test if meter resets after session."""

current_time = datetime(2024, 3, 11, 8, 0, tzinfo=dt_util.DEFAULT_TIME_ZONE)
with freeze_time(current_time) as mock_time:
await setup_with_mock_config(hass, TIME_ENTRY)
async_fire_time_changed(hass, current_time)
await hass.async_block_till_done()

hass.states.async_set("switch.test_switch", "on")
await hass.async_block_till_done()

sensor = "sensor.test_session"
state = hass.states.get(sensor)
assert state.attributes["status"] == SensorState.MEASURING
assert state.state == "0"
assert state.attributes["prev_period"] == "0"
assert state.attributes["sensor_last_reset"] == current_time.isoformat()
assert state.attributes["sensor_next_reset"] is None

current_time = datetime(
2024, 3, 11, 10, 0, tzinfo=dt_util.DEFAULT_TIME_ZONE
)
mock_time.move_to(current_time)
async_fire_time_changed(hass, current_time)
hass.states.async_set("switch.test_switch", "off")
await hass.async_block_till_done()

sensor = "sensor.test_session"
state = hass.states.get(sensor)
assert state.attributes["status"] == SensorState.WAITING_FOR_CONDITION
assert state.state == "0"
assert state.attributes["prev_period"] == "7200"
assert state.attributes["sensor_last_reset"] == current_time.isoformat()
assert state.attributes["sensor_next_reset"] is None





# async def test_sensor_next_reset(hass: HomeAssistant):
# """Test if sensor next and last reset attributes are set correctly."""
# current_time = datetime(2024, 3, 9, 4, 0, tzinfo=dt_util.DEFAULT_TIME_ZONE)
# with freeze_time(current_time) as mock_time:
# await setup_with_mock_config(hass, FORMATTED_TIME_ENTRY)
# async_fire_time_changed(hass, current_time)
# await hass.async_block_till_done()

# sensor = "sensor.test_month"
# state = hass.states.get(sensor)
# assert state.attributes["sensor_last_reset"] == current_time.isoformat()
# assert state.attributes["sensor_next_reset"] == datetime(2024, 4, 1, 0, 0, tzinfo=dt_util.DEFAULT_TIME_ZONE).isoformat()

0 comments on commit 1c7b7d2

Please sign in to comment.