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

Support price integration with prices in one attribute. #376

Merged
merged 2 commits into from
Jan 12, 2025
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
14 changes: 14 additions & 0 deletions custom_components/ev_smart_charging/helpers/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,20 @@ def to_local(self):
item["end"] = dt.as_local(item["end"])
return self

def today(self):
"""Only keep today's data"""
today = dt.now().date()
self.data = [item for item in self.data if item["start"].date() == today]
self.valid = len(self.data) > 12
return self

def tomorrow(self):
"""Only keep tomorrow's data"""
tomorrow = dt.now().date() + timedelta(days=1)
self.data = [item for item in self.data if item["start"].date() == tomorrow]
self.valid = len(self.data) > 12
return self


def get_lowest_hours(
start_hour: datetime,
Expand Down
28 changes: 24 additions & 4 deletions custom_components/ev_smart_charging/helpers/price_adaptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,36 @@ def initiate(self, price_state: State) -> bool:
# This should only happen when testing
return True

# First try for the attributes used by integration using
# separate attributes for today and tomorrow.
if "prices_today" in price_state.attributes:
self._price_attribute_today = "prices_today"
self._price_attribute_tomorrow = "prices_tomorrow"
elif "raw_today" in price_state.attributes:
self._price_attribute_today = "raw_today"
self._price_attribute_tomorrow = "raw_tomorrow"

# Then try for the attributes used by integration using
# the same attribute for today and tomorrow.
else:
self._price_attribute_today = next(
(
attr
for attr in ["prices", "data", "forecast"]
if attr in price_state.attributes
),
None,
)
self._price_attribute_tomorrow = self._price_attribute_today

if not self._price_attribute_today:
return False

# Set _price_key.start and _price_key.value
try:
keys = price_state.attributes[self._price_attribute_today][0]
start_keys = ["time", "start", "hour"]
value_keys = ["price", "value"]
start_keys = ["time", "start", "hour", "start_time", "datetime"]
value_keys = ["price", "value", "price_ct_per_kwh", "electricity_price"]

self._price_format.start = next(
(key for key in start_keys if key in keys), None
Expand Down Expand Up @@ -120,11 +136,15 @@ def is_price_state(self, price_state: State) -> bool:

def get_raw_today_local(self, state) -> Raw:
"""Get the today's prices in local timezone"""
return Raw(state.attributes[self._price_attribute_today], self._price_format)
return Raw(
state.attributes[self._price_attribute_today], self._price_format
).today()

def get_raw_tomorrow_local(self, state) -> Raw:
"""Get the tomorrow's prices in local timezone"""
return Raw(state.attributes[self._price_attribute_tomorrow], self._price_format)
return Raw(
state.attributes[self._price_attribute_tomorrow], self._price_format
).tomorrow()

def get_current_price(self, state) -> float:
"""Return current price."""
Expand Down
14 changes: 13 additions & 1 deletion tests/helpers/test_coordinator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Test ev_smart_charging/helpers/coordinator.py"""

from datetime import datetime

from homeassistant.util import dt as dt_util
Expand Down Expand Up @@ -42,16 +43,20 @@


# pylint: disable=unused-argument
async def test_raw(hass, set_cet_timezone):
async def test_raw(hass, set_cet_timezone, freezer):
"""Test Raw"""

freezer.move_to("2022-09-30T00:10:00+02:00")

price = Raw(PRICE_20220930)
assert price.get_raw() == PRICE_20220930
assert price.is_valid()
assert price.copy().get_raw() == PRICE_20220930
assert price.max_value() == 388.65
assert price.last_value() == 49.64
assert price.number_of_nonzero() == 24
assert len(price.copy().today().get_raw()) == 24
assert len(price.copy().tomorrow().get_raw()) == 0

time = datetime(
2022, 9, 30, 8, 0, 0, tzinfo=dt_util.get_time_zone("Europe/Stockholm")
Expand All @@ -77,6 +82,8 @@ async def test_raw(hass, set_cet_timezone):
assert price.get_raw() == PRICE_20220930
price.extend(price2)
assert price.number_of_nonzero() == 48
assert len(price.copy().today().get_raw()) == 24
assert len(price.copy().tomorrow().get_raw()) == 24

start = price.data[0]["start"]
assert start.tzinfo == dt_util.get_time_zone("Europe/Stockholm")
Expand All @@ -94,6 +101,9 @@ async def test_raw(hass, set_cet_timezone):
assert not price.is_valid()
assert price.last_value() is None

price = Raw(PRICE_20221001)
assert len(price.copy().today().get_raw()) == 0


async def test_raw_energidataservice(hass, set_cet_timezone):
"""Test Raw"""
Expand Down Expand Up @@ -205,6 +215,7 @@ async def test_raw_entsoe(hass, set_cet_timezone, freezer):
assert not price.is_valid()
assert price.last_value() is None


async def test_raw_tge(hass, set_cet_timezone):
"""Test Raw"""

Expand Down Expand Up @@ -258,6 +269,7 @@ async def test_raw_tge(hass, set_cet_timezone):
assert not price.is_valid()
assert price.last_value() is None


async def test_get_lowest_hours_non_continuous(hass, set_cet_timezone, freezer):
"""Test get_lowest_hours()"""

Expand Down
Loading