Skip to content

Commit

Permalink
Merge pull request #38 from astrandb/ApiV2
Browse files Browse the repository at this point in the history
ApiV2 - and many improvements
  • Loading branch information
astrandb committed Sep 17, 2023
2 parents 70c7005 + 9a7c60a commit 408517d
Show file tree
Hide file tree
Showing 10 changed files with 800 additions and 136 deletions.
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

0 comments on commit 408517d

Please sign in to comment.