Skip to content

Commit

Permalink
Merge branch 'feature/structure' into develop
Browse files Browse the repository at this point in the history
# Conflicts:
#	custom_components/peaqev/peaqservice/chargecontroller/ichargecontroller.py
#	custom_components/peaqev/peaqservice/charger/charger.py
#	custom_components/peaqev/peaqservice/hub/nordpool/nordpool.py
  • Loading branch information
magnuselden authored and magnuselden committed Mar 15, 2023
2 parents 348959e + bf02192 commit f6f7111
Show file tree
Hide file tree
Showing 50 changed files with 1,335 additions and 1,076 deletions.
75 changes: 44 additions & 31 deletions custom_components/peaqev/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""The peaqev integration."""
from __future__ import annotations

import asyncio
import logging

from homeassistant.config_entries import ConfigEntry
Expand All @@ -23,40 +24,17 @@ async def async_setup_entry(hass: HomeAssistant, conf: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][conf.entry_id] = conf.data

options = HubOptions()
options.peaqev_lite = bool(conf.data.get("peaqevtype") == TYPELITE)
options.locale = conf.data.get("locale", "")
options.charger.chargertype = conf.data.get("chargertype", "")
if options.charger.chargertype == ChargerType.Outlet.value:
options.charger.powerswitch = conf.data.get("outletswitch", "")
options.charger.powermeter = conf.data.get("outletpowermeter", "")
elif options.charger.chargertype != ChargerType.NoCharger.value:
options.charger.chargerid = conf.data.get("chargerid", "")
if options.charger.chargertype == ChargerType.NoCharger.value:
options.powersensor_includes_car = True
else:
options.powersensor_includes_car = conf.data.get("powersensorincludescar", False)
options.startpeaks = conf.options.get("startpeaks", conf.data.get("startpeaks"))
options.cautionhours = await _get_existing_param(conf, "cautionhours", [])
options.nonhours = await _get_existing_param(conf, "nonhours", [])
options.price.price_aware = await _get_existing_param(conf, "priceaware", False)
options.price.min_price = await _get_existing_param(conf, "min_priceaware_threshold_price", 0)
options.price.top_price = await _get_existing_param(conf, "absolute_top_price", 0)
options.price.dynamic_top_price = await _get_existing_param(conf, "dynamic_top_price", False)
options.price.cautionhour_type = await _get_existing_param(conf, "cautionhour_type", "intermediate")
options.fuse_type = await _get_existing_param(conf, "mains", "")
options = await _set_options(conf)
ci = {}

if options.peaqev_lite:
hub = HomeAssistantHub(hass, options, DOMAIN, ci)
else:
if options.peaqev_lite is False:
ci["powersensor"] = conf.data["name"]
options.powersensor = conf.data["name"]

unsub_options_update_listener = conf.add_update_listener(options_update_listener)
ci["unsub_options_update_listener"] = unsub_options_update_listener
hass.data[DOMAIN][conf.entry_id] = ci
hub = HomeAssistantHub(hass, options, DOMAIN, ci)
unsub_options_update_listener = conf.add_update_listener(options_update_listener)
ci["unsub_options_update_listener"] = unsub_options_update_listener
hass.data[DOMAIN][conf.entry_id] = ci
hub = HomeAssistantHub(hass, options, DOMAIN)

hass.data[DOMAIN]["hub"] = hub

Expand Down Expand Up @@ -90,7 +68,8 @@ async def servicehandler_scheduler_cancel(call):
hass.services.async_register(DOMAIN, "override_nonhours", servicehandler_override_nonhours)
hass.services.async_register(DOMAIN, "scheduler_set", servicehandler_scheduler_set)
hass.services.async_register(DOMAIN, "scheduler_cancel", servicehandler_scheduler_cancel)
hass.config_entries.async_setup_platforms(conf, PLATFORMS)

await hass.config_entries.async_forward_entry_setups(conf, PLATFORMS)

return True

Expand All @@ -102,11 +81,45 @@ async def options_update_listener(hass: HomeAssistant, conf: ConfigEntry):

async def async_unload_entry(hass: HomeAssistant, conf: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(conf, PLATFORMS)
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(conf, component)
for component in PLATFORMS
]
)
)
if unload_ok:
hass.data[DOMAIN].pop(conf.entry_id)
return unload_ok


async def _set_options(conf) -> HubOptions:
options = HubOptions()
options.peaqev_lite = bool(conf.data.get("peaqevtype") == TYPELITE)
options.locale = conf.data.get("locale", "")
options.charger.chargertype = conf.data.get("chargertype", "")
if options.charger.chargertype == ChargerType.Outlet.value:
options.charger.powerswitch = conf.data.get("outletswitch", "")
options.charger.powermeter = conf.data.get("outletpowermeter", "")
elif options.charger.chargertype != ChargerType.NoCharger.value:
options.charger.chargerid = conf.data.get("chargerid", "")
if options.charger.chargertype == ChargerType.NoCharger.value:
options.powersensor_includes_car = True
else:
options.powersensor_includes_car = conf.data.get("powersensorincludescar", False)
options.startpeaks = conf.options.get("startpeaks", conf.data.get("startpeaks"))
options.cautionhours = await _get_existing_param(conf, "cautionhours", [])
options.nonhours = await _get_existing_param(conf, "nonhours", [])
options.price.price_aware = await _get_existing_param(conf, "priceaware", False)
options.price.min_price = await _get_existing_param(conf, "min_priceaware_threshold_price", 0)
options.price.top_price = await _get_existing_param(conf, "absolute_top_price", 0)
options.price.dynamic_top_price = await _get_existing_param(conf, "dynamic_top_price", False)
options.price.cautionhour_type = await _get_existing_param(conf, "cautionhour_type", "intermediate")
options.fuse_type = await _get_existing_param(conf, "mains", "")
options.blocknocturnal = await _get_existing_param(conf, "blocknocturnal", False)
return options


async def _get_existing_param(conf, parameter: str, default_val: any):
return conf.options.get(parameter, conf.data.get(parameter, default_val))
11 changes: 8 additions & 3 deletions custom_components/peaqev/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from datetime import timedelta

from homeassistant.core import HomeAssistant

import custom_components.peaqev.sensors.create_sensor_helper as helper
from custom_components.peaqev.sensors.peaq_binary_sensor import PeaqBinarySensorDone
from custom_components.peaqev.peaqservice.chargertypes.models.chargertypes_enum import ChargerType
from custom_components.peaqev.const import (
DOMAIN)

Expand All @@ -12,9 +12,14 @@
async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): # pylint:disable=unused-argument
hub = hass.data[DOMAIN]["hub"]

peaqsensors = await helper.gather_binary_sensors(hub)
peaqsensors = await _gather_binary_sensors(hub)
async_add_entities(peaqsensors)

SCAN_INTERVAL = timedelta(seconds=4)


async def _gather_binary_sensors(hub) -> list:
ret = []
if hub.chargertype.type != ChargerType.NoCharger:
ret.append(PeaqBinarySensorDone(hub))
return ret
36 changes: 22 additions & 14 deletions custom_components/peaqev/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,18 +164,22 @@ async def async_step_misc(self, user_input=None):
"""Misc options"""
if user_input is not None:
self.data["mains"] = user_input["mains"]
self.data["blocknocturnal"] = user_input["blocknocturnal"]
return self.async_create_entry(title=self.info["title"], data=self.data)

return self.async_show_form(
step_id="misc",
last_step=True,
data_schema=vol.Schema(
schema = vol.Schema(
{
vol.Optional(
"mains",
default="",
): vol.In(FUSES_LIST)
): vol.In(FUSES_LIST),
vol.Optional("blocknocturnal", default=False): cv.boolean,
})

return self.async_show_form(
step_id="misc",
last_step=True,
data_schema=schema
)


Expand Down Expand Up @@ -279,19 +283,23 @@ async def async_step_misc(self, user_input=None):
"""Misc options"""
if user_input is not None:
self.options["mains"] = user_input["mains"]
self.options["blocknocturnal"] = user_input["blocknocturnal"]
return self.async_create_entry(title="", data=self.options)

mainsvalue = await self._get_existing_param("mains", "")
_LOGGER.debug(f"existing mainsvalue is: {mainsvalue}")
blocknocturnal = await self._get_existing_param("blocknocturnal", False)

schema = vol.Schema(
{
vol.Optional(
"mains",
default=mainsvalue,
): vol.In(FUSES_LIST),
vol.Optional("blocknocturnal", default=blocknocturnal): cv.boolean,
})

return self.async_show_form(
step_id="misc",
last_step=True,
data_schema=vol.Schema(
{
vol.Optional(
"mains",
default=mainsvalue,
): vol.In(FUSES_LIST)
})

data_schema=schema
)
2 changes: 1 addition & 1 deletion custom_components/peaqev/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"iot_class": "calculated",
"issue_tracker": "https://github.com/elden1337/hass-peaq/issues",
"requirements": [
"peaqevcore==12.2.1"
"peaqevcore==13.2.0"
],
"version": "2.8.0b1"
}
Original file line number Diff line number Diff line change
@@ -1,55 +1,54 @@
import logging

from peaqevcore.models.chargecontroller_states import ChargeControllerStates
from peaqevcore.services.chargecontroller.chargecontrollerbase import ChargeControllerBase as _core
#from peaqevcore.services.chargecontroller.chargecontrollerbase import ChargeControllerBase as _core

from custom_components.peaqev.peaqservice.chargecontroller.chargecontroller_helpers import defer_start
from custom_components.peaqev.peaqservice.chargecontroller.ichargecontroller import IChargeController

_LOGGER = logging.getLogger(__name__)


class ChargeController(IChargeController):
def __init__(self, hub):
super().__init__(hub)
self._core = _core(charger_state_translation=self._hub.chargertype.chargerstates)
def __init__(self, hub, charger_states):
super().__init__(hub, charger_states)
#self._core = _core(charger_state_translation=charger_states)

@property
def below_startthreshold(self) -> bool:
return self._core._below_start_threshold(
predicted_energy=self._hub.prediction.predictedenergy,
current_peak=self._hub.current_peak_dynamic,
threshold_start=self._hub.threshold.start/100
)
predicted_energy=self.hub.prediction.predictedenergy
current_peak=self.hub.current_peak_dynamic
threshold_start=self.hub.threshold.start / 100
return (predicted_energy * 1000) < ((current_peak * 1000) * threshold_start)

@property
def above_stopthreshold(self) -> bool:
return self._core._above_stop_threshold(
predicted_energy=self._hub.prediction.predictedenergy,
current_peak=self._hub.current_peak_dynamic,
threshold_stop=self._hub.threshold.stop/100
)
predicted_energy=self.hub.prediction.predictedenergy
current_peak=self.hub.current_peak_dynamic
threshold_stop=self.hub.threshold.stop / 100
return (predicted_energy * 1000) > ((current_peak * 1000) * threshold_stop)

def _get_status_charging(self) -> ChargeControllerStates:
if not self._hub.power_canary.alive:
if not self.hub.power_canary.alive:
return ChargeControllerStates.Stop
if all([
self.above_stopthreshold,
self._hub.sensors.totalhourlyenergy.value > 0,
not self._hub.sensors.locale.data.free_charge(self._hub.sensors.locale.data)
self.hub.sensors.totalhourlyenergy.value > 0,
not self.hub.is_free_charge
]):
return ChargeControllerStates.Stop
return ChargeControllerStates.Start

def _get_status_connected(self, charger_state=None) -> ChargeControllerStates:
if charger_state is not None and self._hub.sensors.carpowersensor.value < 1 and self._is_done(charger_state):
if charger_state is not None and self.hub.sensors.carpowersensor.value < 1 and self._is_done(charger_state):
ret = ChargeControllerStates.Done
else:
if all([
any([
(self.below_startthreshold and self._hub.sensors.totalhourlyenergy.value != 0),
self._hub.sensors.locale.data.free_charge(self._hub.sensors.locale.data) is True
(self.below_startthreshold and self.hub.sensors.totalhourlyenergy.value != 0),
self.hub.is_free_charge
]),
not self._defer_start(self._hub.hours.non_hours)
not defer_start(self.hub.hours.non_hours)
]):
ret = ChargeControllerStates.Start
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from datetime import datetime, timedelta


def defer_start(non_hours: list) -> bool:
"""Defer starting if next hour is a non-hour and minute is 50 or greater, to avoid short running times."""
if (datetime.now() + timedelta(hours=1)).hour in non_hours:
return datetime.now().minute >= 50
return False


def calculate_stop_len(nonhours) -> str:
ret = ""
for idx, h in enumerate(nonhours):
if idx + 1 < len(nonhours):
if _getuneven(nonhours[idx + 1], nonhours[idx]):
ret = _get_stopped_string(h)
break
elif idx + 1 == len(nonhours):
ret = _get_stopped_string(h)
break
return ret


def _get_stopped_string(h) -> str:
val = h + 1 if h + 1 < 24 else h + 1 - 24
if len(str(val)) == 1:
return f"Charging stopped until 0{val}:00"
return f"Charging stopped until {val}:00"


def _getuneven(first, second) -> bool:
if second > first:
return first - (second - 24) != 1
return first - second != 1
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,29 @@

from peaqevcore.models.chargecontroller_states import ChargeControllerStates

from custom_components.peaqev.peaqservice.chargecontroller.chargecontroller_helpers import defer_start
from custom_components.peaqev.peaqservice.chargecontroller.ichargecontroller import IChargeController

_LOGGER = logging.getLogger(__name__)


class ChargeControllerLite(IChargeController):
def __init__(self, hub):
super().__init__(hub)
def __init__(self, hub, charger_states):
super().__init__(hub, charger_states)

def _get_status_charging(self) -> ChargeControllerStates:
if self._hub.sensors.totalhourlyenergy.value >= self._hub.current_peak_dynamic and self._hub.sensors.locale.data.free_charge(self._hub.sensors.locale.data) is False:
if self.hub.sensors.totalhourlyenergy.value >= self.hub.current_peak_dynamic and not self.hub.is_free_charge:
ret = ChargeControllerStates.Stop
else:
ret = ChargeControllerStates.Start
return ret

def _get_status_connected(self, charger_state = None) -> ChargeControllerStates:
if charger_state is not None and self._hub.sensors.carpowersensor.value < 1 and self._is_done(charger_state):
if charger_state is not None and self.hub.sensors.carpowersensor.value < 1 and self._is_done(charger_state):
ret = ChargeControllerStates.Done
else:
if (self._hub.totalhourlyenergy.value < self._hub.current_peak_dynamic) or self._hub.locale.data.free_charge(self._hub.locale.data) is True:
ret = ChargeControllerStates.Start if not self._defer_start(self._hub.hours.non_hours) else ChargeControllerStates.Stop
if (self.hub.totalhourlyenergy.value < self.hub.current_peak_dynamic) or self.hub.is_free_charge:
ret = ChargeControllerStates.Start if not defer_start(self.hub.hours.non_hours) else ChargeControllerStates.Stop
else:
ret = ChargeControllerStates.Stop
return ret
Loading

0 comments on commit f6f7111

Please sign in to comment.