Skip to content

Commit

Permalink
chore: merge dev into master (#78)
Browse files Browse the repository at this point in the history
* chore(deps): library bump

* Update manifest.json

* feat: add adjustment sensor based on plan data (#62)

* feat: add adjustment sensor based on plan data

* fix hassfest validation error

* update requirements

* update hacs manifest

* update hacs.json

* fix: sensor name error

* feat: handle API rate limits gracefully (#66)

* build(deps): library bump (#68)

* fix: make all_rates an attribute of current_rate (#69)

* fix: make all_rates an attribute of current_rate

* adjust tests

* fix test sensor name

* allow coordinator to get all_rates sensor

* update mock_api

* fix: switch to async_forward_entry_setup (#70)
  • Loading branch information
firstof9 authored Nov 4, 2023
1 parent fd29a14 commit bd795e9
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 4 deletions.
7 changes: 7 additions & 0 deletions custom_components/openei/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ async def _async_update_data(self) -> dict:
self._data = await self.hass.async_add_executor_job(
get_sensors, self.hass, self._config
)
except openeihttp.RateLimit:
_LOGGER.error("API Rate limit exceded, retrying later.")
self._data = {}
except Exception as exception:
raise UpdateFailed() from exception
return self._data
Expand All @@ -120,11 +123,15 @@ async def _async_refresh_data(self, data=None) -> None:
self._data = await self.hass.async_add_executor_job(
get_sensors, self.hass, self._config
)
except openeihttp.RateLimit:
_LOGGER.error("API Rate limit exceded, retrying later.")
self._data = {}
except Exception as exception:
raise UpdateFailed() from exception


def get_sensors(hass, config) -> dict:
"""Update sensor data."""
api = config.data.get(CONF_API_KEY)
plan = config.data.get(CONF_PLAN)
meter = config.data.get(CONF_SENSOR)
Expand Down
6 changes: 5 additions & 1 deletion custom_components/openei/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
name="Current Energy Rate",
icon="mdi:cash-multiple",
),
"current_adjustment": SensorEntityDescription(
key="current_adjustment",
name="Current Energy Adjustment",
icon="mdi:cash-multiple",
),
"distributed_generation": SensorEntityDescription(
key="distributed_generation",
name="Distributed Generation",
Expand All @@ -69,7 +74,6 @@
key="all_rates",
name="All Listed Rates",
icon="mdi:format-list-bulleted",
entity_category=EntityCategory.DIAGNOSTIC,
),
"monthly_tier_rate": SensorEntityDescription(
key="monthly_tier_rate",
Expand Down
6 changes: 5 additions & 1 deletion custom_components/openei/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ async def async_setup_entry(hass, entry, async_add_devices):

sensors = []
for sensor in SENSOR_TYPES:
if sensor == "all_rates":
continue
sensors.append(OpenEISensor(hass, SENSOR_TYPES[sensor], entry, coordinator))

async_add_devices(sensors, False)
Expand Down Expand Up @@ -56,7 +58,7 @@ def native_value(self) -> Any:
@property
def native_unit_of_measurement(self) -> Any:
"""Return the unit of measurement."""
if self._key in ["current_rate", "monthly_tier_rate"]:
if self._key in ["current_adjustment","current_rate", "monthly_tier_rate"]:
return f"{self.hass.config.currency}/kWh"
if f"{self._key}_uom" in self.coordinator.data:
return self.coordinator.data.get(f"{self._key}_uom")
Expand All @@ -72,6 +74,8 @@ def extra_state_attributes(self) -> Optional[dict]:
"""Return sesnsor attributes."""
attrs = {}
attrs[ATTR_ATTRIBUTION] = ATTRIBUTION
if self._key == "current_rate":
attrs["all_rates"] = self.coordinator.data.get("all_rates")
return attrs

@property
Expand Down
4 changes: 2 additions & 2 deletions requirements_tests.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-r requirements_dev.txt
python-openei
python-openei==0.1.22
pytest
pytest-cov
pytest-homeassistant-custom-component
pytest-homeassistant-custom-component
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Test configurations."""
from unittest.mock import patch
import openeihttp

import pytest

Expand All @@ -25,6 +26,7 @@ def mock_api():
mock_api.return_value.lookup_plans = (
'"Fake Utility Co": [{"name": "Fake Plan Name", "label": "randomstring"}]'
)
mock_api.return_value.all_rates = [ 0.24477, 0.007 ]

yield mock_api

Expand Down Expand Up @@ -52,5 +54,13 @@ def mock_get_sensors():
"rate_name": "Fake Test Rate",
"mincharge": 10,
"mincharge_uom": "$/month",
"all_rates": [ 0.24477, 0.007 ],
}
yield mock_sensors

@pytest.fixture(name="mock_sensors_err")
def mock_sensors_api_error():
"""Mock of get sensors function."""
with patch("custom_components.openei.get_sensors") as mock_sensors:
mock_sensors.side_effect = openeihttp.RateLimit("Error")
yield mock_sensors
2 changes: 2 additions & 0 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from custom_components.openei.const import DOMAIN

pytestmark = pytest.mark.asyncio


@pytest.mark.parametrize(
"input_1,step_id_2,input_2,step_id_3,input_3,title,data",
Expand Down
16 changes: 16 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
from openeihttp import APIError
from pytest_homeassistant_custom_component.common import MockConfigEntry

from openeihttp import RateLimit

from custom_components.openei.const import DOMAIN
from tests.const import CONFIG_DATA, CONFIG_DATA_MISSING_PLAN, CONFIG_DATA_WITH_SENSOR

pytestmark = pytest.mark.asyncio

async def test_setup_entry(hass, mock_sensors, mock_api):
"""Test settting up entities."""
Expand Down Expand Up @@ -102,3 +105,16 @@ async def test_setup_entry_sensor_plan_error(hass, mock_api, caplog):
assert not await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert "Plan configuration missing." in caplog.text

async def test_rate_limit_error(hass, mock_sensors_err, caplog):
"""Test settting up entities."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Fake Utility Co",
data=CONFIG_DATA,
)

entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert "API Rate limit exceded, retrying later." in caplog.text
9 changes: 9 additions & 0 deletions tests/test_sensors.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"""Tests for sensors."""
import pytest

from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.openei.const import DOMAIN
from tests.const import CONFIG_DATA

FAKE_MINCHARGE_SENSOR = "sensor.fake_utility_co_minimum_charge"
FAKE_CURRENT_RATE_SENSOR = "sensor.fake_utility_co_current_energy_rate"

pytestmark = pytest.mark.asyncio


async def test_sensors(hass, mock_sensors, mock_api):
Expand All @@ -24,3 +28,8 @@ async def test_sensors(hass, mock_sensors, mock_api):
assert state is not None
assert state.state == "10"
assert state.attributes["unit_of_measurement"] == "$/month"

state = hass.states.get(FAKE_CURRENT_RATE_SENSOR)
assert state is not None
assert state.state == "0.24477"
assert state.attributes["all_rates"] == [ 0.24477, 0.007 ]

0 comments on commit bd795e9

Please sign in to comment.