Skip to content
Open
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
13 changes: 13 additions & 0 deletions custom_components/sonic/CHANGELOG_0.2.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SONIC 0.2.4 — test build

- Removed legacy timezone dependency from telemetry timestamp handling.
- Uses module-level ZoneInfo for the Sonic API timestamp.
- Handles a missing telemetry timestamp safely.
- Uses a timezone-aware datetime.
- Uses Home Assistant's standard L/min flow-rate unit.
- Replaced async_timeout with asyncio.timeout.
- Removed redundant one-task asyncio.gather calls.
- Runs Sonic details and telemetry API requests concurrently.
- Fixed PropertyEntity.async_update reference.
- Uses coordinator update success for property availability.
- Version 0.2.4.
15 changes: 5 additions & 10 deletions custom_components/sonic/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from datetime import timedelta
from typing import Any

from async_timeout import timeout
from herolabsapi.client import Client
from herolabsapi.errors import RequestError

Expand Down Expand Up @@ -35,12 +34,8 @@ def __init__(self, hass: HomeAssistant, api_client: Client, device_id: str) -> N
async def _async_update_data(self):
"""Update data via library."""
try:
async with timeout(10):
await asyncio.gather(
*[
self._update_device(),
]
)
async with asyncio.timeout(10):
await self._update_device()
except (RequestError) as error:
raise UpdateFailed(error) from error

Expand Down Expand Up @@ -111,19 +106,19 @@ def battery_state(self) -> str:
@property
def auto_shut_off_enabled(self) -> bool:
"""Return the auto shut off enabled boolean"""
return self._device_information["auto_shut_off_enabled"]
return self._device_information.get("auto_shut_off_enabled")

@property
def auto_shut_off_time_limit(self) -> int:
"""Return the Sonic offline auto shut off water usage time limit in seconds[0;integer::max).
When set to 0 usage time check is not performed."""
return self._device_information["auto_shut_off_time_limit"]
return self._device_information.get("auto_shut_off_time_limit")

@property
def auto_shut_off_volume_limit(self) -> int:
"""Return the Sonic offline auto shut off used water volume limit in millilitres [0;integer::max).
When set to 0 volume used check is not performed."""
return self._device_information["auto_shut_off_volume_limit"]
return self._device_information.get("auto_shut_off_volume_limit")

@property
def signal_id(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/sonic/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def available(self) -> bool:

async def async_update(self):
"""Update Property entity."""
await self._property.async_request_refresh()
await self._device.async_request_refresh()

async def async_added_to_hass(self):
"""When entity is added to hass."""
Expand Down
14 changes: 10 additions & 4 deletions custom_components/sonic/manifest.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
{
"domain": "sonic",
"name": "Sonic (Hero Labs)",
"codeowners": ["@markvader"],
"codeowners": [
"@markvader"
],
"config_flow": true,
"documentation": "https://github.com/markvader/sonic_hacs",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/markvader/sonic_hacs/issues",
"loggers": ["sonic"],
"requirements": ["herolabsapi==0.5.1"],
"version": "v0.2.3"
"loggers": [
"sonic"
],
"requirements": [
"herolabsapi==0.5.1"
],
"version": "0.2.4"
}
9 changes: 2 additions & 7 deletions custom_components/sonic/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from datetime import timedelta
from typing import Any

from async_timeout import timeout
from herolabsapi.client import Client
from herolabsapi.errors import RequestError

Expand Down Expand Up @@ -36,12 +35,8 @@ def __init__(self, hass: HomeAssistant, api_client: Client, property_id: str) ->
async def _async_update_data(self):
"""Update data via library."""
try:
async with timeout(10):
await asyncio.gather(
*[
self._update_property(),
]
)
async with asyncio.timeout(10):
await self._update_property()
except (RequestError) as error:
raise UpdateFailed(error) from error

Expand Down
31 changes: 15 additions & 16 deletions custom_components/sonic/sensor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"""The Sonic Water Shut-off Valve integration."""
from __future__ import annotations
from datetime import datetime
import pytz

from zoneinfo import ZoneInfo
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
Expand All @@ -13,6 +12,7 @@
UnitOfPressure,
UnitOfTemperature,
UnitOfVolume,
UnitOfVolumeFlowRate,
UnitOfTime,
)
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -80,18 +80,18 @@ async def async_setup_entry(
async_add_entities(entities)



SONIC_TIMEZONE = ZoneInfo("Europe/London")
class SonicCurrentFlowRateSensor(SonicEntity, SensorEntity):
"""Monitors the current water flow rate."""

_attr_icon = GAUGE_ICON
_attr_native_unit_of_measurement = "litres per min"
_attr_native_unit_of_measurement = UnitOfVolumeFlowRate.LITERS_PER_MINUTE
_attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT

def __init__(self, device):
"""Initialize the flow rate sensor."""
super().__init__("current_flow_rate", NAME_FLOW_RATE, device)
self._state: float = None

@property
def native_value(self) -> float | None:
"""Return the current flow rate in Litre per minute."""
Expand All @@ -110,8 +110,6 @@ class SonicTemperatureSensor(SonicEntity, SensorEntity):
def __init__(self, device):
"""Initialize the temperature sensor."""
super().__init__("temperature", NAME_WATER_TEMPERATURE, device)
self._state: float = None

@property
def native_value(self) -> float | None:
"""Return the current temperature."""
Expand All @@ -130,8 +128,6 @@ class SonicPressureSensor(SonicEntity, SensorEntity):
def __init__(self, device):
"""Initialize the water pressure sensor."""
super().__init__("water_pressure", NAME_WATER_PRESSURE, device)
self._state: float = None

@property
def native_value(self) -> float | None:
"""Return the current water pressure in bar."""
Expand All @@ -153,7 +149,7 @@ def __init__(self, device):
self._state: str = None

@property
def native_value(self) -> str | None:
def native_value(self) -> datetime | None:
"""Return the current battery state."""
return self._device.battery_state

Expand All @@ -174,10 +170,11 @@ def __init__(self, device):
def native_value(self) -> str | None:
"""Return the current telemetry time state."""
telemetry_timestamp = self._device.last_heard_from_time
# telemetry_timezone = self._device.property_timezone
timezone = pytz.timezone("Europe/London")
telemetry_datetime = datetime.fromtimestamp(telemetry_timestamp, timezone)
return telemetry_datetime

if telemetry_timestamp is None:
return None

return datetime.fromtimestamp(telemetry_timestamp, SONIC_TIMEZONE)


class SonicValveStateSensor(SonicEntity, SensorEntity):
Expand Down Expand Up @@ -232,7 +229,8 @@ def __init__(self, device):
@property
def native_value(self) -> int | None:
"""Return the auto_shut_off_time_limit state in minutes."""
return round((self._device.auto_shut_off_time_limit)/60)
val = self._device.auto_shut_off_time_limit
return round(val / 60) if val is not None else None


class SonicAutoShutOffVolumeLimitSensor(SonicEntity, SensorEntity):
Expand All @@ -250,7 +248,8 @@ def __init__(self, device):
@property
def native_value(self) -> int | None:
"""Return the auto_shut_off_volume_limit state."""
return round((self._device.auto_shut_off_volume_limit)/1000)
val = self._device.auto_shut_off_volume_limit
return round(val / 1000) if val is not None else None

class PropertyLongFlowNotificationDelay(PropertyEntity, SensorEntity):
"""Return the long flow notification delay in minutes at property"""
Expand Down
Loading