juham-automation 0.1.1__py3-none-any.whl → 0.1.3__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/energycostcalculator.py +79 -59
- juham_automation/automation/heatingoptimizer.py +18 -19
- {juham_automation-0.1.1.dist-info → juham_automation-0.1.3.dist-info}/METADATA +5 -5
- {juham_automation-0.1.1.dist-info → juham_automation-0.1.3.dist-info}/RECORD +8 -8
- {juham_automation-0.1.1.dist-info → juham_automation-0.1.3.dist-info}/WHEEL +0 -0
- {juham_automation-0.1.1.dist-info → juham_automation-0.1.3.dist-info}/entry_points.txt +0 -0
- {juham_automation-0.1.1.dist-info → juham_automation-0.1.3.dist-info}/licenses/LICENSE.rst +0 -0
- {juham_automation-0.1.1.dist-info → juham_automation-0.1.3.dist-info}/top_level.txt +0 -0
|
@@ -6,6 +6,7 @@ from juham_core import Juham
|
|
|
6
6
|
from juham_core.timeutils import (
|
|
7
7
|
elapsed_seconds_in_day,
|
|
8
8
|
elapsed_seconds_in_hour,
|
|
9
|
+
elapsed_seconds_in_interval,
|
|
9
10
|
quantize,
|
|
10
11
|
timestamp,
|
|
11
12
|
)
|
|
@@ -28,17 +29,20 @@ class EnergyCostCalculator(Juham):
|
|
|
28
29
|
_kwh_to_joule_coeff: float = 1000.0 * 3600
|
|
29
30
|
_joule_to_kwh_coeff: float = 1.0 / _kwh_to_joule_coeff
|
|
30
31
|
|
|
31
|
-
energy_balancing_interval:
|
|
32
|
+
energy_balancing_interval: int = 900 # in seconds (15 minutes)
|
|
32
33
|
|
|
33
34
|
def __init__(self, name: str = "ecc") -> None:
|
|
34
35
|
super().__init__(name)
|
|
35
36
|
self.current_ts: float = 0
|
|
37
|
+
self.total_balance_interval : float = 0
|
|
36
38
|
self.total_balance_hour: float = 0
|
|
37
39
|
self.total_balance_day: float = 0
|
|
40
|
+
self.net_energy_balance_cost_interval: float = 0
|
|
38
41
|
self.net_energy_balance_cost_hour: float = 0
|
|
39
42
|
self.net_energy_balance_cost_day: float = 0
|
|
40
|
-
self.
|
|
41
|
-
self.
|
|
43
|
+
self.net_energy_balance_start_interval : float = elapsed_seconds_in_interval(timestamp(), self.energy_balancing_interval)
|
|
44
|
+
self.net_energy_balance_start_hour : float = elapsed_seconds_in_hour(timestamp())
|
|
45
|
+
self.net_energy_balance_start_day : float = elapsed_seconds_in_day(timestamp())
|
|
42
46
|
self.spots: list[dict[str, float]] = []
|
|
43
47
|
self.init_topics()
|
|
44
48
|
|
|
@@ -88,76 +92,68 @@ class EnergyCostCalculator(Juham):
|
|
|
88
92
|
"""
|
|
89
93
|
return price * self._joule_to_kwh_coeff
|
|
90
94
|
|
|
91
|
-
def get_prices(self, ts_prev: float, ts_now: float) -> tuple[float, float]:
|
|
92
|
-
"""Fetch the electricity prices for the given two subsequent time
|
|
93
|
-
stamps.
|
|
94
95
|
|
|
96
|
+
|
|
97
|
+
def get_price_at(self, ts: float) -> float:
|
|
98
|
+
"""Return the spot price applicable at the given timestamp.
|
|
95
99
|
Args:
|
|
96
|
-
|
|
97
|
-
ts_now (float): current time
|
|
100
|
+
ts (float): current time
|
|
98
101
|
Returns:
|
|
99
102
|
Electricity prices for the given interval
|
|
100
103
|
"""
|
|
101
|
-
prev_price = None
|
|
102
|
-
current_price = None
|
|
103
104
|
|
|
104
105
|
for i in range(0, len(self.spots) - 1):
|
|
105
106
|
r0 = self.spots[i]
|
|
106
107
|
r1 = self.spots[i + 1]
|
|
107
108
|
ts0 = r0["Timestamp"]
|
|
108
109
|
ts1 = r1["Timestamp"]
|
|
109
|
-
if
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
if ts >= ts0 and ts < ts1:
|
|
111
|
+
return r0["PriceWithTax"]
|
|
112
|
+
|
|
113
|
+
# If timestamp is exactly equal to the last spot timestamp or beyond
|
|
114
|
+
if ts >= self.spots[-1]["Timestamp"]:
|
|
115
|
+
return self.spots[-1]["PriceWithTax"]
|
|
116
|
+
|
|
117
|
+
self.error(f"PANIC: Timestamp {ts} out of bounds for spot price lookup")
|
|
118
|
+
return 0.0
|
|
119
|
+
|
|
120
|
+
|
|
117
121
|
|
|
118
122
|
def calculate_net_energy_cost(
|
|
119
123
|
self, ts_prev: float, ts_now: float, energy: float
|
|
120
124
|
) -> float:
|
|
121
|
-
"""
|
|
122
|
-
|
|
125
|
+
"""
|
|
126
|
+
Calculate the cost (or revenue) of energy consumed/produced over the given time interval.
|
|
127
|
+
Positive values indicate revenue, negative values indicate cost.
|
|
123
128
|
|
|
124
129
|
Args:
|
|
125
|
-
ts_prev (
|
|
126
|
-
ts_now (
|
|
127
|
-
energy (float):
|
|
130
|
+
ts_prev (float): Start timestamp of the interval
|
|
131
|
+
ts_now (float): End timestamp of the interval
|
|
132
|
+
energy (float): Energy consumed during the interval (in watts or Joules)
|
|
133
|
+
|
|
128
134
|
Returns:
|
|
129
|
-
|
|
135
|
+
float: Total cost/revenue for the interval
|
|
130
136
|
"""
|
|
131
|
-
cost
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
ts_0 = quantize(self.energy_balancing_interval, now)
|
|
149
|
-
t1 = (ts_0 - prev) / elapsed
|
|
150
|
-
t2 = (now - ts_0) / elapsed
|
|
151
|
-
cost = (
|
|
152
|
-
cost
|
|
153
|
-
+ energy
|
|
154
|
-
* ((1.0 - t1) * start_price + t2 * stop_price)
|
|
155
|
-
* elapsed_seconds
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
prev = prev + elapsed_seconds
|
|
137
|
+
cost = 0.0
|
|
138
|
+
current = ts_prev
|
|
139
|
+
interval = self.energy_balancing_interval
|
|
140
|
+
|
|
141
|
+
while current < ts_now:
|
|
142
|
+
next_ts = min(ts_now, current + interval)
|
|
143
|
+
# Get spot price at start and end of interval
|
|
144
|
+
price_start = self.map_kwh_prices_to_joules(self.get_price_at(current))
|
|
145
|
+
price_end = self.map_kwh_prices_to_joules(self.get_price_at(next_ts))
|
|
146
|
+
|
|
147
|
+
# Trapezoidal integration: average price over interval
|
|
148
|
+
avg_price = (price_start + price_end) / 2.0
|
|
149
|
+
dt = next_ts - current
|
|
150
|
+
cost += energy * avg_price * dt
|
|
151
|
+
|
|
152
|
+
current = next_ts
|
|
153
|
+
|
|
159
154
|
return cost
|
|
160
155
|
|
|
156
|
+
|
|
161
157
|
def on_powerconsumption(self, ts_now: float, m: dict[Any, Any]) -> None:
|
|
162
158
|
"""Calculate net energy cost and update the hourly consumption attribute
|
|
163
159
|
accordingly.
|
|
@@ -170,21 +166,29 @@ class EnergyCostCalculator(Juham):
|
|
|
170
166
|
if not self.spots:
|
|
171
167
|
self.info("Waiting for electricity prices...")
|
|
172
168
|
elif self.current_ts == 0:
|
|
169
|
+
self.net_energy_balance_cost_interval = 0.0
|
|
173
170
|
self.net_energy_balance_cost_hour = 0.0
|
|
174
171
|
self.net_energy_balance_cost_day = 0.0
|
|
175
172
|
self.current_ts = ts_now
|
|
176
173
|
self.net_energy_balance_start_hour = quantize(
|
|
174
|
+
3600, ts_now
|
|
175
|
+
)
|
|
176
|
+
self.net_energy_balance_start_interval = quantize(
|
|
177
177
|
self.energy_balancing_interval, ts_now
|
|
178
178
|
)
|
|
179
179
|
else:
|
|
180
180
|
# calculate cost of energy consumed/produced
|
|
181
181
|
dp: float = self.calculate_net_energy_cost(self.current_ts, ts_now, power)
|
|
182
|
+
self.net_energy_balance_cost_interval = self.net_energy_balance_cost_interval + dp
|
|
182
183
|
self.net_energy_balance_cost_hour = self.net_energy_balance_cost_hour + dp
|
|
183
184
|
self.net_energy_balance_cost_day = self.net_energy_balance_cost_day + dp
|
|
184
185
|
|
|
185
186
|
# calculate and publish energy balance
|
|
186
187
|
dt = ts_now - self.current_ts # time elapsed since previous call
|
|
187
188
|
balance = dt * power # energy consumed/produced in this slot in Joules
|
|
189
|
+
self.total_balance_interval = (
|
|
190
|
+
self.total_balance_interval + balance * self._joule_to_kwh_coeff
|
|
191
|
+
)
|
|
188
192
|
self.total_balance_hour = (
|
|
189
193
|
self.total_balance_hour + balance * self._joule_to_kwh_coeff
|
|
190
194
|
)
|
|
@@ -195,16 +199,32 @@ class EnergyCostCalculator(Juham):
|
|
|
195
199
|
self.publish_energy_cost(
|
|
196
200
|
ts_now,
|
|
197
201
|
self.name,
|
|
202
|
+
self.net_energy_balance_cost_interval,
|
|
198
203
|
self.net_energy_balance_cost_hour,
|
|
199
204
|
self.net_energy_balance_cost_day,
|
|
200
205
|
)
|
|
201
206
|
|
|
202
207
|
# Check if the current energy balancing interval has ended
|
|
203
208
|
# If so, reset the net_energy_balance attribute for the next interval
|
|
204
|
-
if
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
if ts_now - self.net_energy_balance_start_hour > self.energy_balancing_interval:
|
|
210
|
+
# publish average energy cost per hour
|
|
211
|
+
if abs(self.total_balance_interval) > 0:
|
|
212
|
+
msg = {
|
|
213
|
+
"name": self.name,
|
|
214
|
+
"average_interval": self.net_energy_balance_cost_interval
|
|
215
|
+
/ self.total_balance_interval,
|
|
216
|
+
"ts": ts_now,
|
|
217
|
+
}
|
|
218
|
+
self.publish(self.topic_out_energy_cost, json.dumps(msg), 0, False)
|
|
219
|
+
|
|
220
|
+
# reset for the next hour
|
|
221
|
+
self.total_balance_interval = 0
|
|
222
|
+
self.net_energy_balance_cost_interval = 0.0
|
|
223
|
+
self.net_energy_balance_start_interval = ts_now
|
|
224
|
+
|
|
225
|
+
# Check if the current energy balancing interval has ended
|
|
226
|
+
# If so, reset the net_energy_balance attribute for the next interval
|
|
227
|
+
if ts_now - self.net_energy_balance_start_hour > 3600:
|
|
208
228
|
# publish average energy cost per hour
|
|
209
229
|
if abs(self.total_balance_hour) > 0:
|
|
210
230
|
msg = {
|
|
@@ -253,9 +273,9 @@ class EnergyCostCalculator(Juham):
|
|
|
253
273
|
self.publish(self.topic_out_net_energy_balance, json.dumps(msg), 1, True)
|
|
254
274
|
|
|
255
275
|
def publish_energy_cost(
|
|
256
|
-
self, ts_now: float, site: str, cost_hour: float, cost_day: float
|
|
276
|
+
self, ts_now: float, site: str, cost_interval : float, cost_hour: float, cost_day: float
|
|
257
277
|
) -> None:
|
|
258
|
-
"""Publish daily and
|
|
278
|
+
"""Publish daily, hourly and per interval energy cost/revenue
|
|
259
279
|
|
|
260
280
|
Args:
|
|
261
281
|
ts_now (float): timestamp
|
|
@@ -263,5 +283,5 @@ class EnergyCostCalculator(Juham):
|
|
|
263
283
|
cost_hour (float): cost or revenue per hour.
|
|
264
284
|
cost_day (float) : cost or revenue per day
|
|
265
285
|
"""
|
|
266
|
-
msg = {"name": site, "cost_hour": cost_hour, "cost_day": cost_day, "ts": ts_now}
|
|
286
|
+
msg = {"name": site, "cost_interval": cost_interval, "cost_hour": cost_hour, "cost_day": cost_day, "ts": ts_now}
|
|
267
287
|
self.publish(self.topic_out_energy_cost, json.dumps(msg), 1, True)
|
|
@@ -33,7 +33,7 @@ class HeatingOptimizer(Juham):
|
|
|
33
33
|
the hour is free. The UOI threshold determines the slots that are allowed to be consumed.
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
-
energy_balancing_interval: float =
|
|
36
|
+
energy_balancing_interval: float = 900
|
|
37
37
|
"""Energy balancing interval, as regulated by the industry/converment. In seconds"""
|
|
38
38
|
|
|
39
39
|
radiator_power: float = 6000 # W
|
|
@@ -61,18 +61,18 @@ class HeatingOptimizer(Juham):
|
|
|
61
61
|
"""Weight determining how large a share of the time slot a consumer receives compared to others ."""
|
|
62
62
|
|
|
63
63
|
temperature_limits: dict[int, tuple[float, float]] = {
|
|
64
|
-
1: (
|
|
65
|
-
2: (
|
|
66
|
-
3: (
|
|
67
|
-
4: (
|
|
68
|
-
5: (
|
|
69
|
-
6: (
|
|
70
|
-
7: (
|
|
71
|
-
8: (
|
|
72
|
-
9: (
|
|
73
|
-
10: (
|
|
74
|
-
11: (
|
|
75
|
-
12: (
|
|
64
|
+
1: (60.0, 65.0), # January
|
|
65
|
+
2: (55.0, 65.0), # February
|
|
66
|
+
3: (50.0, 64.0), # March
|
|
67
|
+
4: (20.0, 50.0), # April
|
|
68
|
+
5: (10.0, 40.0), # May
|
|
69
|
+
6: (10.0, 38.0), # June
|
|
70
|
+
7: (10.0, 38.0), # July
|
|
71
|
+
8: (35.0, 40.0), # August
|
|
72
|
+
9: (40.0, 50.0), # September
|
|
73
|
+
10: (45.0, 55.0), # October
|
|
74
|
+
11: (50.0, 58.0), # November
|
|
75
|
+
12: (55.0, 62.0), # December
|
|
76
76
|
}
|
|
77
77
|
"""Temperature limits for each month. The minimum temperature is maintained regardless of the cost.
|
|
78
78
|
The limits are defined as a dictionary where the keys are month numbers (1-12)
|
|
@@ -128,9 +128,8 @@ class HeatingOptimizer(Juham):
|
|
|
128
128
|
self.topic_out_energybalance = self.make_topic_name("energybalance/consumers")
|
|
129
129
|
self.topic_out_power = self.make_topic_name("power")
|
|
130
130
|
|
|
131
|
-
self.current_temperature = 100
|
|
132
|
-
self.
|
|
133
|
-
self.current_relay_state = -1
|
|
131
|
+
self.current_temperature : float = 100.0
|
|
132
|
+
self.current_relay_state : int = -1
|
|
134
133
|
self.heating_plan: list[dict[str, int]] = []
|
|
135
134
|
self.power_plan: list[dict[str, Any]] = []
|
|
136
135
|
self.ranked_spot_prices: list[dict[Any, Any]] = []
|
|
@@ -195,7 +194,7 @@ class HeatingOptimizer(Juham):
|
|
|
195
194
|
ranked_hours: list[dict[str, Any]] = []
|
|
196
195
|
for h in sh:
|
|
197
196
|
utc_ts = h["Timestamp"]
|
|
198
|
-
if utc_ts
|
|
197
|
+
if utc_ts >= ts_utc_now:
|
|
199
198
|
ranked_hours.append(h)
|
|
200
199
|
|
|
201
200
|
return ranked_hours
|
|
@@ -268,7 +267,7 @@ class HeatingOptimizer(Juham):
|
|
|
268
267
|
def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None:
|
|
269
268
|
m = None
|
|
270
269
|
ts: float = timestamp()
|
|
271
|
-
ts_utc_quantized: float = quantize(
|
|
270
|
+
ts_utc_quantized: float = quantize(self.energy_balancing_interval, ts - self.energy_balancing_interval)
|
|
272
271
|
if msg.topic == self.topic_in_spot:
|
|
273
272
|
self.on_spot(json.loads(msg.payload.decode()), ts_utc_quantized)
|
|
274
273
|
return
|
|
@@ -496,7 +495,7 @@ class HeatingOptimizer(Juham):
|
|
|
496
495
|
Returns:
|
|
497
496
|
list: list of utilization entries
|
|
498
497
|
"""
|
|
499
|
-
ts_utc_quantized = quantize(
|
|
498
|
+
ts_utc_quantized = quantize(self.energy_balancing_interval, timestamp() - self.energy_balancing_interval)
|
|
500
499
|
starts: str = timestampstr(ts_utc_quantized)
|
|
501
500
|
self.info(
|
|
502
501
|
f"{self.name} created power plan starting at {starts} with {len(self.ranked_spot_prices)} hours of spot prices",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: juham-automation
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Juha's Ultimate Home Automation Masterpiece
|
|
5
5
|
Author-email: J Meskanen <juham.api@gmail.com>
|
|
6
6
|
Maintainer-email: "J. Meskanen" <juham.api@gmail.com>
|
|
@@ -30,11 +30,11 @@ License: LICENSE
|
|
|
30
30
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
Project-URL: Homepage, https://gitlab.com/juham/juham/juham-automation
|
|
34
|
-
Project-URL: Bug Reports, https://gitlab.com/juham/juham/juham-
|
|
33
|
+
Project-URL: Homepage, https://gitlab.com/juham/juham/juham-automation
|
|
34
|
+
Project-URL: Bug Reports, https://gitlab.com/juham/juham/juham-automationt
|
|
35
35
|
Project-URL: Funding, https://meskanen.com
|
|
36
36
|
Project-URL: Say Thanks!, http://meskanen.com
|
|
37
|
-
Project-URL: Source, https://gitlab.com/juham/juham/juham-automation
|
|
37
|
+
Project-URL: Source, https://gitlab.com/juham/juham/juham-automation
|
|
38
38
|
Keywords: home,automation,juham
|
|
39
39
|
Classifier: Development Status :: 3 - Alpha
|
|
40
40
|
Classifier: Intended Audience :: Developers
|
|
@@ -44,7 +44,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
44
44
|
Requires-Python: >=3.8
|
|
45
45
|
Description-Content-Type: text/markdown
|
|
46
46
|
License-File: LICENSE.rst
|
|
47
|
-
Requires-Dist: juham_core>=0.1.
|
|
47
|
+
Requires-Dist: juham_core>=0.1.6
|
|
48
48
|
Provides-Extra: dev
|
|
49
49
|
Requires-Dist: check-manifest; extra == "dev"
|
|
50
50
|
Requires-Dist: coverage>=7.0; extra == "dev"
|
|
@@ -3,8 +3,8 @@ juham_automation/japp.py,sha256=L2u1mfKvun2fiXhB3AEJD9zMDcdFZ3_doXZYJJzu9tg,1646
|
|
|
3
3
|
juham_automation/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
4
4
|
juham_automation/automation/__init__.py,sha256=uxkIrcRSp1cFikn-oBRtQ8XiT9cSf7xjm3CS1RN7lAQ,522
|
|
5
5
|
juham_automation/automation/energybalancer.py,sha256=Mf9bK-Xo4zNalePL5EIGvlFObMkjWgeh76gvcU-ydIk,11532
|
|
6
|
-
juham_automation/automation/energycostcalculator.py,sha256=
|
|
7
|
-
juham_automation/automation/heatingoptimizer.py,sha256=
|
|
6
|
+
juham_automation/automation/energycostcalculator.py,sha256=OOvKhRbQ99wtnmoy9R3kGdAPMSkoDLa5GnZswpu52u0,11853
|
|
7
|
+
juham_automation/automation/heatingoptimizer.py,sha256=M03r9sZLAPavjj0LlFXiVZqAKHN5ZUvcDVi2QR5yOrk,24684
|
|
8
8
|
juham_automation/automation/powermeter_simulator.py,sha256=3WZcjByRTdqnC77l7LjP-TEjmZ8XBEO4hClYsrjxmBE,4549
|
|
9
9
|
juham_automation/automation/spothintafi.py,sha256=cZbi7w2fVweHX_fh1r5MTjGdesX9wDQta2mfVjtiwvw,4331
|
|
10
10
|
juham_automation/automation/watercirculator.py,sha256=a8meMNaONbHcIH3y0vP0UulJc1-gZiLZpw7H8kAOreY,6410
|
|
@@ -17,9 +17,9 @@ juham_automation/ts/log_ts.py,sha256=XsNaazuPmRUZLUqxU0DZae_frtT6kAFcXJTc598CtOA
|
|
|
17
17
|
juham_automation/ts/power_ts.py,sha256=e7bSeZjitY4C_gLup9L0NjvU_WnQsl3ayDhVShj32KY,1399
|
|
18
18
|
juham_automation/ts/powermeter_ts.py,sha256=gXzfK2S4SzrQ9GqM0tsLaV6z_vYmTkBatTcaivASSXs,2188
|
|
19
19
|
juham_automation/ts/powerplan_ts.py,sha256=LZeE7TnzPCDaugggKlaV-K48lDwwnC1ZNum50JYAWaY,1482
|
|
20
|
-
juham_automation-0.1.
|
|
21
|
-
juham_automation-0.1.
|
|
22
|
-
juham_automation-0.1.
|
|
23
|
-
juham_automation-0.1.
|
|
24
|
-
juham_automation-0.1.
|
|
25
|
-
juham_automation-0.1.
|
|
20
|
+
juham_automation-0.1.3.dist-info/licenses/LICENSE.rst,sha256=QVHD5V5_HSys2PdPdig_xKggDj8cGX33ALKqRsYyjtI,1089
|
|
21
|
+
juham_automation-0.1.3.dist-info/METADATA,sha256=5Suhmsd8drHmj91gPngyVohtnA3sPdwsjsVIGYaQR7w,7122
|
|
22
|
+
juham_automation-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
juham_automation-0.1.3.dist-info/entry_points.txt,sha256=h-KzuKjmGPd4_iX_oiGvxx4IEc97dVbGGlhdh5ctbpI,605
|
|
24
|
+
juham_automation-0.1.3.dist-info/top_level.txt,sha256=jfohvtocvX_gfT21AhJk7Iay5ZiQsS3HzrDjF7S4Qp0,17
|
|
25
|
+
juham_automation-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|