Skip to content

Commit

Permalink
Maintenance update
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoGos committed Dec 24, 2024
1 parent 8b3339d commit 8745c56
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 94 deletions.
65 changes: 40 additions & 25 deletions custom_components/davis_vantage/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""The Davis Vantage integration."""

from __future__ import annotations
from typing import Any
import logging
Expand All @@ -24,21 +25,20 @@
CONFIG_INTERVAL,
CONFIG_PROTOCOL,
CONFIG_LINK,
DATA_ARCHIVE_PERIOD
DATA_ARCHIVE_PERIOD,
)
from .coordinator import DavisVantageDataUpdateCoordinator
from .utils import convert_to_iso_datetime

PLATFORMS: list[Platform] = [
Platform.SENSOR,
Platform.BINARY_SENSOR
]
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.BINARY_SENSOR]

_LOGGER: logging.Logger = logging.getLogger(__package__)


async def async_setup(hass: HomeAssistant, config: Any) -> bool:
return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Davis Vantage from a config entry."""
if hass.data.get(DOMAIN) is None:
Expand All @@ -50,51 +50,57 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
link = entry.data.get(CONFIG_LINK, "")
rain_collector = entry.data.get(CONFIG_RAIN_COLLECTOR, RAIN_COLLECTOR_IMPERIAL)

hass.data[DOMAIN]['interval'] = entry.data.get(CONFIG_INTERVAL, 30)
hass.data[DOMAIN]["interval"] = entry.data.get(CONFIG_INTERVAL, 30)

client = DavisVantageClient(hass, protocol, link, rain_collector)
await client.connect_to_station()
static_info = await client.async_get_static_info()
firmware_version = static_info.get('version', None) if static_info is not None else None
hass.data.setdefault(DATA_ARCHIVE_PERIOD, static_info.get('archive_period', None) if static_info is not None else None)
firmware_version = (
static_info.get("version", None) if static_info is not None else None
)
hass.data.setdefault(
DATA_ARCHIVE_PERIOD,
static_info.get("archive_period", None) if static_info is not None else None,
)

device_info = DeviceInfo(
identifiers={(DOMAIN, entry.entry_id)},
manufacturer=MANUFACTURER,
name=NAME,
model=entry.data.get(CONFIG_STATION_MODEL, "Unknown"),
sw_version=firmware_version,
hw_version=None
hw_version=None,
)

hass.data[DOMAIN][entry.entry_id] = coordinator = DavisVantageDataUpdateCoordinator(
hass=hass, client=client, device_info=device_info)
hass=hass, client=client, device_info=device_info
)

await coordinator.async_config_entry_first_refresh()

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(async_reload_entry))

async def set_davis_time(call: ServiceCall) -> None:
async def set_davis_time(_: ServiceCall) -> None:
await client.async_set_davis_time()

async def get_davis_time(call: ServiceCall) -> dict[str, Any]:
async def get_davis_time(_: ServiceCall) -> dict[str, Any]:
davis_time = await client.async_get_davis_time()
if davis_time is not None:
return {
"davis_time": convert_to_iso_datetime(davis_time, ZoneInfo(hass.config.time_zone))
"davis_time": convert_to_iso_datetime(
davis_time, ZoneInfo(hass.config.time_zone)
)
}
else:
return {
"error": "Couldn't get davis time, please try again later"
}
return {"error": "Couldn't get davis time, please try again later"}

async def get_raw_data(call: ServiceCall) -> dict[str, Any]:
async def get_raw_data(_: ServiceCall) -> dict[str, Any]:
raw_data = client.get_raw_data()
json_data = safe_serialize(raw_data)
return json.loads(json_data)

async def get_info(call: ServiceCall) -> dict[str, Any]:
async def get_info(_: ServiceCall) -> dict[str, Any]:
info = await client.async_get_info()
if info is not None:
return info
Expand All @@ -103,27 +109,36 @@ async def get_info(call: ServiceCall) -> dict[str, Any]:
"error": "Couldn't get firmware information from Davis weather station"
}

hass.services.async_register(DOMAIN, SERVICE_SET_DAVIS_TIME, set_davis_time)
hass.services.async_register(
DOMAIN, SERVICE_SET_DAVIS_TIME, set_davis_time
)
hass.services.async_register(
DOMAIN, SERVICE_GET_DAVIS_TIME, get_davis_time, supports_response=SupportsResponse.ONLY
DOMAIN,
SERVICE_GET_DAVIS_TIME,
get_davis_time,
supports_response=SupportsResponse.ONLY,
)

hass.services.async_register(
DOMAIN, SERVICE_GET_RAW_DATA, get_raw_data, supports_response=SupportsResponse.ONLY
DOMAIN,
SERVICE_GET_RAW_DATA,
get_raw_data,
supports_response=SupportsResponse.ONLY,
)

hass.services.async_register(
DOMAIN, SERVICE_GET_INFO, get_info, supports_response=SupportsResponse.ONLY
)

def safe_serialize(obj: Any):
default = lambda o: f"<<non-serializable: {type(o).__qualname__}>>" # type: ignore
return json.dumps(obj, default=default) # type: ignore
# default = lambda obj: f"<<non-serializable: {type(obj).__qualname__}>>" # type: ignore
default = get_default(obj)
return json.dumps(obj, default=default) # type: ignore

def get_default(obj: Any):
return f"<<non-serializable: {type(obj).__qualname__}>>"

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unloaded := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
Expand Down
17 changes: 10 additions & 7 deletions custom_components/davis_vantage/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from functools import cached_property
from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorEntity,
Expand All @@ -12,12 +13,10 @@
from .coordinator import DavisVantageDataUpdateCoordinator

DESCRIPTIONS: list[BinarySensorEntityDescription] = [
BinarySensorEntityDescription(
key="IsRaining",
translation_key="is_raining"
)
BinarySensorEntityDescription(key="IsRaining", translation_key="is_raining")
]


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
Expand Down Expand Up @@ -57,11 +56,15 @@ def __init__(
"""Initialize Davis Vantage sensor."""
super().__init__(coordinator=coordinator)
self.entity_description = description
self.entity_id = f"{BINARY_SENSOR_DOMAIN}.{DEFAULT_NAME} {KEY_TO_NAME[description.key]}".lower()
self._attr_unique_id = f"{entry_id}-{DEFAULT_NAME} {KEY_TO_NAME[description.key]}"
self.entity_id = (
f"{BINARY_SENSOR_DOMAIN}.{DEFAULT_NAME} {KEY_TO_NAME[description.key]}".lower()
)
self._attr_unique_id = (
f"{entry_id}-{DEFAULT_NAME} {KEY_TO_NAME[description.key]}"
)
self._attr_device_info = coordinator.device_info

@property
@cached_property
def is_on(self) -> bool | None:
"""Return the is_on of the sensor."""
key = self.entity_description.key
Expand Down
74 changes: 32 additions & 42 deletions custom_components/davis_vantage/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""All client function"""

from typing import Any
from datetime import datetime, timedelta, time, date
from zoneinfo import ZoneInfo
Expand Down Expand Up @@ -33,14 +34,10 @@
class DavisVantageClient:
"""Davis Vantage Client class"""

_vantagepro2: VantagePro2 = None # type: ignore
_vantagepro2: VantagePro2 = None # type: ignore

def __init__(
self,
hass: HomeAssistant,
protocol: str,
link: str,
rain_collector: str
self, hass: HomeAssistant, protocol: str, link: str, rain_collector: str
) -> None:
self._hass = hass
self._protocol = protocol
Expand Down Expand Up @@ -68,7 +65,7 @@ async def async_get_vantagepro2fromurl(self, url: str) -> VantagePro2 | None:
return vp

async def connect_to_station(self):
self._vantagepro2 = await self.async_get_vantagepro2fromurl(self.get_link()) # type: ignore
self._vantagepro2 = await self.async_get_vantagepro2fromurl(self.get_link()) # type: ignore

def get_current_data(
self,
Expand All @@ -78,30 +75,26 @@ def get_current_data(
archives = None
hilows = None

if not self._vantagepro2:
self.get_vantagepro2fromurl(self.get_link())

try:
self._vantagepro2.link.open()
data = self._vantagepro2.get_current_data() # type: ignore
data = self._vantagepro2.get_current_data()
except Exception as e:
self._vantagepro2.link.close()
raise e

#if self._hass.data.get(DATA_ARCHIVE_PERIOD) is None:
# _LOGGER.warning("Didn't get the archive period the first time, trying again")
# try:
# static_info = self.async_get_info()
# self._hass.data.setdefault(DATA_ARCHIVE_PERIOD, static_info.get('archive_period', None))
# except Exception as e:
# raise e

try:
hilows = self._vantagepro2.get_hilows() # type: ignore
hilows = self._vantagepro2.get_hilows()
except Exception:
pass

try:
end_datetime = datetime.now()
start_datetime = end_datetime - timedelta(minutes=(self._hass.data.get(DATA_ARCHIVE_PERIOD) * 2)) # type: ignore
archives = self._vantagepro2.get_archives(start_datetime, end_datetime) # type: ignore
start_datetime = end_datetime - \
timedelta(minutes=self._hass.data.get(DATA_ARCHIVE_PERIOD) * 2) # type: ignore
archives = self._vantagepro2.get_archives(start_datetime, end_datetime) # type: ignore
except Exception:
pass
finally:
Expand Down Expand Up @@ -143,8 +136,8 @@ async def async_get_current_data(self) -> LoopDataParserRevB | None:
data["LastError"] = "Couldn't acquire data, no data received"

except Exception as e:
_LOGGER.warning(f"Couldn't acquire data from {self.get_link()}: {e}")
data["LastError"] = f"Couldn't acquire data: {e}"
_LOGGER.error("Couldn't acquire data from %s: %s", self.get_link(), e)
data["LastError"] = f"Couldn't acquire data on {self.get_link()}: {e}"

if data["LastError"]:
data["LastErrorTime"] = now
Expand Down Expand Up @@ -216,7 +209,7 @@ def get_info(self) -> dict[str, Any] | None:
try:
self._vantagepro2.link.open()
firmware_version = self._vantagepro2.firmware_version # type: ignore
archive_period = self._vantagepro2.archive_period # type: ignore
archive_period = self._vantagepro2.archive_period # type: ignore
firmware_date = self._vantagepro2.firmware_date # type: ignore
diagnostics = self._vantagepro2.diagnostics # type: ignore
except Exception as e:
Expand All @@ -227,7 +220,7 @@ def get_info(self) -> dict[str, Any] | None:
"version": firmware_version,
"date": firmware_date,
"diagnostics": diagnostics,
"archive_period": archive_period
"archive_period": archive_period,
}

async def async_get_info(self) -> dict[str, Any] | None:
Expand All @@ -243,15 +236,12 @@ def get_static_info(self) -> dict[str, Any] | None:
try:
self._vantagepro2.link.open()
firmware_version = self._vantagepro2.firmware_version # type: ignore
archive_period = self._vantagepro2.archive_period # type: ignore
archive_period = self._vantagepro2.archive_period # type: ignore
except Exception as e:
raise e
finally:
self._vantagepro2.link.close()
return {
"version": firmware_version,
"archive_period": archive_period
}
return {"version": firmware_version, "archive_period": archive_period}

async def async_get_static_info(self) -> dict[str, Any] | None:
info = None
Expand Down Expand Up @@ -297,7 +287,7 @@ def convert_values(self, data: dict[str, Any]) -> None:
data["RainCollector"] = self._rain_collector
if data["RainCollector"] != RAIN_COLLECTOR_IMPERIAL:
self.correct_rain_values(data)
data['StormStartDate'] = self.strtodate(data['StormStartDate'])
data["StormStartDate"] = self.strtodate(data["StormStartDate"])

def correct_rain_values(self, data: dict[str, Any]):
if data["RainDay"] is not None:
Expand Down Expand Up @@ -330,8 +320,8 @@ def remove_incorrect_data(
for key in data.keys(): # type: ignore
info_key = re.sub(r"\d+$", "", key) # type: ignore
data_type = data_info.get(info_key, "")
raw_value = raw_data.get(info_key, 0) # type: ignore
if self.is_incorrect_value(raw_value, data_type): # type: ignore
raw_value = raw_data.get(info_key, 0) # type: ignore
if self.is_incorrect_value(raw_value, data_type): # type: ignore
data[key] = None # type: ignore

def is_incorrect_value(self, raw_value: int, data_type: str) -> bool:
Expand All @@ -353,33 +343,33 @@ def add_hilows(self, hilows: HighLowParserRevB | None, data: dict[str, Any]):
if not hilows:
return
data["TempOutHiDay"] = hilows["TempHiDay"]
data["TempOutHiTime"] = self.strtotime(hilows["TempHiTime"]) # type: ignore
data["TempOutHiTime"] = self.strtotime(hilows["TempHiTime"]) # type: ignore
data["TempOutLowDay"] = hilows["TempLoDay"]
data["TempOutLowTime"] = self.strtotime(hilows["TempLoTime"]) # type: ignore
data["TempOutLowTime"] = self.strtotime(hilows["TempLoTime"]) # type: ignore

data["DewPointHiDay"] = hilows["DewHiDay"]
data["DewPointHiTime"] = self.strtotime(hilows["DewHiTime"]) # type: ignore
data["DewPointHiTime"] = self.strtotime(hilows["DewHiTime"]) # type: ignore
data["DewPointLowDay"] = hilows["DewLoDay"]
data["DewPointLowTime"] = self.strtotime(hilows["DewLoTime"]) # type: ignore
data["DewPointLowTime"] = self.strtotime(hilows["DewLoTime"]) # type: ignore

data["RainRateDay"] = hilows["RainHiDay"]
data["RainRateTime"] = self.strtotime(hilows["RainHiTime"]) # type: ignore
data["RainRateTime"] = self.strtotime(hilows["RainHiTime"]) # type: ignore

data["BarometerHiDay"] = hilows["BaroHiDay"]
data["BarometerHiTime"] = self.strtotime(hilows["BaroHiTime"]) # type: ignore
data["BarometerHiTime"] = self.strtotime(hilows["BaroHiTime"]) # type: ignore
data["BarometerLowDay"] = hilows["BaroLoDay"]
data["BarometerLoTime"] = self.strtotime(hilows["BaroLoTime"]) # type: ignore
data["BarometerLoTime"] = self.strtotime(hilows["BaroLoTime"]) # type: ignore

data["SolarRadDay"] = hilows["SolarHiDay"]
data["SolarRadTime"] = self.strtotime(hilows["SolarHiTime"]) # type: ignore
data["SolarRadTime"] = self.strtotime(hilows["SolarHiTime"]) # type: ignore

data["UVDay"] = hilows["UVHiDay"]
data["UVTime"] = self.strtotime(hilows["UVHiTime"]) # type: ignore
data["UVTime"] = self.strtotime(hilows["UVHiTime"]) # type: ignore

data["WindGustDay"] = hilows["WindHiDay"]
data["WindGustTime"] = self.strtotime(hilows["WindHiTime"]) # type: ignore
data["WindGustTime"] = self.strtotime(hilows["WindHiTime"]) # type: ignore

def get_link(self) -> str | None:
def get_link(self) -> str:
"""Get device link for use with vproweather."""
if self._protocol == PROTOCOL_NETWORK:
return f"tcp:{self._link}"
Expand Down
Loading

0 comments on commit 8745c56

Please sign in to comment.