Skip to content

Commit

Permalink
feat: add mincharge sensor (#47)
Browse files Browse the repository at this point in the history
* feat: add mincharge sensor

* adjust tests
  • Loading branch information
firstof9 authored Sep 18, 2021
1 parent 8efc280 commit a42e177
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 34 deletions.
7 changes: 6 additions & 1 deletion custom_components/openei/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ def get_sensors(hass, config) -> dict:

for sensor in SENSOR_TYPES:
_sensor = {}
_sensor[sensor] = getattr(rate, sensor)
value = getattr(rate, sensor)
if isinstance(value, tuple):
_sensor[sensor] = value[0]
_sensor[f"{sensor}_uom"] = value[1]
else:
_sensor[sensor] = getattr(rate, sensor)
data.update(_sensor)

for sensor in BINARY_SENSORS:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/openei/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ async def _get_plan_list(hass, user_input) -> list | None:

def _lookup_plans(handler) -> list:
"""Return list of utilities and plans."""
response = handler.lookup_plans()
response = handler.lookup_plans
response["Not Listed"] = [{"name": "Not Listed", "label": "Not Listed"}]
_LOGGER.debug("lookup_plans: %s", response)
return response
Expand Down
5 changes: 3 additions & 2 deletions custom_components/openei/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
SENSOR_TYPES = {
"current_rate": [
"Current Energy Rate",
"mdi:currency-usd",
"mdi:cash-multiple",
None,
DEVICE_CLASS_MONETARY,
],
Expand All @@ -51,10 +51,11 @@
"all_rates": ["All Listed Rates", "mdi:format-list-bulleted", None, None],
"monthly_tier_rate": [
"Monthly Energy Rate",
"mdi:currency-usd",
"mdi:cash-multiple",
None,
DEVICE_CLASS_MONETARY,
],
"mincharge": ["Minimum Charge", "mdi:cash-multiple", None, DEVICE_CLASS_MONETARY],
}

BINARY_SENSORS = {
Expand Down
2 changes: 1 addition & 1 deletion custom_components/openei/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"iot_class": "cloud_polling",
"config_flow": true,
"codeowners": ["@firstof9"],
"requirements": ["python-openei==0.1.14"],
"requirements": ["python-openei==0.1.15"],
"version": "0.1.5"
}
15 changes: 9 additions & 6 deletions custom_components/openei/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,7 @@ def __init__(self, hass, sensor_type, entry, coordinator) -> None:
self._unique_id = entry.entry_id
self._config = entry
self.coordinator = coordinator
self._attr_native_unit_of_measurement = (
f"{self.hass.config.currency}/kWh"
if self._name in ["current_rate", "monthly_tier_rate"]
else None
)
self._device_class = SENSOR_TYPES[self._name][3]
self._attr_native_value = self.coordinator.data.get(self._name)

@property
def unique_id(self) -> str:
Expand All @@ -60,6 +54,15 @@ def native_value(self) -> Any:
"""Return the value of the sensor."""
return self.coordinator.data.get(self._name)

@property
def native_unit_of_measurement(self) -> Any:
"""Return the unit of measurement."""
if self._name in ["current_rate", "monthly_tier_rate"]:
return f"{self.hass.config.currency}/kWh"
if f"{self._name}_uom" in self.coordinator.data:
return self.coordinator.data.get(f"{self._name}_uom")
return None

@property
def available(self) -> bool:
"""Return if entity is available."""
Expand Down
34 changes: 25 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,30 @@ def auto_enable_custom_integrations(enable_custom_integrations):
@pytest.fixture(name="mock_api")
def mock_api():
"""Mock the library calls."""
with patch("custom_components.openei.openeihttp"), patch(
"custom_components.openei.config_flow.openeihttp"
):
mock_conn = mock.Mock(spec=openeihttp.Rates)
mock_conn.return_value.current_rate.return_value = 0.24477
mock_conn.return_value.distributed_generation.return_value = "Net Metering"
mock_conn.return_value.approval.return_value = True
mock_conn.return_value.rate_name.return_value = 0.24477
with patch("custom_components.openei.openeihttp.Rates") as mock_api:
# mock_api = mock.Mock(spec=openeihttp.Rates)
mock_api.return_value.current_rate = 0.24477
mock_api.return_value.distributed_generation = "Net Metering"
mock_api.return_value.approval = True
mock_api.return_value.rate_name = 0.24477
mock_api.return_value.mincharge = (10, "$/month")
mock_api.return_value.lookup_plans = (
'"Fake Utility Co": [{"name": "Fake Plan Name", "label": "randomstring"}]'
)

yield mock_api


@pytest.fixture(name="mock_api_config")
def mock_api_config():
"""Mock the library calls."""
with patch("custom_components.openei.config_flow.openeihttp.Rates") as mock_api:
# mock_api = mock.Mock()
mock_api.return_value.lookup_plans = {
"Fake Utility Co": [{"name": "Fake Plan Name", "label": "randomstring"}]
}

yield mock_conn
yield mock_api


@pytest.fixture(name="mock_sensors")
Expand All @@ -37,5 +51,7 @@ def mock_get_sensors():
"distributed_generation": "Net Metering",
"approval": True,
"rate_name": "Fake Test Rate",
"mincharge": 10,
"mincharge_uom": "$/month",
}
yield mock_sensors
10 changes: 9 additions & 1 deletion tests/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
CONFIG_DATA = {
"api_key": "fakeAPIKey",
"utility": "Fake Utility Co.",
"utility": "Fake Utility Co",
"rate_plan": "totallyfakerateplan",
"manual_plan": "manualfakerateplan",
}

CONFIG_DATA_WITH_SENSOR = {
"api_key": "fakeAPIKey",
"utility": "Fake Utility Co",
"rate_plan": "totallyfakerateplan",
"manual_plan": "manualfakerateplan",
"sensor": "sensor.fakesensor",
}
7 changes: 1 addition & 6 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ async def test_options_flow(
title,
data,
hass,
mock_api,
mock_api_config,
):
"""Test config flow options."""
entry = MockConfigEntry(
Expand Down Expand Up @@ -173,11 +173,6 @@ async def test_options_flow(
with patch("custom_components.openei.async_setup", return_value=True), patch(
"custom_components.openei.async_setup_entry",
return_value=True,
), patch(
"custom_components.openei.config_flow._lookup_plans",
return_value={
"Fake Utility Co": [{"name": "Fake Plan Name", "label": "randomstring"}]
},
), patch(
"custom_components.openei.config_flow._get_entities",
return_value=["(none)"],
Expand Down
30 changes: 23 additions & 7 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@
from pytest_homeassistant_custom_component.common import MockConfigEntry

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


async def test_setup_entry(hass, mock_sensors, mock_api):
"""Test settting up entities."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Fake Utility Co.",
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 len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 5
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 6
assert len(hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN)) == 1
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
Expand All @@ -32,22 +32,22 @@ async def test_unload_entry(hass, mock_sensors, mock_api):
"""Test unloading entities."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Fake Utility Co.",
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 len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 5
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 6
assert len(hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN)) == 1
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1

assert await hass.config_entries.async_unload(entries[0].entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 5
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 6
assert len(hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN)) == 1
assert len(hass.states.async_entity_ids(DOMAIN)) == 0

Expand All @@ -60,7 +60,7 @@ async def test_setup_api_error(hass):
"""Test settting up entities."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Fake Utility Co.",
title="Fake Utility Co",
data=CONFIG_DATA,
)

Expand All @@ -71,3 +71,19 @@ async def test_setup_api_error(hass):

assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert not hass.data.get(DOMAIN)


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

entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

assert "Using meter data from sensor: sensor.fakesensor" in caplog.text
assert "Sensor: sensor.fakesensor is not valid." in caplog.text
26 changes: 26 additions & 0 deletions tests/test_sensors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Tests for sensors."""

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"


async def test_sensors(hass, mock_sensors, mock_api):
"""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()

state = hass.states.get(FAKE_MINCHARGE_SENSOR)
assert state is not None
assert state.state == "10"
assert state.attributes["unit_of_measurement"] == "$/month"

0 comments on commit a42e177

Please sign in to comment.