juham-automation 0.0.19__py3-none-any.whl → 0.0.27__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/__init__.py +42 -38
- juham_automation/automation/__init__.py +23 -21
- juham_automation/automation/energybalancer.py +158 -0
- juham_automation/automation/energycostcalculator.py +267 -266
- juham_automation/automation/{hotwateroptimizer.py → heatingoptimizer.py} +539 -620
- juham_automation/automation/powermeter_simulator.py +139 -139
- juham_automation/automation/spothintafi.py +140 -140
- juham_automation/automation/watercirculator.py +159 -159
- juham_automation/japp.py +53 -49
- juham_automation/ts/__init__.py +27 -25
- juham_automation/ts/electricityprice_ts.py +51 -51
- juham_automation/ts/energybalancer_ts.py +47 -0
- juham_automation/ts/energycostcalculator_ts.py +43 -43
- juham_automation/ts/forecast_ts.py +97 -97
- juham_automation/ts/log_ts.py +57 -57
- juham_automation/ts/power_ts.py +49 -49
- juham_automation/ts/powermeter_ts.py +67 -70
- juham_automation/ts/powerplan_ts.py +45 -45
- juham_automation-0.0.27.dist-info/METADATA +152 -0
- juham_automation-0.0.27.dist-info/RECORD +25 -0
- {juham_automation-0.0.19.dist-info → juham_automation-0.0.27.dist-info}/entry_points.txt +3 -1
- {juham_automation-0.0.19.dist-info → juham_automation-0.0.27.dist-info}/licenses/LICENSE.rst +25 -25
- juham_automation-0.0.19.dist-info/METADATA +0 -106
- juham_automation-0.0.19.dist-info/RECORD +0 -23
- {juham_automation-0.0.19.dist-info → juham_automation-0.0.27.dist-info}/WHEEL +0 -0
- {juham_automation-0.0.19.dist-info → juham_automation-0.0.27.dist-info}/top_level.txt +0 -0
juham_automation/__init__.py
CHANGED
@@ -1,38 +1,42 @@
|
|
1
|
-
"""
|
2
|
-
Description
|
3
|
-
===========
|
4
|
-
|
5
|
-
Juham - Juha's Ultimate Home Automation Masterpiece
|
6
|
-
|
7
|
-
"""
|
8
|
-
|
9
|
-
from .automation import EnergyCostCalculator
|
10
|
-
from .automation import PowerMeterSimulator
|
11
|
-
from .automation import SpotHintaFi
|
12
|
-
from .automation import WaterCirculator
|
13
|
-
from .automation import
|
14
|
-
from .
|
15
|
-
from .ts import
|
16
|
-
from .ts import
|
17
|
-
from .ts import
|
18
|
-
from .ts import
|
19
|
-
from .ts import
|
20
|
-
from .ts import
|
21
|
-
from .
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"
|
31
|
-
"
|
32
|
-
"
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
38
|
-
|
1
|
+
"""
|
2
|
+
Description
|
3
|
+
===========
|
4
|
+
|
5
|
+
Juham - Juha's Ultimate Home Automation Masterpiece
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from .automation import EnergyCostCalculator
|
10
|
+
from .automation import PowerMeterSimulator
|
11
|
+
from .automation import SpotHintaFi
|
12
|
+
from .automation import WaterCirculator
|
13
|
+
from .automation import HeatingOptimizer
|
14
|
+
from .automation import EnergyBalancer
|
15
|
+
from .ts import EnergyCostCalculatorTs
|
16
|
+
from .ts import ForecastTs
|
17
|
+
from .ts import LogTs
|
18
|
+
from .ts import PowerTs
|
19
|
+
from .ts import PowerPlanTs
|
20
|
+
from .ts import PowerMeterTs
|
21
|
+
from .ts import ElectricityPriceTs
|
22
|
+
from .ts import EnergyBalancerTs
|
23
|
+
from .japp import JApp
|
24
|
+
|
25
|
+
|
26
|
+
__all__ = [
|
27
|
+
"EnergyCostCalculator",
|
28
|
+
"EnergyCostCalculatorTs",
|
29
|
+
"ForecastTs",
|
30
|
+
"HeatingOptimizer",
|
31
|
+
"EnergyBalancer",
|
32
|
+
"LogTs",
|
33
|
+
"PowerTs",
|
34
|
+
"PowerPlanTs",
|
35
|
+
"PowerMeterTs",
|
36
|
+
"SpotHintaFi",
|
37
|
+
"WaterCirculator",
|
38
|
+
"JApp",
|
39
|
+
"PowerMeterSimulator",
|
40
|
+
"ElectricityPriceTs",
|
41
|
+
"EnergyBalancerTs",
|
42
|
+
]
|
@@ -1,21 +1,23 @@
|
|
1
|
-
"""
|
2
|
-
Description
|
3
|
-
===========
|
4
|
-
|
5
|
-
Juham - Juha's Ultimate Home Automation classes
|
6
|
-
|
7
|
-
"""
|
8
|
-
|
9
|
-
from .energycostcalculator import EnergyCostCalculator
|
10
|
-
from .spothintafi import SpotHintaFi
|
11
|
-
from .watercirculator import WaterCirculator
|
12
|
-
from .
|
13
|
-
from .
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
"
|
18
|
-
"
|
19
|
-
"
|
20
|
-
"
|
21
|
-
|
1
|
+
"""
|
2
|
+
Description
|
3
|
+
===========
|
4
|
+
|
5
|
+
Juham - Juha's Ultimate Home Automation classes
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from .energycostcalculator import EnergyCostCalculator
|
10
|
+
from .spothintafi import SpotHintaFi
|
11
|
+
from .watercirculator import WaterCirculator
|
12
|
+
from .heatingoptimizer import HeatingOptimizer
|
13
|
+
from .energybalancer import EnergyBalancer
|
14
|
+
from .powermeter_simulator import PowerMeterSimulator
|
15
|
+
|
16
|
+
__all__ = [
|
17
|
+
"EnergyCostCalculator",
|
18
|
+
"HeatingOptimizer",
|
19
|
+
"SpotHintaFi",
|
20
|
+
"WaterCirculator",
|
21
|
+
"PowerMeterSimulator",
|
22
|
+
"EnergyBalancer",
|
23
|
+
]
|
@@ -0,0 +1,158 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Any, Dict
|
3
|
+
from typing_extensions import override
|
4
|
+
|
5
|
+
from juham_core import Juham, timestamp
|
6
|
+
from juham_core.timeutils import timestampstr, quantize
|
7
|
+
from masterpiece import MqttMsg
|
8
|
+
|
9
|
+
|
10
|
+
class EnergyBalancer(Juham):
|
11
|
+
"""The energy balancer monitors the balance between produced and consumed energy
|
12
|
+
within the balancing interval to determine if there is enough energy available for
|
13
|
+
a given energy-consuming device, such as heating radiators, to operate within the
|
14
|
+
remaining time of the interval.
|
15
|
+
|
16
|
+
Any number of energy-consuming devices can be connected to the energy balancer.
|
17
|
+
The energy balancer is typically used in conjunction with a power meter that reads
|
18
|
+
the total power consumption of the house. The energy balancer uses the power meter
|
19
|
+
"""
|
20
|
+
|
21
|
+
#: Description of the attribute
|
22
|
+
energy_balancing_interval: int = 3600
|
23
|
+
radiator_power: float = 3000
|
24
|
+
timezone: str = "Europe/Helsinki"
|
25
|
+
|
26
|
+
def __init__(self, name: str = "energybalancer") -> None:
|
27
|
+
"""Initialize the energy balancer.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
name (str): name of the heating radiator
|
31
|
+
power (float): power of the consumer in watts
|
32
|
+
"""
|
33
|
+
super().__init__(name)
|
34
|
+
|
35
|
+
self.topic_in_powerconsumption = self.make_topic_name("powerconsumption")
|
36
|
+
self.topic_in_net_energy_balance = self.make_topic_name("net_energy_balance")
|
37
|
+
self.topic_out_energybalance = self.make_topic_name("energybalance")
|
38
|
+
self.net_energy_balance: float = 0.0 # Energy balance in joules (watt-seconds)
|
39
|
+
self.net_energy_balance_ts: float = -1
|
40
|
+
self.needed_energy: float = self.energy_balancing_interval * self.radiator_power
|
41
|
+
self.net_energy_balancing_mode: bool = False
|
42
|
+
|
43
|
+
@override
|
44
|
+
def on_connect(self, client: object, userdata: Any, flags: int, rc: int) -> None:
|
45
|
+
super().on_connect(client, userdata, flags, rc)
|
46
|
+
if rc == 0:
|
47
|
+
self.subscribe(self.topic_in_net_energy_balance)
|
48
|
+
|
49
|
+
@override
|
50
|
+
def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None:
|
51
|
+
ts: float = timestamp()
|
52
|
+
|
53
|
+
if msg.topic == self.topic_in_net_energy_balance:
|
54
|
+
self.on_power(json.loads(msg.payload.decode()), ts)
|
55
|
+
else:
|
56
|
+
super().on_message(client, userdata, msg)
|
57
|
+
|
58
|
+
def on_power(self, m: dict[str, Any], ts: float) -> None:
|
59
|
+
"""Handle the power consumption. Read the current power balance and accumulate
|
60
|
+
to the net energy balance to reflect the energy produced (or consumed) within the
|
61
|
+
current time slot.
|
62
|
+
Args:
|
63
|
+
m (dict[str, Any]): power consumption message
|
64
|
+
ts (float): current time
|
65
|
+
"""
|
66
|
+
self.update_energy_balance(m["power"], ts)
|
67
|
+
|
68
|
+
def update_energy_balance(self, power: float, ts: float) -> None:
|
69
|
+
"""Update the current net net energy balance. The change in the balance is calculate the
|
70
|
+
energy balance, which the time elapsed since the last update, multiplied by the
|
71
|
+
power. Positive energy balance means we have produced energy that can be consumed
|
72
|
+
at the end of the interval. The target is to use all the energy produced during the
|
73
|
+
balancing interval. This method is typically called by the powermeter reading the
|
74
|
+
total power consumption of the house
|
75
|
+
|
76
|
+
Args:
|
77
|
+
power (float): power reading from the powermeter. Positive value means
|
78
|
+
energy produced, negative value means energy consumed. The value of 0 means
|
79
|
+
the house is not consuming or producing energy.
|
80
|
+
ts (float): current time in utc seconds
|
81
|
+
"""
|
82
|
+
|
83
|
+
# regardless of the mode, if we hit the end of the interval, reset the balance
|
84
|
+
quantized_ts: float = ts % self.energy_balancing_interval
|
85
|
+
if self.net_energy_balance_ts < 0:
|
86
|
+
self.net_energy_balance_ts = quantized_ts
|
87
|
+
self.needed_energy = self.energy_balancing_interval - quantized_ts
|
88
|
+
elif quantized_ts <= self.net_energy_balance_ts:
|
89
|
+
self.reset_net_energy_balance()
|
90
|
+
else:
|
91
|
+
# update the energy balance with the elapsed time and the power
|
92
|
+
elapsed_ts = quantized_ts - self.net_energy_balance_ts
|
93
|
+
balance: float = elapsed_ts * power # joules i.e. watt-seconds
|
94
|
+
self.net_energy_balance = self.net_energy_balance + balance
|
95
|
+
self.net_energy_balance_ts = quantized_ts
|
96
|
+
self.needed_energy = (
|
97
|
+
self.energy_balancing_interval - quantized_ts
|
98
|
+
) * self.radiator_power
|
99
|
+
|
100
|
+
if self.net_energy_balancing_mode:
|
101
|
+
if self.net_energy_balance <= 0:
|
102
|
+
# if we have used all the energy, disable the balancing mode
|
103
|
+
self.reset_net_energy_balance()
|
104
|
+
else:
|
105
|
+
if self.net_energy_balance >= self.needed_energy:
|
106
|
+
self.net_energy_balancing_mode = True
|
107
|
+
self.publish_energybalance(ts)
|
108
|
+
|
109
|
+
def consider_net_energy_balance(self, ts: float) -> bool:
|
110
|
+
"""Check if there is enough energy available for the consumer to heat
|
111
|
+
the water in the remaining time within the balancing interval, and switch
|
112
|
+
the balancing mode on if sufficient.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
ts (float): current time
|
116
|
+
|
117
|
+
Returns:
|
118
|
+
bool: true if production exceeds the consumption
|
119
|
+
"""
|
120
|
+
return self.net_energy_balancing_mode
|
121
|
+
|
122
|
+
def reset_net_energy_balance(self) -> None:
|
123
|
+
"""Reset the net energy balance at the end of the interval."""
|
124
|
+
self.net_energy_balance = 0.0
|
125
|
+
self.needed_energy = self.energy_balancing_interval * self.radiator_power
|
126
|
+
self.net_energy_balance_ts = 0
|
127
|
+
self.net_energy_balancing_mode = False
|
128
|
+
|
129
|
+
def activate_balancing_mode(self, ts: float) -> None:
|
130
|
+
"""Activate balancing mode when enough energy is available."""
|
131
|
+
self.net_energy_balancing_mode = True
|
132
|
+
self.info(
|
133
|
+
f"{int(self.net_energy_balance/3600)} Wh is enough to supply the radiator, enable"
|
134
|
+
)
|
135
|
+
|
136
|
+
def deactivate_balancing_mode(self) -> None:
|
137
|
+
"""Deactivate balancing mode when energy is depleted or interval ends."""
|
138
|
+
self.net_energy_balancing_mode = False
|
139
|
+
self.info("Balance used, or the end of the interval reached, disable")
|
140
|
+
self.net_energy_balance = 0.0 # Reset the energy balance at the interval's end
|
141
|
+
|
142
|
+
def publish_energybalance(self, ts: float) -> None:
|
143
|
+
"""Publish energy balance information.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
ts (float): current time
|
147
|
+
Returns:
|
148
|
+
dict: diagnostics information
|
149
|
+
"""
|
150
|
+
m: dict[str, Any] = {
|
151
|
+
"Unit": self.name,
|
152
|
+
"Mode": self.net_energy_balancing_mode,
|
153
|
+
"Rc": self.net_energy_balancing_mode,
|
154
|
+
"CurrentBalance": self.net_energy_balance,
|
155
|
+
"NeededBalance": self.needed_energy,
|
156
|
+
"Timestamp": ts,
|
157
|
+
}
|
158
|
+
self.publish(self.topic_out_energybalance, json.dumps(m))
|