Skip to content

Commit

Permalink
misc
Browse files Browse the repository at this point in the history
  • Loading branch information
magnuselden authored and magnuselden committed Sep 12, 2024
1 parent 5a32ff0 commit 177759b
Show file tree
Hide file tree
Showing 15 changed files with 115 additions and 54 deletions.
2 changes: 2 additions & 0 deletions custom_components/peaqhvac/sensors/min_maxsensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def icon(self) -> str:
return "mdi:thermometer"

def update(self) -> None:
if not self._hub.is_initialized:
return
if self._sensorname == AVERAGESENSOR_INDOORS:
self._state = self._hub.sensors.average_temp_indoors.value
self._min = self._hub.sensors.average_temp_indoors.min
Expand Down
2 changes: 2 additions & 0 deletions custom_components/peaqhvac/sensors/money_data_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def icon(self) -> str:
return "mdi:database-outline"

async def async_update(self) -> None:
if not self.hub.is_initialized:
return
try:
ret = self.hub.spotprice.average_data
except:
Expand Down
3 changes: 3 additions & 0 deletions custom_components/peaqhvac/sensors/offsetsensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def __init__(self, hub, entry_id, name):
self._peaks_today = []
self._peaks_tomorrow = []
self._prognosis = []
self._aux_dict = None

@property
def unit_of_measurement(self):
Expand All @@ -38,6 +39,8 @@ def icon(self) -> str:
return "mdi:stairs"

async def async_update(self) -> None:
if not self._hub.is_initialized:
return
state_result = await self._hub.hvac_service.house_heater.async_adjusted_offset(
self._hub.hvac_service.model.current_offset
)
Expand Down
2 changes: 2 additions & 0 deletions custom_components/peaqhvac/sensors/peaqsensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ def icon(self) -> str:
return self._icon

async def async_update(self) -> None:
if not self._hub.is_initialized:
return
if self._sensorname == HEATINGDEMAND:
self._state = self._hub.hvac_service.house_heater.demand.value
elif self._sensorname == WATERDEMAND:
Expand Down
2 changes: 2 additions & 0 deletions custom_components/peaqhvac/sensors/simple_money_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def unit_of_measurement(self):
return self._attr_unit_of_measurement

async def async_update(self) -> None:
if not self.hub.is_initialized:
return
self._use_cent = self.hub.spotprice.use_cent
self._attr_unit_of_measurement = getattr(self.hub.spotprice, "currency")
ret = getattr(self.hub.spotprice, self._caller_attribute)
Expand Down
2 changes: 2 additions & 0 deletions custom_components/peaqhvac/sensors/simple_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def icon(self) -> str:
return self._icon

async def async_update(self) -> None:
if not self._hub.is_initialized:
return
ret = await self._hub.async_get_internal_sensor(self._internal_entity)
if ret is not None:
if self._internal_entity == NEXT_WATER_START:
Expand Down
2 changes: 2 additions & 0 deletions custom_components/peaqhvac/sensors/trendsensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def extra_state_attributes(self) -> dict:
return attr_dict

async def async_update(self) -> None:
if not self._hub.is_initialized:
return
self._state = getattr(self.datasensor, "trend")
self._samples = getattr(self.datasensor, "samples")
self._oldest_sample = getattr(self.datasensor, "oldest_sample")
Expand Down
12 changes: 10 additions & 2 deletions custom_components/peaqhvac/service/hub/hub_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,27 @@ def __init__(self, hass: HomeAssistant):
self.hub = None

async def async_create(self, options: ConfigModel) -> Hub:
_LOGGER.debug("Entering async_create in hub_factory")
observer = Observer(self.hass)
_LOGGER.debug("Hubfactory > Created observer")
options.observer = observer
options.misc_options.peaqev_discovered = self._get_peaqev()
_LOGGER.debug("Hubfactory > Created options and set peaqev_discovered")

hub = Hub(self.hass, observer, options)
_LOGGER.debug("Hubfactory > Created hub")
spotprice = SpotPriceFactory.create(
hub=hub,
observer=observer,
system=PeaqSystem.PeaqHvac,
test=False,
is_active=True,
)
_LOGGER.debug("Hubfactory > Created spotprice")
sensors = HubSensors(observer=observer, options=options, hass=self.hass)
_LOGGER.debug("Hubfactory > Created sensors")
states = StateChanges(hub, self.hass)
_LOGGER.debug("Hubfactory > Created states")
self.hub = hub
await self.async_setup(spotprice, sensors, states)
return hub
Expand All @@ -53,12 +60,13 @@ async def async_setup(self, spotprice, sensors, states) -> None:
self.hub.hvac_service = HvacFactory.create(
self.hass, self.hub.options, self.hub, self.hub.observer
)

_LOGGER.debug("Hubfactory > Created hvacfactory")
self.hub.prognosis = WeatherPrognosis(
self.hass, sensors.average_temp_outdoors, self.hub.observer
)
_LOGGER.debug("Hubfactory > Created prognosis")
self.hub.offset = OffsetFactory.create(self.hub, self.hub.observer)

_LOGGER.debug("Hubfactory > Created offset")
await self.async_setup_trackers()

async def async_setup_trackers(self):
Expand Down
2 changes: 1 addition & 1 deletion custom_components/peaqhvac/service/hub/hubsensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, observer, options: ConfigModel, hass):
initval=options.misc_options.enabled_on_boot, data_type=bool
)
self.hvac_tolerance = options.hvac_tolerance
self.average_temp_indoors = Average(entities=options.indoor_tempsensors)
self.average_temp_indoors = Average(entities=options.indoor_tempsensors, observer=observer)
self.average_temp_outdoors = Average(
entities=options.outdoor_tempsensors,
observer_message=ObserverTypes.TemperatureOutdoorsChanged,
Expand Down
3 changes: 2 additions & 1 deletion custom_components/peaqhvac/service/hub/weather_prognosis.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ def _get_two_hour_prog(self, thishour: datetime) -> PrognosisExportModel | None:
def _setup_weather_prognosis(
self,
): # todo: this must be handled with weeather servicecall.
pass
self._is_initialized = True

# try:
# entities = template.integration_entities(self._hass, "met")
# if len(entities) < 1:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ async def async_calculated_offsetdata(
self.hub.sensors.set_temp_indoors.adjusted_temp,
)

_LOGGER.debug(f"current_offset: {current_offset}, tempdiff: {tempdiff}, tempextremas: {tempextremas}, temptrend: {temptrend}")
return CalculatedOffsetModel(
current_offset=current_offset,
current_tempdiff=tempdiff,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ async def async_update_offset(self, raw_offset:int|None = None) -> bool:
if self.model.current_offset != _hvac_offset:
await self.observer.async_broadcast(ObserverTypes.OffsetsChanged)
if self._force_update:
await self.observer.async_broadcast(ObserverTypes.UpdateOperation)
# await self.observer.async_broadcast(ObserverTypes.UpdateOperation)
await self.request_periodic_updates()
ret = True
except Exception as e:
_LOGGER.exception(f"Error in updating offsets: {e}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ def current_offset(self) -> int:
try:
self._set_offset()
if len(self.model.raw_offsets):
ret = self.model.raw_offsets.get(
datetime.now().replace(minute=0, second=0, microsecond=0), 0
)
latest_key = max((key for key in self.model.raw_offsets if key <= datetime.now()), default=None)
if latest_key is not None:
ret = self.model.raw_offsets[latest_key]
else:
ret = 0
except KeyError as e:
_LOGGER.error(
f"Unable to get current offset: {e}. raw_offsets: {self.model.raw_offsets}"
Expand Down
61 changes: 57 additions & 4 deletions custom_components/peaqhvac/service/hvac/offset/offset_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ def get_offset_dict(offset_dict, dt_now) -> dict:
}


def set_offset_dict(prices: list[float], dt: datetime, min_price: float) -> dict:
def set_offset_dict(prices: list[float], dt: datetime, min_price: float, split_into_parts: bool = False) -> dict:
dt = dt.replace(minute=0, second=0, microsecond=0)
all_offsets = _deviation_from_mean(prices, min_price, dt)
all_offsets = _deviation_from_mean(prices, min_price, dt, split_into_parts)
return all_offsets


Expand All @@ -71,7 +71,7 @@ def _get_timedelta(prices: list[float]) -> int:


def _deviation_from_mean(
prices: list[float], min_price: float, dt: datetime
prices: list[float], min_price: float, dt: datetime, split_into_parts: bool = False
) -> dict[datetime, float]:
if not len(prices):
return {}
Expand Down Expand Up @@ -103,7 +103,60 @@ def _deviation_from_mean(
else:
setval = round(deviation, 2)
deviation_dict[dt.replace(hour=0) + timedelta(minutes=delta * i)] = setval
return deviation_dict
if not split_into_parts:
return deviation_dict
return handle_splitting(deviation_dict)


def handle_splitting(data: dict[datetime, float]) -> dict[datetime, float]:
sorted_keys = sorted(data.keys())
result = {}

for i in range(len(sorted_keys) - 1):
current_key = sorted_keys[i]
next_key = sorted_keys[i + 1]
current_value = data[current_key]
next_value = data[next_key]

# Calculate the difference
diff = next_value - current_value

# Determine the split intervals
if abs(diff) > 1.0: # Large difference, split into half-hour intervals
interval = timedelta(minutes=30)
num_splits = 2
elif abs(diff) > 0.5: # Moderate difference, split into 20-minute intervals
interval = timedelta(minutes=20)
num_splits = 3
elif abs(diff) > 0.2: # Small difference, split into quarterly intervals
interval = timedelta(minutes=15)
num_splits = 4
else:
interval = timedelta(minutes=60)
num_splits = 1


# Calculate the split values
split_values = [current_value] * num_splits

# Adjust the last split value to be closer to the next hour's deviation
if num_splits > 1:
split_values[-1] = (split_values[-1] + next_value) / 2

# Ensure the average of the split values equals the original value
total_adjustment = current_value * num_splits - sum(split_values)
adjustment_per_split = total_adjustment / num_splits
split_values = [v + adjustment_per_split for v in split_values]

# Add the split values to the result
for j in range(num_splits):
split_key = current_key + interval * j
result[split_key] = split_values[j]

# Add the last key-value pair
result[sorted_keys[-1]] = data[sorted_keys[-1]]

return result


def max_price_lower_internal(tempdiff: float, peaks_today: list) -> bool:
Expand Down
64 changes: 22 additions & 42 deletions custom_components/peaqhvac/test/test_offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,50 +228,30 @@ def test_assert_cheaper_hours_tomorrow_not_lower_offset_than_today():

# assert 1 > 2

def test_split_into_periods():
_tolerance = 10
indoors_preset = HvacPresets.Normal
prices = P231218
prices_tomorrow = P231219
now_dt = datetime(2023, 12, 18, 20, 43, 0)
offset_dict = set_offset_dict(prices + prices_tomorrow, now_dt, 0, False)
offs2 = offset_per_day(
all_prices=prices + prices_tomorrow,
day_values=offset_dict,
tolerance=_tolerance,
indoors_preset=indoors_preset,
)

# def test_offsets_correct_curve_over_night_cached_today():
# _tolerance = 3
# indoors_preset = HvacPresets.Normal
# prices = P231215
# prices_tomorrow = P231216
# now_dt = datetime(2023,12,15,0,3,0)
# offset_dict1 = set_offset_dict(prices, now_dt, 0, {})
# today1 = offset_per_day(
# all_prices=prices,
# day_values=offset_dict1,
# tolerance=_tolerance,
# indoors_preset=indoors_preset,
# )
#
# ret1 = smooth_transitions(
# vals=today1,
# tolerance=_tolerance,
# )
# key_today_only = [1,1, 2, 2, 3, 2, -2,
# -4, -7, -6, -4, -3, -1, 0, 1, 0,
# -2, -1, 1, 2, 2, 3, 6, 4]
# assert [v for k,v in ret1.items()] == key_today_only
#
# now_dt = datetime(2023,12,15,13,3,0)
# offset_dict2 = set_offset_dict(prices+prices_tomorrow, now_dt, 0, ret1)
# today2 = offset_per_day(
# all_prices=prices + prices_tomorrow,
# day_values=offset_dict2,
# tolerance=_tolerance,
# indoors_preset=indoors_preset,
# )
#
# ret2 = smooth_transitions(
# vals=today2,
# tolerance=_tolerance,
# )
#
# key_today = [-3, -2, -2, -2, -2, -2,
# -3, -4, -5, -5, -4, -4, -3, -2, -2, -2, -3, -3, -2, -2, -2, -2, -1, -2,
# 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2]
#
# assert [v for k, v in ret2.items()] == key_today
ret = smooth_transitions(
vals=offs2,
tolerance=_tolerance,
)

print(offset_dict)
print(offs2)
print(ret)

assert 1 > 2

def test_smooth_transistions_no_weather_prog_nothing_exceeds_tolerance():
_tolerance = 3
Expand Down

0 comments on commit 177759b

Please sign in to comment.