juham-automation 0.0.17__py3-none-any.whl → 0.0.19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- juham_automation/automation/hotwateroptimizer.py +70 -31
 - {juham_automation-0.0.17.dist-info → juham_automation-0.0.19.dist-info}/METADATA +1 -1
 - {juham_automation-0.0.17.dist-info → juham_automation-0.0.19.dist-info}/RECORD +7 -7
 - {juham_automation-0.0.17.dist-info → juham_automation-0.0.19.dist-info}/WHEEL +0 -0
 - {juham_automation-0.0.17.dist-info → juham_automation-0.0.19.dist-info}/entry_points.txt +0 -0
 - {juham_automation-0.0.17.dist-info → juham_automation-0.0.19.dist-info}/licenses/LICENSE.rst +0 -0
 - {juham_automation-0.0.17.dist-info → juham_automation-0.0.19.dist-info}/top_level.txt +0 -0
 
| 
         @@ -176,41 +176,64 @@ class HotWaterOptimizer(Juham): 
     | 
|
| 
       176 
176 
     | 
    
         
             
                    self.debug(f"Forecast sorted for the next {str(len(ranked_hours))} hours")
         
     | 
| 
       177 
177 
     | 
    
         
             
                    return ranked_hours
         
     | 
| 
       178 
178 
     | 
    
         | 
| 
      
 179 
     | 
    
         
            +
                def on_spot(self, m: list[dict[str, Any]], ts_quantized: float) -> None:
         
     | 
| 
      
 180 
     | 
    
         
            +
                    """Handle the spot prices.
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 183 
     | 
    
         
            +
                        list[dict[str, Any]]: list of spot prices
         
     | 
| 
      
 184 
     | 
    
         
            +
                        ts_quantized (float): current time
         
     | 
| 
      
 185 
     | 
    
         
            +
                    """
         
     | 
| 
      
 186 
     | 
    
         
            +
                    self.ranked_spot_prices = self.sort_by_rank(m, ts_quantized)
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                def on_forecast(
         
     | 
| 
      
 189 
     | 
    
         
            +
                    self, forecast: list[dict[str, Any]], ts_utc_quantized: float
         
     | 
| 
      
 190 
     | 
    
         
            +
                ) -> None:
         
     | 
| 
      
 191 
     | 
    
         
            +
                    """Handle the solar forecast.
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 194 
     | 
    
         
            +
                        m (list[dict[str, Any]]): list of forecast prices
         
     | 
| 
      
 195 
     | 
    
         
            +
                        ts_quantized (float): current time
         
     | 
| 
      
 196 
     | 
    
         
            +
                    """
         
     | 
| 
      
 197 
     | 
    
         
            +
                    # reject forecasts that don't have solarenergy key
         
     | 
| 
      
 198 
     | 
    
         
            +
                    for f in forecast:
         
     | 
| 
      
 199 
     | 
    
         
            +
                        if not "solarenergy" in f:
         
     | 
| 
      
 200 
     | 
    
         
            +
                            return
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                    self.ranked_solarpower = self.sort_by_power(forecast, ts_utc_quantized)
         
     | 
| 
      
 203 
     | 
    
         
            +
                    self.debug(
         
     | 
| 
      
 204 
     | 
    
         
            +
                        f"Solar energy forecast received and ranked for {len(self.ranked_solarpower)} hours"
         
     | 
| 
      
 205 
     | 
    
         
            +
                    )
         
     | 
| 
      
 206 
     | 
    
         
            +
                    self.power_plan = []  # reset power plan, it depends on forecast
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                def on_power(self, m: dict[str, Any], ts: float) -> None:
         
     | 
| 
      
 209 
     | 
    
         
            +
                    """Handle the power consumption. Read the current power balance and accumulate
         
     | 
| 
      
 210 
     | 
    
         
            +
                    to the net energy balance to reflect the  energy produced (or consumed) within the
         
     | 
| 
      
 211 
     | 
    
         
            +
                    current time slot.
         
     | 
| 
      
 212 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 213 
     | 
    
         
            +
                        m (dict[str, Any]): power consumption message
         
     | 
| 
      
 214 
     | 
    
         
            +
                        ts (float): current time
         
     | 
| 
      
 215 
     | 
    
         
            +
                    """
         
     | 
| 
      
 216 
     | 
    
         
            +
                    self.net_energy_power = m["power"]
         
     | 
| 
      
 217 
     | 
    
         
            +
                    balance: float = (ts - self.net_energy_balance_ts) * self.net_energy_power
         
     | 
| 
      
 218 
     | 
    
         
            +
                    self.net_energy_balance = self.net_energy_balance + balance
         
     | 
| 
      
 219 
     | 
    
         
            +
                    self.net_energy_balance_ts = ts
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
       179 
221 
     | 
    
         
             
                @override
         
     | 
| 
       180 
222 
     | 
    
         
             
                def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None:
         
     | 
| 
       181 
223 
     | 
    
         
             
                    m = None
         
     | 
| 
       182 
224 
     | 
    
         
             
                    ts: float = timestamp()
         
     | 
| 
       183 
225 
     | 
    
         
             
                    ts_utc_quantized: float = quantize(3600, ts - 3600)
         
     | 
| 
       184 
226 
     | 
    
         
             
                    if msg.topic == self.topic_spot:
         
     | 
| 
       185 
     | 
    
         
            -
                        self. 
     | 
| 
       186 
     | 
    
         
            -
                            json.loads(msg.payload.decode()), ts_utc_quantized
         
     | 
| 
       187 
     | 
    
         
            -
                        )
         
     | 
| 
       188 
     | 
    
         
            -
                        self.debug(
         
     | 
| 
       189 
     | 
    
         
            -
                            f"Spot prices received and ranked for {len(self.ranked_spot_prices)} hours"
         
     | 
| 
       190 
     | 
    
         
            -
                        )
         
     | 
| 
       191 
     | 
    
         
            -
                        self.power_plan = []  # reset power plan, it depends on spot prices
         
     | 
| 
      
 227 
     | 
    
         
            +
                        self.on_spot(json.loads(msg.payload.decode()), ts_utc_quantized)
         
     | 
| 
       192 
228 
     | 
    
         
             
                        return
         
     | 
| 
       193 
229 
     | 
    
         
             
                    elif msg.topic == self.topic_forecast:
         
     | 
| 
       194 
     | 
    
         
            -
                         
     | 
| 
       195 
     | 
    
         
            -
                        # reject messages that don't have  solarenergy forecast
         
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
                        for f in forecast:
         
     | 
| 
       198 
     | 
    
         
            -
                            if not "solarenergy" in f:
         
     | 
| 
       199 
     | 
    
         
            -
                                return
         
     | 
| 
       200 
     | 
    
         
            -
             
     | 
| 
       201 
     | 
    
         
            -
                        self.ranked_solarpower = self.sort_by_power(forecast, ts_utc_quantized)
         
     | 
| 
       202 
     | 
    
         
            -
                        self.debug(
         
     | 
| 
       203 
     | 
    
         
            -
                            f"Solar energy forecast received and ranked for {len(self.ranked_solarpower)} hours"
         
     | 
| 
       204 
     | 
    
         
            -
                        )
         
     | 
| 
       205 
     | 
    
         
            -
                        self.power_plan = []  # reset power plan, it depends on forecast
         
     | 
| 
      
 230 
     | 
    
         
            +
                        self.on_forecast(json.loads(msg.payload.decode()), ts_utc_quantized)
         
     | 
| 
       206 
231 
     | 
    
         
             
                        return
         
     | 
| 
       207 
232 
     | 
    
         
             
                    elif msg.topic == self.topic_temperature:
         
     | 
| 
       208 
233 
     | 
    
         
             
                        m = json.loads(msg.payload.decode())
         
     | 
| 
       209 
234 
     | 
    
         
             
                        self.current_temperature = m["temperature"]
         
     | 
| 
       210 
235 
     | 
    
         
             
                    elif msg.topic == self.topic_in_net_energy_balance:
         
     | 
| 
       211 
     | 
    
         
            -
                         
     | 
| 
       212 
     | 
    
         
            -
                        self.net_energy_balance = m["energy"]
         
     | 
| 
       213 
     | 
    
         
            -
                        self.net_energy_power = m["power"]
         
     | 
| 
      
 236 
     | 
    
         
            +
                        self.on_power(json.loads(msg.payload.decode()), ts)
         
     | 
| 
       214 
237 
     | 
    
         
             
                    elif msg.topic == self.topic_in_powerconsumption:
         
     | 
| 
       215 
238 
     | 
    
         
             
                        m = json.loads(msg.payload.decode())
         
     | 
| 
       216 
239 
     | 
    
         
             
                        self.current_power = m["real_total"]
         
     | 
| 
         @@ -220,7 +243,10 @@ class HotWaterOptimizer(Juham): 
     | 
|
| 
       220 
243 
     | 
    
         
             
                    self.on_powerplan(ts)
         
     | 
| 
       221 
244 
     | 
    
         | 
| 
       222 
245 
     | 
    
         
             
                def on_powerplan(self, ts_utc_now: float) -> None:
         
     | 
| 
       223 
     | 
    
         
            -
                    """Apply power plan.
         
     | 
| 
      
 246 
     | 
    
         
            +
                    """Apply the power plan. Check if the relay needs to be switched on or off.
         
     | 
| 
      
 247 
     | 
    
         
            +
                    The relay is switched on if the current temperature is below the maximum
         
     | 
| 
      
 248 
     | 
    
         
            +
                    temperature and the current time is within the heating plan. The relay is switched off
         
     | 
| 
      
 249 
     | 
    
         
            +
                    if the current temperature is above the maximum temperature or the current time is outside.
         
     | 
| 
       224 
250 
     | 
    
         | 
| 
       225 
251 
     | 
    
         
             
                    Args:
         
     | 
| 
       226 
252 
     | 
    
         
             
                        ts_utc_now (float): utc time
         
     | 
| 
         @@ -313,22 +339,33 @@ class HotWaterOptimizer(Juham): 
     | 
|
| 
       313 
339 
     | 
    
         | 
| 
       314 
340 
     | 
    
         
             
                    # don't bother to switch the relay on for small intervals, to avoid
         
     | 
| 
       315 
341 
     | 
    
         
             
                    # wearing contactors out
         
     | 
| 
       316 
     | 
    
         
            -
                    if  
     | 
| 
      
 342 
     | 
    
         
            +
                    if (
         
     | 
| 
      
 343 
     | 
    
         
            +
                        not self.net_energy_balancing_mode
         
     | 
| 
      
 344 
     | 
    
         
            +
                        and remaining_ts < self.operation_threshold
         
     | 
| 
      
 345 
     | 
    
         
            +
                    ):
         
     | 
| 
       317 
346 
     | 
    
         
             
                        print(
         
     | 
| 
       318 
347 
     | 
    
         
             
                            f"Skipping balance, remaining time {remaining_ts}s < {self.operation_threshold}s"
         
     | 
| 
       319 
348 
     | 
    
         
             
                        )
         
     | 
| 
       320 
349 
     | 
    
         
             
                        return False
         
     | 
| 
      
 350 
     | 
    
         
            +
                    elif remaining_ts <= 0:
         
     | 
| 
      
 351 
     | 
    
         
            +
                        self.net_energy_balancing_rc = False  # heating off
         
     | 
| 
      
 352 
     | 
    
         
            +
                        self.info(
         
     | 
| 
      
 353 
     | 
    
         
            +
                            f"End of the balancing interval reached, disabled with {self.net_energy_balance/3600}kWh left"
         
     | 
| 
      
 354 
     | 
    
         
            +
                        )
         
     | 
| 
      
 355 
     | 
    
         
            +
                        self.net_energy_balance = 0.0
         
     | 
| 
      
 356 
     | 
    
         
            +
                        self.net_energy_balance_ts = ts
         
     | 
| 
      
 357 
     | 
    
         
            +
                        return False
         
     | 
| 
       321 
358 
     | 
    
         | 
| 
       322 
     | 
    
         
            -
                    # check if the balance is sufficient for heating the  
     | 
| 
       323 
     | 
    
         
            -
                    # if yes then switch heating on 
     | 
| 
       324 
     | 
    
         
            -
                    needed_energy =  
     | 
| 
      
 359 
     | 
    
         
            +
                    # check if the balance is sufficient for heating the remainin interval
         
     | 
| 
      
 360 
     | 
    
         
            +
                    # if yes then switch heating on
         
     | 
| 
      
 361 
     | 
    
         
            +
                    needed_energy = self.radiator_power * remaining_ts
         
     | 
| 
       325 
362 
     | 
    
         
             
                    elapsed_interval = ts - self.net_energy_balance_ts
         
     | 
| 
       326 
363 
     | 
    
         
             
                    print(
         
     | 
| 
       327 
     | 
    
         
            -
                        f"Needed energy {int(needed_energy)} 
     | 
| 
      
 364 
     | 
    
         
            +
                        f"Needed energy {int(needed_energy)/3600}kWh, current balance {int(self.net_energy_balance/3600)}kWh"
         
     | 
| 
       328 
365 
     | 
    
         
             
                    )
         
     | 
| 
       329 
366 
     | 
    
         | 
| 
       330 
367 
     | 
    
         
             
                    if (
         
     | 
| 
       331 
     | 
    
         
            -
                        self.net_energy_balance  
     | 
| 
      
 368 
     | 
    
         
            +
                        self.net_energy_balance >= needed_energy
         
     | 
| 
       332 
369 
     | 
    
         
             
                    ) and not self.net_energy_balancing_rc:
         
     | 
| 
       333 
370 
     | 
    
         
             
                        self.net_energy_balance_ts = ts
         
     | 
| 
       334 
371 
     | 
    
         
             
                        self.net_energy_balancing_rc = True  # heat
         
     | 
| 
         @@ -338,7 +375,7 @@ class HotWaterOptimizer(Juham): 
     | 
|
| 
       338 
375 
     | 
    
         
             
                        # check if we have reach the end of the interval, or consumed all the energy
         
     | 
| 
       339 
376 
     | 
    
         
             
                        # of the current slot. If so switch the energy balancer mode off
         
     | 
| 
       340 
377 
     | 
    
         
             
                        if (
         
     | 
| 
       341 
     | 
    
         
            -
                            elapsed_interval > self.energy_balancing_interval 
     | 
| 
      
 378 
     | 
    
         
            +
                            elapsed_interval > self.energy_balancing_interval
         
     | 
| 
       342 
379 
     | 
    
         
             
                            or self.net_energy_balance < 0
         
     | 
| 
       343 
380 
     | 
    
         
             
                        ):
         
     | 
| 
       344 
381 
     | 
    
         
             
                            self.net_energy_balancing_rc = False  # heating off
         
     | 
| 
         @@ -346,7 +383,9 @@ class HotWaterOptimizer(Juham): 
     | 
|
| 
       346 
383 
     | 
    
         
             
                    return self.net_energy_balancing_rc
         
     | 
| 
       347 
384 
     | 
    
         | 
| 
       348 
385 
     | 
    
         
             
                def consider_heating(self, ts: float) -> int:
         
     | 
| 
       349 
     | 
    
         
            -
                    """Consider whether the target boiler needs heating.
         
     | 
| 
      
 386 
     | 
    
         
            +
                    """Consider whether the target boiler needs heating. Check first if the solar
         
     | 
| 
      
 387 
     | 
    
         
            +
                    energy is enough to heat the water the remaining time in the current slot.
         
     | 
| 
      
 388 
     | 
    
         
            +
                    If not, follow the predefined heating plan computed earlier based on the cheapest spot prices.
         
     | 
| 
       350 
389 
     | 
    
         | 
| 
       351 
390 
     | 
    
         
             
                    Args:
         
     | 
| 
       352 
391 
     | 
    
         
             
                        ts (float): current UTC time
         
     | 
| 
         @@ -3,7 +3,7 @@ juham_automation/japp.py,sha256=zD5ulfIcaSzwbVKjHv2tdXpw79fpw97B7P-v-ncY6e4,1520 
     | 
|
| 
       3 
3 
     | 
    
         
             
            juham_automation/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
         
     | 
| 
       4 
4 
     | 
    
         
             
            juham_automation/automation/__init__.py,sha256=73Mw0jkipeMCoUtpREHJPATfKe368ZyNeqfbjamBx_Q,481
         
     | 
| 
       5 
5 
     | 
    
         
             
            juham_automation/automation/energycostcalculator.py,sha256=7GGKLv5JpHAY3XVjbkboXuQBk-whpWkBwNuQ68Tv4Pc,11091
         
     | 
| 
       6 
     | 
    
         
            -
            juham_automation/automation/hotwateroptimizer.py,sha256= 
     | 
| 
      
 6 
     | 
    
         
            +
            juham_automation/automation/hotwateroptimizer.py,sha256=ImbbfSBQMQXGVneAo8ldN1Dcjyjow75qhLZ1kbqhQzQ,24999
         
     | 
| 
       7 
7 
     | 
    
         
             
            juham_automation/automation/powermeter_simulator.py,sha256=0g0gOD9WTqxUj9IbENkea_33JrJ2sZDSQVtmxVUHcD8,4688
         
     | 
| 
       8 
8 
     | 
    
         
             
            juham_automation/automation/spothintafi.py,sha256=XnL2zIPx_XaP_1E8ksuYEUemtHP7N6tLlSv2LEBQyXQ,4471
         
     | 
| 
       9 
9 
     | 
    
         
             
            juham_automation/automation/watercirculator.py,sha256=d7PQFNajtVafizS_y2R_6GWhm_GYb8uV4-QScz1Sggo,6569
         
     | 
| 
         @@ -15,9 +15,9 @@ juham_automation/ts/log_ts.py,sha256=DPfeJhbSMQChY37mjAxEmE73Ys3dxUvNsN78PSuBm9Y 
     | 
|
| 
       15 
15 
     | 
    
         
             
            juham_automation/ts/power_ts.py,sha256=esNbtH1xklyUaf0YJQ2wDuxTAV3SnEfx-FtiBGPaSVA,1448
         
     | 
| 
       16 
16 
     | 
    
         
             
            juham_automation/ts/powermeter_ts.py,sha256=zSATxZAzz1KJeU1wFK8CP86iySWnHil89mridz7WHos,2421
         
     | 
| 
       17 
17 
     | 
    
         
             
            juham_automation/ts/powerplan_ts.py,sha256=-Lhc7v5Cj7USy2MfmyUEusXSox9UbEoDtYGReDEt3cw,1527
         
     | 
| 
       18 
     | 
    
         
            -
            juham_automation-0.0. 
     | 
| 
       19 
     | 
    
         
            -
            juham_automation-0.0. 
     | 
| 
       20 
     | 
    
         
            -
            juham_automation-0.0. 
     | 
| 
       21 
     | 
    
         
            -
            juham_automation-0.0. 
     | 
| 
       22 
     | 
    
         
            -
            juham_automation-0.0. 
     | 
| 
       23 
     | 
    
         
            -
            juham_automation-0.0. 
     | 
| 
      
 18 
     | 
    
         
            +
            juham_automation-0.0.19.dist-info/licenses/LICENSE.rst,sha256=D3SSbUrv10lpAZ91lTMCQAke-MXMvrjFDsDyM3vEKJI,1114
         
     | 
| 
      
 19 
     | 
    
         
            +
            juham_automation-0.0.19.dist-info/METADATA,sha256=zjFtQzp9U-30OuXPZHRoyWSgewTKr5tdM9dGceg3ICY,4733
         
     | 
| 
      
 20 
     | 
    
         
            +
            juham_automation-0.0.19.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
         
     | 
| 
      
 21 
     | 
    
         
            +
            juham_automation-0.0.19.dist-info/entry_points.txt,sha256=di8tXChhP8B_98bQ44u-1zkOha2kZCoJpCAXxTgoSw8,491
         
     | 
| 
      
 22 
     | 
    
         
            +
            juham_automation-0.0.19.dist-info/top_level.txt,sha256=jfohvtocvX_gfT21AhJk7Iay5ZiQsS3HzrDjF7S4Qp0,17
         
     | 
| 
      
 23 
     | 
    
         
            +
            juham_automation-0.0.19.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
    
        {juham_automation-0.0.17.dist-info → juham_automation-0.0.19.dist-info}/licenses/LICENSE.rst
    RENAMED
    
    | 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |