juham-automation 0.0.12__py3-none-any.whl → 0.0.14__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.
@@ -1,38 +1,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 HotWaterOptimizer
14
- from .ts import EnergyCostCalculatorTs
15
- from .ts import ForecastTs
16
- from .ts import LogTs
17
- from .ts import PowerTs
18
- from .ts import PowerPlanTs
19
- from .ts import PowerMeterTs
20
- from .ts import ElectricityPriceTs
21
- from .japp import JApp
22
-
23
-
24
- __all__ = [
25
- "EnergyCostCalculator",
26
- "EnergyCostCalculatorTs",
27
- "ForecastTs",
28
- "HotWaterOptimizer",
29
- "LogTs",
30
- "PowerTs",
31
- "PowerPlanTs",
32
- "PowerMeterTs",
33
- "SpotHintaFi",
34
- "WaterCirculator",
35
- "JApp",
36
- "PowerMeterSimulator",
37
- "ElectricityPriceTs",
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 HotWaterOptimizer
14
+ from .ts import EnergyCostCalculatorTs
15
+ from .ts import ForecastTs
16
+ from .ts import LogTs
17
+ from .ts import PowerTs
18
+ from .ts import PowerPlanTs
19
+ from .ts import PowerMeterTs
20
+ from .ts import ElectricityPriceTs
21
+ from .japp import JApp
22
+
23
+
24
+ __all__ = [
25
+ "EnergyCostCalculator",
26
+ "EnergyCostCalculatorTs",
27
+ "ForecastTs",
28
+ "HotWaterOptimizer",
29
+ "LogTs",
30
+ "PowerTs",
31
+ "PowerPlanTs",
32
+ "PowerMeterTs",
33
+ "SpotHintaFi",
34
+ "WaterCirculator",
35
+ "JApp",
36
+ "PowerMeterSimulator",
37
+ "ElectricityPriceTs",
38
+ ]
@@ -1,21 +1,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 .hotwateroptimizer import HotWaterOptimizer
13
- from .powermeter_simulator import PowerMeterSimulator
14
-
15
- __all__ = [
16
- "EnergyCostCalculator",
17
- "HotWaterOptimizer",
18
- "SpotHintaFi",
19
- "WaterCirculator",
20
- "PowerMeterSimulator",
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 .hotwateroptimizer import HotWaterOptimizer
13
+ from .powermeter_simulator import PowerMeterSimulator
14
+
15
+ __all__ = [
16
+ "EnergyCostCalculator",
17
+ "HotWaterOptimizer",
18
+ "SpotHintaFi",
19
+ "WaterCirculator",
20
+ "PowerMeterSimulator",
21
+ ]
@@ -1,266 +1,266 @@
1
- from typing import Any
2
- from typing_extensions import override
3
- import json
4
- from masterpiece.mqtt import MqttMsg
5
- from juham_core import Juham
6
- from juham_core.timeutils import (
7
- elapsed_seconds_in_day,
8
- elapsed_seconds_in_hour,
9
- quantize,
10
- timestamp,
11
- )
12
-
13
-
14
- class EnergyCostCalculator(Juham):
15
- """The EnergyCostCalculator class calculates the net energy balance between produced
16
- and consumed energy for Time-Based Settlement (TBS). It performs the following functions:
17
-
18
- * Subscribes to 'spot' and 'power' MQTT topics.
19
- * Calculates the net energy and the rate of change of the net energy per hour and per day (24h)
20
- * Publishes the calculated values to the MQTT net energy balance topic.
21
- * Stores the data in a time series database.
22
-
23
- This information helps other home automation components optimize energy usage and
24
- minimize electricity bills.
25
- """
26
-
27
- _kwh_to_joule_coeff: float = 1000.0 * 3600
28
- _joule_to_kwh_coeff: float = 1.0 / _kwh_to_joule_coeff
29
-
30
- energy_balancing_interval: float = 3600
31
-
32
- def __init__(self, name: str = "ecc") -> None:
33
- super().__init__(name)
34
- self.current_ts: float = 0
35
- self.total_balance_hour: float = 0
36
- self.total_balance_day: float = 0
37
- self.net_energy_balance_cost_hour: float = 0
38
- self.net_energy_balance_cost_day: float = 0
39
- self.net_energy_balance_start_hour = elapsed_seconds_in_hour(timestamp())
40
- self.net_energy_balance_start_day = elapsed_seconds_in_day(timestamp())
41
- self.spots: list[dict[str, float]] = []
42
- self.init_topics()
43
-
44
- def init_topics(self) -> None:
45
- self.topic_in_spot = self.make_topic_name("spot")
46
- self.topic_in_powerconsumption = self.make_topic_name("powerconsumption")
47
- self.topic_out_net_energy_balance = self.make_topic_name("net_energy_balance")
48
- self.topic_out_energy_cost = self.make_topic_name("net_energy_cost")
49
-
50
- @override
51
- def on_connect(self, client: object, userdata: Any, flags: int, rc: int) -> None:
52
- super().on_connect(client, userdata, flags, rc)
53
- if rc == 0:
54
- self.subscribe(self.topic_in_spot)
55
- self.subscribe(self.topic_in_powerconsumption)
56
-
57
- @override
58
- def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None:
59
- ts_now = timestamp()
60
-
61
- m = json.loads(msg.payload.decode())
62
- if msg.topic == self.topic_in_spot:
63
- self.on_spot(m)
64
- elif msg.topic == self.topic_in_powerconsumption:
65
- self.on_powerconsumption(ts_now, m)
66
- else:
67
- self.error(f"Unknown event {msg.topic}")
68
-
69
- def on_spot(self, spot: dict[Any, Any]) -> None:
70
- """Stores the received per hour electricity prices to spots list.
71
-
72
- Args:
73
- spot (list): list of hourly spot prices
74
- """
75
-
76
- for s in spot:
77
- self.spots.append(
78
- {"Timestamp": s["Timestamp"], "PriceWithTax": s["PriceWithTax"]}
79
- )
80
-
81
- def map_kwh_prices_to_joules(self, price: float) -> float:
82
- """Convert the given electricity price in kWh to Watt seconds (J)
83
- Args:
84
- price (float): electricity price given as kWh
85
- Returns:
86
- Electricity price per watt second (J)
87
- """
88
- return price * self._joule_to_kwh_coeff
89
-
90
- def get_prices(self, ts_prev: float, ts_now: float) -> tuple[float, float]:
91
- """Fetch the electricity prices for the given two subsequent time
92
- stamps.
93
-
94
- Args:
95
- ts_prev (float): previous time
96
- ts_now (float): current time
97
- Returns:
98
- Electricity prices for the given interval
99
- """
100
- prev_price = None
101
- current_price = None
102
-
103
- for i in range(0, len(self.spots) - 1):
104
- r0 = self.spots[i]
105
- r1 = self.spots[i + 1]
106
- ts0 = r0["Timestamp"]
107
- ts1 = r1["Timestamp"]
108
- if ts_prev >= ts0 and ts_prev <= ts1:
109
- prev_price = r0["PriceWithTax"]
110
- if ts_now >= ts0 and ts_now <= ts1:
111
- current_price = r0["PriceWithTax"]
112
- if prev_price is not None and current_price is not None:
113
- return prev_price, current_price
114
- self.error("PANIC: run out of spot prices")
115
- return 0.0, 0.0
116
-
117
- def calculate_net_energy_cost(
118
- self, ts_prev: float, ts_now: float, energy: float
119
- ) -> float:
120
- """Given time interval as start and stop Calculate the cost over the
121
- given time period. Positive values indicate revenue, negative cost.
122
-
123
- Args:
124
- ts_prev (timestamp): beginning time stamp of the interval
125
- ts_now (timestamp): end of the interval
126
- energy (float): energy consumed during the time interval
127
- Returns:
128
- Cost or revenue
129
- """
130
- cost: float = 0
131
- prev = ts_prev
132
- while prev < ts_now:
133
- elapsed_seconds: float = ts_now - prev
134
- if elapsed_seconds > self.energy_balancing_interval:
135
- elapsed_seconds = self.energy_balancing_interval
136
- now = prev + elapsed_seconds
137
- start_per_kwh, stop_per_kwh = self.get_prices(prev, now)
138
- start_price = self.map_kwh_prices_to_joules(start_per_kwh)
139
- stop_price = self.map_kwh_prices_to_joules(stop_per_kwh)
140
- if abs(stop_price - start_price) < 1e-24:
141
- cost = cost + energy * elapsed_seconds * start_price
142
- else:
143
- # interpolate cost over energy balancing interval boundary
144
- elapsed = now - prev
145
- if elapsed < 0.00001:
146
- return 0.0
147
- ts_0 = quantize(self.energy_balancing_interval, now)
148
- t1 = (ts_0 - prev) / elapsed
149
- t2 = (now - ts_0) / elapsed
150
- cost = (
151
- cost
152
- + energy
153
- * ((1.0 - t1) * start_price + t2 * stop_price)
154
- * elapsed_seconds
155
- )
156
-
157
- prev = prev + elapsed_seconds
158
- return cost
159
-
160
- def on_powerconsumption(self, ts_now: float, m: dict[Any, Any]) -> None:
161
- """Calculate net energy cost and update the hourly consumption attribute
162
- accordingly.
163
-
164
- Args:
165
- ts_now (float): time stamp of the energy consumed
166
- m (dict): Juham MQTT message holding energy reading
167
- """
168
- power = m["real_total"]
169
- if not self.spots:
170
- self.info("Waiting for electricity prices...")
171
- elif self.current_ts == 0:
172
- self.net_energy_balance_cost_hour = 0.0
173
- self.net_energy_balance_cost_day = 0.0
174
- self.current_ts = ts_now
175
- self.net_energy_balance_start_hour = quantize(
176
- self.energy_balancing_interval, ts_now
177
- )
178
- else:
179
- # calculate cost of energy consumed/produced
180
- dp: float = self.calculate_net_energy_cost(self.current_ts, ts_now, power)
181
- self.net_energy_balance_cost_hour = self.net_energy_balance_cost_hour + dp
182
- self.net_energy_balance_cost_day = self.net_energy_balance_cost_day + dp
183
-
184
- # calculate and publish energy balance
185
- dt = ts_now - self.current_ts # time elapsed since previous call
186
- balance = dt * power # energy consumed/produced in this slot in Joules
187
- self.total_balance_hour = (
188
- self.total_balance_hour + balance * self._joule_to_kwh_coeff
189
- )
190
- self.total_balance_day = (
191
- self.total_balance_day + balance * self._joule_to_kwh_coeff
192
- )
193
- self.publish_net_energy_balance(ts_now, self.name, balance, power)
194
- self.publish_energy_cost(
195
- ts_now,
196
- self.name,
197
- self.net_energy_balance_cost_hour,
198
- self.net_energy_balance_cost_day,
199
- )
200
-
201
- # Check if the current energy balancing interval has ended
202
- # If so, reset the net_energy_balance attribute for the next interval
203
- if (
204
- ts_now - self.net_energy_balance_start_hour
205
- > self.energy_balancing_interval
206
- ):
207
- # publish average energy cost per hour
208
- if abs(self.total_balance_hour) > 0:
209
- msg = {
210
- "name": self.name,
211
- "average_hour": self.net_energy_balance_cost_hour
212
- / self.total_balance_hour,
213
- "ts": ts_now,
214
- }
215
- self.publish(self.topic_out_energy_cost, json.dumps(msg), 0, False)
216
-
217
- # reset for the next hour
218
- self.total_balance_hour = 0
219
- self.net_energy_balance_cost_hour = 0.0
220
- self.net_energy_balance_start_hour = ts_now
221
-
222
- if ts_now - self.net_energy_balance_start_day > 24 * 3600:
223
- if abs(self.total_balance_day) > 0:
224
- msg = {
225
- "name": self.name,
226
- "average_day": self.net_energy_balance_cost_day
227
- / self.total_balance_day,
228
- "ts": ts_now,
229
- }
230
- self.publish(self.topic_out_energy_cost, json.dumps(msg), 0, False)
231
- # reset for the next day
232
- self.total_balance_day = 0
233
- self.net_energy_balance_cost_day = 0.0
234
- self.net_energy_balance_start_day = ts_now
235
-
236
- self.current_ts = ts_now
237
-
238
- def publish_net_energy_balance(
239
- self, ts_now: float, site: str, energy: float, power: float
240
- ) -> None:
241
- """Publish the net energy balance for the current energy balancing interval, as well as
242
- the real-time power at which energy is currently being produced or consumed (the
243
- rate of change of net energy).
244
-
245
- Args:
246
- ts_now (float): timestamp
247
- site (str): site
248
- energy (float): cost or revenue.
249
- power (float) : momentary power (rage of change of energy)
250
- """
251
- msg = {"site": site, "power": power, "energy": energy, "ts": ts_now}
252
- self.publish(self.topic_out_net_energy_balance, json.dumps(msg), 1, True)
253
-
254
- def publish_energy_cost(
255
- self, ts_now: float, site: str, cost_hour: float, cost_day: float
256
- ) -> None:
257
- """Publish daily and hourly energy cost/revenue
258
-
259
- Args:
260
- ts_now (float): timestamp
261
- site (str): site
262
- cost_hour (float): cost or revenue per hour.
263
- cost_day (float) : cost or revenue per day
264
- """
265
- msg = {"name": site, "cost_hour": cost_hour, "cost_day": cost_day, "ts": ts_now}
266
- self.publish(self.topic_out_energy_cost, json.dumps(msg), 1, True)
1
+ from typing import Any
2
+ from typing_extensions import override
3
+ import json
4
+ from masterpiece.mqtt import MqttMsg
5
+ from juham_core import Juham
6
+ from juham_core.timeutils import (
7
+ elapsed_seconds_in_day,
8
+ elapsed_seconds_in_hour,
9
+ quantize,
10
+ timestamp,
11
+ )
12
+
13
+
14
+ class EnergyCostCalculator(Juham):
15
+ """The EnergyCostCalculator class calculates the net energy balance between produced
16
+ and consumed energy for Time-Based Settlement (TBS). It performs the following functions:
17
+
18
+ * Subscribes to 'spot' and 'power' MQTT topics.
19
+ * Calculates the net energy and the rate of change of the net energy per hour and per day (24h)
20
+ * Publishes the calculated values to the MQTT net energy balance topic.
21
+ * Stores the data in a time series database.
22
+
23
+ This information helps other home automation components optimize energy usage and
24
+ minimize electricity bills.
25
+ """
26
+
27
+ _kwh_to_joule_coeff: float = 1000.0 * 3600
28
+ _joule_to_kwh_coeff: float = 1.0 / _kwh_to_joule_coeff
29
+
30
+ energy_balancing_interval: float = 3600
31
+
32
+ def __init__(self, name: str = "ecc") -> None:
33
+ super().__init__(name)
34
+ self.current_ts: float = 0
35
+ self.total_balance_hour: float = 0
36
+ self.total_balance_day: float = 0
37
+ self.net_energy_balance_cost_hour: float = 0
38
+ self.net_energy_balance_cost_day: float = 0
39
+ self.net_energy_balance_start_hour = elapsed_seconds_in_hour(timestamp())
40
+ self.net_energy_balance_start_day = elapsed_seconds_in_day(timestamp())
41
+ self.spots: list[dict[str, float]] = []
42
+ self.init_topics()
43
+
44
+ def init_topics(self) -> None:
45
+ self.topic_in_spot = self.make_topic_name("spot")
46
+ self.topic_in_powerconsumption = self.make_topic_name("powerconsumption")
47
+ self.topic_out_net_energy_balance = self.make_topic_name("net_energy_balance")
48
+ self.topic_out_energy_cost = self.make_topic_name("net_energy_cost")
49
+
50
+ @override
51
+ def on_connect(self, client: object, userdata: Any, flags: int, rc: int) -> None:
52
+ super().on_connect(client, userdata, flags, rc)
53
+ if rc == 0:
54
+ self.subscribe(self.topic_in_spot)
55
+ self.subscribe(self.topic_in_powerconsumption)
56
+
57
+ @override
58
+ def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None:
59
+ ts_now = timestamp()
60
+
61
+ m = json.loads(msg.payload.decode())
62
+ if msg.topic == self.topic_in_spot:
63
+ self.on_spot(m)
64
+ elif msg.topic == self.topic_in_powerconsumption:
65
+ self.on_powerconsumption(ts_now, m)
66
+ else:
67
+ self.error(f"Unknown event {msg.topic}")
68
+
69
+ def on_spot(self, spot: dict[Any, Any]) -> None:
70
+ """Stores the received per hour electricity prices to spots list.
71
+
72
+ Args:
73
+ spot (list): list of hourly spot prices
74
+ """
75
+
76
+ for s in spot:
77
+ self.spots.append(
78
+ {"Timestamp": s["Timestamp"], "PriceWithTax": s["PriceWithTax"]}
79
+ )
80
+
81
+ def map_kwh_prices_to_joules(self, price: float) -> float:
82
+ """Convert the given electricity price in kWh to Watt seconds (J)
83
+ Args:
84
+ price (float): electricity price given as kWh
85
+ Returns:
86
+ Electricity price per watt second (J)
87
+ """
88
+ return price * self._joule_to_kwh_coeff
89
+
90
+ def get_prices(self, ts_prev: float, ts_now: float) -> tuple[float, float]:
91
+ """Fetch the electricity prices for the given two subsequent time
92
+ stamps.
93
+
94
+ Args:
95
+ ts_prev (float): previous time
96
+ ts_now (float): current time
97
+ Returns:
98
+ Electricity prices for the given interval
99
+ """
100
+ prev_price = None
101
+ current_price = None
102
+
103
+ for i in range(0, len(self.spots) - 1):
104
+ r0 = self.spots[i]
105
+ r1 = self.spots[i + 1]
106
+ ts0 = r0["Timestamp"]
107
+ ts1 = r1["Timestamp"]
108
+ if ts_prev >= ts0 and ts_prev <= ts1:
109
+ prev_price = r0["PriceWithTax"]
110
+ if ts_now >= ts0 and ts_now <= ts1:
111
+ current_price = r0["PriceWithTax"]
112
+ if prev_price is not None and current_price is not None:
113
+ return prev_price, current_price
114
+ self.error("PANIC: run out of spot prices")
115
+ return 0.0, 0.0
116
+
117
+ def calculate_net_energy_cost(
118
+ self, ts_prev: float, ts_now: float, energy: float
119
+ ) -> float:
120
+ """Given time interval as start and stop Calculate the cost over the
121
+ given time period. Positive values indicate revenue, negative cost.
122
+
123
+ Args:
124
+ ts_prev (timestamp): beginning time stamp of the interval
125
+ ts_now (timestamp): end of the interval
126
+ energy (float): energy consumed during the time interval
127
+ Returns:
128
+ Cost or revenue
129
+ """
130
+ cost: float = 0
131
+ prev = ts_prev
132
+ while prev < ts_now:
133
+ elapsed_seconds: float = ts_now - prev
134
+ if elapsed_seconds > self.energy_balancing_interval:
135
+ elapsed_seconds = self.energy_balancing_interval
136
+ now = prev + elapsed_seconds
137
+ start_per_kwh, stop_per_kwh = self.get_prices(prev, now)
138
+ start_price = self.map_kwh_prices_to_joules(start_per_kwh)
139
+ stop_price = self.map_kwh_prices_to_joules(stop_per_kwh)
140
+ if abs(stop_price - start_price) < 1e-24:
141
+ cost = cost + energy * elapsed_seconds * start_price
142
+ else:
143
+ # interpolate cost over energy balancing interval boundary
144
+ elapsed = now - prev
145
+ if elapsed < 0.00001:
146
+ return 0.0
147
+ ts_0 = quantize(self.energy_balancing_interval, now)
148
+ t1 = (ts_0 - prev) / elapsed
149
+ t2 = (now - ts_0) / elapsed
150
+ cost = (
151
+ cost
152
+ + energy
153
+ * ((1.0 - t1) * start_price + t2 * stop_price)
154
+ * elapsed_seconds
155
+ )
156
+
157
+ prev = prev + elapsed_seconds
158
+ return cost
159
+
160
+ def on_powerconsumption(self, ts_now: float, m: dict[Any, Any]) -> None:
161
+ """Calculate net energy cost and update the hourly consumption attribute
162
+ accordingly.
163
+
164
+ Args:
165
+ ts_now (float): time stamp of the energy consumed
166
+ m (dict): Juham MQTT message holding energy reading
167
+ """
168
+ power = m["real_total"]
169
+ if not self.spots:
170
+ self.info("Waiting for electricity prices...")
171
+ elif self.current_ts == 0:
172
+ self.net_energy_balance_cost_hour = 0.0
173
+ self.net_energy_balance_cost_day = 0.0
174
+ self.current_ts = ts_now
175
+ self.net_energy_balance_start_hour = quantize(
176
+ self.energy_balancing_interval, ts_now
177
+ )
178
+ else:
179
+ # calculate cost of energy consumed/produced
180
+ dp: float = self.calculate_net_energy_cost(self.current_ts, ts_now, power)
181
+ self.net_energy_balance_cost_hour = self.net_energy_balance_cost_hour + dp
182
+ self.net_energy_balance_cost_day = self.net_energy_balance_cost_day + dp
183
+
184
+ # calculate and publish energy balance
185
+ dt = ts_now - self.current_ts # time elapsed since previous call
186
+ balance = dt * power # energy consumed/produced in this slot in Joules
187
+ self.total_balance_hour = (
188
+ self.total_balance_hour + balance * self._joule_to_kwh_coeff
189
+ )
190
+ self.total_balance_day = (
191
+ self.total_balance_day + balance * self._joule_to_kwh_coeff
192
+ )
193
+ self.publish_net_energy_balance(ts_now, self.name, balance, power)
194
+ self.publish_energy_cost(
195
+ ts_now,
196
+ self.name,
197
+ self.net_energy_balance_cost_hour,
198
+ self.net_energy_balance_cost_day,
199
+ )
200
+
201
+ # Check if the current energy balancing interval has ended
202
+ # If so, reset the net_energy_balance attribute for the next interval
203
+ if (
204
+ ts_now - self.net_energy_balance_start_hour
205
+ > self.energy_balancing_interval
206
+ ):
207
+ # publish average energy cost per hour
208
+ if abs(self.total_balance_hour) > 0:
209
+ msg = {
210
+ "name": self.name,
211
+ "average_hour": self.net_energy_balance_cost_hour
212
+ / self.total_balance_hour,
213
+ "ts": ts_now,
214
+ }
215
+ self.publish(self.topic_out_energy_cost, json.dumps(msg), 0, False)
216
+
217
+ # reset for the next hour
218
+ self.total_balance_hour = 0
219
+ self.net_energy_balance_cost_hour = 0.0
220
+ self.net_energy_balance_start_hour = ts_now
221
+
222
+ if ts_now - self.net_energy_balance_start_day > 24 * 3600:
223
+ if abs(self.total_balance_day) > 0:
224
+ msg = {
225
+ "name": self.name,
226
+ "average_day": self.net_energy_balance_cost_day
227
+ / self.total_balance_day,
228
+ "ts": ts_now,
229
+ }
230
+ self.publish(self.topic_out_energy_cost, json.dumps(msg), 0, False)
231
+ # reset for the next day
232
+ self.total_balance_day = 0
233
+ self.net_energy_balance_cost_day = 0.0
234
+ self.net_energy_balance_start_day = ts_now
235
+
236
+ self.current_ts = ts_now
237
+
238
+ def publish_net_energy_balance(
239
+ self, ts_now: float, site: str, energy: float, power: float
240
+ ) -> None:
241
+ """Publish the net energy balance for the current energy balancing interval, as well as
242
+ the real-time power at which energy is currently being produced or consumed (the
243
+ rate of change of net energy).
244
+
245
+ Args:
246
+ ts_now (float): timestamp
247
+ site (str): site
248
+ energy (float): cost or revenue.
249
+ power (float) : momentary power (rage of change of energy)
250
+ """
251
+ msg = {"site": site, "power": power, "energy": energy, "ts": ts_now}
252
+ self.publish(self.topic_out_net_energy_balance, json.dumps(msg), 1, True)
253
+
254
+ def publish_energy_cost(
255
+ self, ts_now: float, site: str, cost_hour: float, cost_day: float
256
+ ) -> None:
257
+ """Publish daily and hourly energy cost/revenue
258
+
259
+ Args:
260
+ ts_now (float): timestamp
261
+ site (str): site
262
+ cost_hour (float): cost or revenue per hour.
263
+ cost_day (float) : cost or revenue per day
264
+ """
265
+ msg = {"name": site, "cost_hour": cost_hour, "cost_day": cost_day, "ts": ts_now}
266
+ self.publish(self.topic_out_energy_cost, json.dumps(msg), 1, True)