Add sensor platform for Aurora integration (#43148)

* Removed pylint disable on unused after updating CI files that were out of date.
* Pylint still fails due to bug on DOMAIN import. Added disable check.
* Addressed PR comments
* Added import for ClientError to test_config_flow.py

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
pull/46168/head
djtimca 2021-02-06 02:05:39 -05:00 committed by GitHub
parent ce159d7db3
commit f2d9e6f70c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 119 additions and 61 deletions

View File

@ -72,6 +72,7 @@ omit =
homeassistant/components/aurora/__init__.py homeassistant/components/aurora/__init__.py
homeassistant/components/aurora/binary_sensor.py homeassistant/components/aurora/binary_sensor.py
homeassistant/components/aurora/const.py homeassistant/components/aurora/const.py
homeassistant/components/aurora/sensor.py
homeassistant/components/aurora_abb_powerone/sensor.py homeassistant/components/aurora_abb_powerone/sensor.py
homeassistant/components/avea/light.py homeassistant/components/avea/light.py
homeassistant/components/avion/light.py homeassistant/components/avion/light.py

View File

@ -4,16 +4,26 @@ import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
from aiohttp import ClientError
from auroranoaa import AuroraForecast from auroranoaa import AuroraForecast
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from homeassistant.const import ATTR_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
UpdateFailed,
)
from .const import ( from .const import (
ATTR_ENTRY_TYPE,
ATTR_IDENTIFIERS,
ATTR_MANUFACTURER,
ATTR_MODEL,
ATTRIBUTION,
AURORA_API, AURORA_API,
CONF_THRESHOLD, CONF_THRESHOLD,
COORDINATOR, COORDINATOR,
@ -24,7 +34,7 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PLATFORMS = ["binary_sensor"] PLATFORMS = ["binary_sensor", "sensor"]
async def async_setup(hass: HomeAssistant, config: dict): async def async_setup(hass: HomeAssistant, config: dict):
@ -126,5 +136,54 @@ class AuroraDataUpdateCoordinator(DataUpdateCoordinator):
try: try:
return await self.api.get_forecast_data(self.longitude, self.latitude) return await self.api.get_forecast_data(self.longitude, self.latitude)
except ConnectionError as error: except ClientError as error:
raise UpdateFailed(f"Error updating from NOAA: {error}") from error raise UpdateFailed(f"Error updating from NOAA: {error}") from error
class AuroraEntity(CoordinatorEntity):
"""Implementation of the base Aurora Entity."""
def __init__(
self,
coordinator: AuroraDataUpdateCoordinator,
name: str,
icon: str,
):
"""Initialize the Aurora Entity."""
super().__init__(coordinator=coordinator)
self._name = name
self._unique_id = f"{self.coordinator.latitude}_{self.coordinator.longitude}"
self._icon = icon
@property
def unique_id(self):
"""Define the unique id based on the latitude and longitude."""
return self._unique_id
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {"attribution": ATTRIBUTION}
@property
def icon(self):
"""Return the icon for the sensor."""
return self._icon
@property
def device_info(self):
"""Define the device based on name."""
return {
ATTR_IDENTIFIERS: {(DOMAIN, self._unique_id)},
ATTR_NAME: self.coordinator.name,
ATTR_MANUFACTURER: "NOAA",
ATTR_MODEL: "Aurora Visibility Sensor",
ATTR_ENTRY_TYPE: "service",
}

View File

@ -1,19 +1,10 @@
"""Support for aurora forecast data sensor.""" """Support for Aurora Forecast binary sensor."""
import logging import logging
from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.const import ATTR_NAME
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AuroraDataUpdateCoordinator from . import AuroraEntity
from .const import ( from .const import COORDINATOR, DOMAIN
ATTR_IDENTIFIERS,
ATTR_MANUFACTURER,
ATTR_MODEL,
ATTRIBUTION,
COORDINATOR,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -21,55 +12,17 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, entry, async_add_entries): async def async_setup_entry(hass, entry, async_add_entries):
"""Set up the binary_sensor platform.""" """Set up the binary_sensor platform."""
coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR] coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
name = coordinator.name name = f"{coordinator.name} Aurora Visibility Alert"
entity = AuroraSensor(coordinator, name) entity = AuroraSensor(coordinator=coordinator, name=name, icon="mdi:hazard-lights")
async_add_entries([entity]) async_add_entries([entity])
class AuroraSensor(CoordinatorEntity, BinarySensorEntity): class AuroraSensor(AuroraEntity, BinarySensorEntity):
"""Implementation of an aurora sensor.""" """Implementation of an aurora sensor."""
def __init__(self, coordinator: AuroraDataUpdateCoordinator, name):
"""Define the binary sensor for the Aurora integration."""
super().__init__(coordinator=coordinator)
self._name = name
self.coordinator = coordinator
self._unique_id = f"{self.coordinator.latitude}_{self.coordinator.longitude}"
@property
def unique_id(self):
"""Define the unique id based on the latitude and longitude."""
return self._unique_id
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property @property
def is_on(self): def is_on(self):
"""Return true if aurora is visible.""" """Return true if aurora is visible."""
return self.coordinator.data > self.coordinator.threshold return self.coordinator.data > self.coordinator.threshold
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {"attribution": ATTRIBUTION}
@property
def icon(self):
"""Return the icon for the sensor."""
return "mdi:hazard-lights"
@property
def device_info(self):
"""Define the device based on name."""
return {
ATTR_IDENTIFIERS: {(DOMAIN, self._unique_id)},
ATTR_NAME: self.coordinator.name,
ATTR_MANUFACTURER: "NOAA",
ATTR_MODEL: "Aurora Visibility Sensor",
}

View File

@ -1,6 +1,7 @@
"""Config flow for SpaceX Launches and Starman.""" """Config flow for SpaceX Launches and Starman."""
import logging import logging
from aiohttp import ClientError
from auroranoaa import AuroraForecast from auroranoaa import AuroraForecast
import voluptuous as vol import voluptuous as vol
@ -9,7 +10,12 @@ from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client from homeassistant.helpers import aiohttp_client
from .const import CONF_THRESHOLD, DEFAULT_NAME, DEFAULT_THRESHOLD, DOMAIN from .const import ( # pylint: disable=unused-import
CONF_THRESHOLD,
DEFAULT_NAME,
DEFAULT_THRESHOLD,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -40,14 +46,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
try: try:
await api.get_forecast_data(longitude, latitude) await api.get_forecast_data(longitude, latitude)
except ConnectionError: except ClientError:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception") _LOGGER.exception("Unexpected exception")
errors["base"] = "unknown" errors["base"] = "unknown"
else: else:
await self.async_set_unique_id( await self.async_set_unique_id(
f"{DOMAIN}_{user_input[CONF_LONGITUDE]}_{user_input[CONF_LATITUDE]}" f"{user_input[CONF_LONGITUDE]}_{user_input[CONF_LATITUDE]}"
) )
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
return self.async_create_entry( return self.async_create_entry(

View File

@ -6,6 +6,7 @@ AURORA_API = "aurora_api"
ATTR_IDENTIFIERS = "identifiers" ATTR_IDENTIFIERS = "identifiers"
ATTR_MANUFACTURER = "manufacturer" ATTR_MANUFACTURER = "manufacturer"
ATTR_MODEL = "model" ATTR_MODEL = "model"
ATTR_ENTRY_TYPE = "entry_type"
DEFAULT_POLLING_INTERVAL = 5 DEFAULT_POLLING_INTERVAL = 5
CONF_THRESHOLD = "forecast_threshold" CONF_THRESHOLD = "forecast_threshold"
DEFAULT_THRESHOLD = 75 DEFAULT_THRESHOLD = 75

View File

@ -0,0 +1,36 @@
"""Support for Aurora Forecast sensor."""
import logging
from homeassistant.const import PERCENTAGE
from . import AuroraEntity
from .const import COORDINATOR, DOMAIN
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, entry, async_add_entries):
"""Set up the sensor platform."""
coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
entity = AuroraSensor(
coordinator=coordinator,
name=f"{coordinator.name} Aurora Visibility %",
icon="mdi:gauge",
)
async_add_entries([entity])
class AuroraSensor(AuroraEntity):
"""Implementation of an aurora sensor."""
@property
def state(self):
"""Return % chance the aurora is visible."""
return self.coordinator.data
@property
def unit_of_measurement(self):
"""Return the unit of measure."""
return PERCENTAGE

View File

@ -2,6 +2,8 @@
from unittest.mock import patch from unittest.mock import patch
from aiohttp import ClientError
from homeassistant import config_entries, data_entry_flow, setup from homeassistant import config_entries, data_entry_flow, setup
from homeassistant.components.aurora.const import DOMAIN from homeassistant.components.aurora.const import DOMAIN
@ -55,7 +57,7 @@ async def test_form_cannot_connect(hass):
with patch( with patch(
"homeassistant.components.aurora.AuroraForecast.get_forecast_data", "homeassistant.components.aurora.AuroraForecast.get_forecast_data",
side_effect=ConnectionError, side_effect=ClientError,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],