Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ApiV2 - and many improvements #38

Merged
merged 2 commits into from
Sep 17, 2023
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
145 changes: 134 additions & 11 deletions custom_components/weatherlink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN
from .pyweatherlink import WLHub

PLATFORMS = [Platform.SENSOR]
from .config_flow import API_V1, API_V2
from .const import (
CONF_API_KEY_V2,
CONF_API_SECRET,
CONF_API_TOKEN,
CONF_API_VERSION,
CONF_STATION_ID,
DOMAIN,
)
from .pyweatherlink import WLHub, WLHubV2

PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]

_LOGGER = logging.getLogger(__name__)

Expand All @@ -26,12 +34,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {}
hass.data[DOMAIN][entry.entry_id]["api"] = WLHub(
websession=async_get_clientsession(hass),
username=entry.data["username"],
password=entry.data["password"],
apitoken=entry.data["apitoken"],
)
if entry.data[CONF_API_VERSION] == API_V1:
hass.data[DOMAIN][entry.entry_id]["api"] = WLHub(
websession=async_get_clientsession(hass),
username=entry.data["username"],
password=entry.data["password"],
apitoken=entry.data[CONF_API_TOKEN],
)

if entry.data[CONF_API_VERSION] == API_V2:
hass.data[DOMAIN][entry.entry_id]["api"] = WLHubV2(
websession=async_get_clientsession(hass),
station_id=entry.data[CONF_STATION_ID],
api_key_v2=entry.data[CONF_API_KEY_V2],
api_secret=entry.data[CONF_API_SECRET],
)
hass.data[DOMAIN][entry.entry_id]["station_data"] = await hass.data[DOMAIN][
entry.entry_id
]["api"].get_station()

coordinator = await get_coordinator(hass, entry)
if not coordinator.last_update_success:
Expand Down Expand Up @@ -59,12 +79,98 @@ async def get_coordinator(
if "coordinator" in hass.data[DOMAIN][entry.entry_id]:
return hass.data[DOMAIN][entry.entry_id]["coordinator"]

def _preprocess(indata: str):
outdata = {}
# _LOGGER.debug("Received data: %s", indata)
if entry.data[CONF_API_VERSION] == API_V1:
outdata["DID"] = indata["davis_current_observation"].get("DID")
outdata["station_name"] = indata["davis_current_observation"].get(
"station_name"
)
outdata["temp_out"] = indata.get("temp_f")
outdata["temp_in"] = indata["davis_current_observation"].get("temp_in_f")
outdata["hum_in"] = indata["davis_current_observation"].get(
"relative_humidity_in"
)
outdata["hum_out"] = indata.get("relative_humidity")
outdata["bar_sea_level"] = indata.get("pressure_in")
outdata["wind_mph"] = indata.get("wind_mph")
outdata["wind_dir"] = indata.get("wind_degrees")
outdata["dewpoint"] = indata.get("dewpoint_f")
outdata["rain_day"] = indata["davis_current_observation"].get("rain_day_in")
outdata["rain_rate"] = indata["davis_current_observation"].get(
"rain_rate_in_per_hr"
)
outdata["rain_month"] = indata["davis_current_observation"].get(
"rain_month_in"
)
outdata["rain_year"] = indata["davis_current_observation"].get(
"rain_year_in"
)
if entry.data[CONF_API_VERSION] == API_V2:
outdata["station_id_uuid"] = indata["station_id_uuid"]
for sensor in indata["sensors"]:
if sensor["sensor_type"] == 37 and sensor["data_structure_type"] == 10:
outdata["temp_out"] = sensor["data"][0]["temp"]
outdata["hum_out"] = sensor["data"][0]["hum"]
outdata["wind_mph"] = sensor["data"][0]["wind_speed_last"]
outdata["wind_dir"] = sensor["data"][0]["wind_dir_last"]
outdata["dewpoint"] = sensor["data"][0]["dew_point"]
outdata["rain_day"] = float(sensor["data"][0]["rainfall_daily_in"])
outdata["rain_rate"] = sensor["data"][0]["rain_rate_last_in"]
outdata["rain_month"] = sensor["data"][0]["rainfall_monthly_in"]
outdata["rain_year"] = sensor["data"][0]["rainfall_year_in"]
outdata["trans_battery_flag"] = sensor["data"][0][
"trans_battery_flag"
]
if sensor["sensor_type"] == 37 and sensor["data_structure_type"] == 2:
outdata["temp_out"] = sensor["data"][0]["temp_out"]
outdata["temp_in"] = sensor["data"][0]["temp_in"]
outdata["bar_sea_level"] = sensor["data"][0]["bar"]
outdata["hum_out"] = sensor["data"][0]["hum_out"]
outdata["wind_mph"] = sensor["data"][0]["wind_speed"]
outdata["wind_dir"] = sensor["data"][0]["wind_dir"]
outdata["dewpoint"] = sensor["data"][0]["dew_point"]
outdata["rain_day"] = float(sensor["data"][0]["rain_day_in"])
outdata["rain_rate"] = sensor["data"][0]["rain_rate_in"]
outdata["rain_month"] = sensor["data"][0]["rain_month_in"]
outdata["rain_year"] = sensor["data"][0]["rain_year_in"]
if sensor["sensor_type"] == 37 and sensor["data_structure_type"] == 23:
outdata["temp_out"] = sensor["data"][0]["temp"]
outdata["hum_out"] = sensor["data"][0]["hum"]
outdata["wind_mph"] = sensor["data"][0]["wind_speed_last"]
outdata["wind_dir"] = sensor["data"][0]["wind_dir_last"]
outdata["dewpoint"] = sensor["data"][0]["dew_point"]
outdata["rain_day"] = float(sensor["data"][0]["rainfall_day_in"])
outdata["rain_rate"] = sensor["data"][0]["rain_rate_last_in"]
outdata["rain_month"] = sensor["data"][0]["rainfall_month_in"]
outdata["rain_year"] = sensor["data"][0]["rainfall_year_in"]
outdata["trans_battery_flag"] = sensor["data"][0][
"trans_battery_flag"
]
if sensor["sensor_type"] == 365 and sensor["data_structure_type"] == 21:
outdata["temp_in"] = sensor["data"][0]["temp_in"]
outdata["hum_in"] = sensor["data"][0]["hum_in"]
if sensor["sensor_type"] == 243 and sensor["data_structure_type"] == 12:
outdata["temp_in"] = sensor["data"][0]["temp_in"]
outdata["hum_in"] = sensor["data"][0]["hum_in"]
if sensor["sensor_type"] == 242 and sensor["data_structure_type"] == 12:
outdata["bar_sea_level"] = sensor["data"][0]["bar_sea_level"]
outdata["bar_trend"] = sensor["data"][0]["bar_trend"]
if sensor["sensor_type"] == 242 and sensor["data_structure_type"] == 19:
outdata["bar_sea_level"] = sensor["data"][0]["bar_sea_level"]
outdata["bar_trend"] = sensor["data"][0]["bar_trend"]

return outdata

async def async_fetch():
api = hass.data[DOMAIN][entry.entry_id]["api"]
try:
async with async_timeout.timeout(10):
res = await api.request("GET")
return await res.json()
json_data = await res.json()
hass.data[DOMAIN][entry.entry_id]["current"] = json_data
return _preprocess(json_data)
except ClientResponseError as exc:
_LOGGER.warning("API fetch failed. Status: %s, - %s", exc.code, exc.message)
raise UpdateFailed(exc) from exc
Expand All @@ -78,3 +184,20 @@ async def async_fetch():
)
await hass.data[DOMAIN][entry.entry_id]["coordinator"].async_refresh()
return hass.data[DOMAIN][entry.entry_id]["coordinator"]


async def async_migrate_entry(hass, config_entry: ConfigEntry):
"""Migrate old entry."""
_LOGGER.info("Migrating from version %s", config_entry.version)

if config_entry.version == 1:
new_data = {**config_entry.data}

new_data[CONF_API_VERSION] = API_V1

config_entry.version = 2
hass.config_entries.async_update_entry(config_entry, data=new_data)

_LOGGER.info("Migration to version %s successful", config_entry.version)

return True
127 changes: 127 additions & 0 deletions custom_components/weatherlink/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"""Platform for binary sensor integration."""
from __future__ import annotations

from dataclasses import dataclass
import logging
from typing import Final

from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import get_coordinator
from .config_flow import API_V1, API_V2
from .const import CONF_API_VERSION, DOMAIN
from .pyweatherlink import WLData

_LOGGER = logging.getLogger(__name__)


@dataclass
class WLBinarySensorDescription(BinarySensorEntityDescription):
"""Class describing Weatherlink binarysensor entities."""

tag: str | None = None
exclude: set = ()


SENSOR_TYPES: Final[tuple[WLBinarySensorDescription, ...]] = (
WLBinarySensorDescription(
key="TransmitterBattery",
tag="trans_battery_flag",
device_class=BinarySensorDeviceClass.BATTERY,
translation_key="trans_battery",
entity_category=EntityCategory.DIAGNOSTIC,
exclude=(API_V1,),
),
)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the binary sensor platform."""
coordinator = await get_coordinator(hass, config_entry)

async_add_entities(
WLSensor(coordinator, hass, config_entry, description)
for description in SENSOR_TYPES
if config_entry.data[CONF_API_VERSION] not in description.exclude
)


class WLSensor(CoordinatorEntity, BinarySensorEntity):
"""Representation of a Binary Sensor."""

entity_description: WLBinarySensorDescription
sensor_data = WLData()

def __init__(
self,
coordinator,
hass: HomeAssistant,
entry: ConfigEntry,
description: WLBinarySensorDescription,
):
"""Initialize the sensor."""
super().__init__(coordinator)
self.hass = hass
self.entry: ConfigEntry = entry
self.entity_description = description
self._attr_has_entity_name = True
self._attr_unique_id = (
f"{self.get_unique_id_base()}-{self.entity_description.key}"
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self.get_unique_id_base())},
name=self.generate_name(),
manufacturer="Davis",
model=self.generate_model(),
configuration_url="https://www.weatherlink.com/",
)

def get_unique_id_base(self):
"""Generate base for unique_id."""
unique_base = None
if self.entry.data[CONF_API_VERSION] == API_V1:
unique_base = self.coordinator.data["DID"]
if self.entry.data[CONF_API_VERSION] == API_V2:
unique_base = self.coordinator.data["station_id_uuid"]
return unique_base

def generate_name(self):
"""Generate device name."""
if self.entry.data[CONF_API_VERSION] == API_V1:
return self.coordinator.data["station_name"]
if self.entry.data[CONF_API_VERSION] == API_V2:
return self.hass.data[DOMAIN][self.entry.entry_id]["station_data"][
"stations"
][0]["station_name"]

return "Unknown devicename"

def generate_model(self):
"""Generate model string."""
if self.entry.data[CONF_API_VERSION] == API_V1:
return "Weatherlink - API V1"
if self.entry.data[CONF_API_VERSION] == API_V2:
model = self.hass.data[DOMAIN][self.entry.entry_id]["station_data"][
"stations"
][0].get("product_number")
return f"Weatherlink {model}"
return "Weatherlink"

@property
def is_on(self):
"""Return the state of the sensor."""
# _LOGGER.debug("Key: %s", self.entity_description.key)
return self.coordinator.data.get(self.entity_description.tag)
Loading