juham-automation 0.2.7__tar.gz → 0.2.20__tar.gz
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-0.2.7/juham_automation.egg-info → juham_automation-0.2.20}/PKG-INFO +2 -3
- {juham_automation-0.2.7/docs/source → juham_automation-0.2.20}/README.rst +0 -1
- {juham_automation-0.2.7 → juham_automation-0.2.20}/docs/source/CHANGELOG.rst +14 -1
- {juham_automation-0.2.7 → juham_automation-0.2.20/docs/source}/README.rst +0 -1
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/__init__.py +0 -2
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/__init__.py +0 -2
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/heatingoptimizer.py +18 -32
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/leakdetector.py +0 -2
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/japp.py +0 -2
- {juham_automation-0.2.7 → juham_automation-0.2.20/juham_automation.egg-info}/PKG-INFO +2 -3
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation.egg-info/SOURCES.txt +0 -3
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation.egg-info/entry_points.txt +0 -1
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation.egg-info/requires.txt +1 -1
- {juham_automation-0.2.7 → juham_automation-0.2.20}/pyproject.toml +2 -3
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_heatingoptimizer.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_heatingoptimizer.py +2 -8
- juham_automation-0.2.7/examples/myapp.log +0 -52
- juham_automation-0.2.7/juham_automation/automation/spothintafi.py +0 -140
- juham_automation-0.2.7/tests/automation/test_spothintafi.py +0 -67
- {juham_automation-0.2.7 → juham_automation-0.2.20}/LICENSE.rst +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/MANIFEST.in +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/docs/source/CONTRIBUTING.rst +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/docs/source/LICENSE.rst +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/examples/myapp.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/energybalancer.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/energycostcalculator.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/watercirculator.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/py.typed +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/__init__.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/electricityprice_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/energybalancer_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/energycostcalculator_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/forecast_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/log_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/power_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/powermeter_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/powerplan_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation.egg-info/dependency_links.txt +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation.egg-info/top_level.txt +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/setup.cfg +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/__init__.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/__pycache__/test_japp.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/__pycache__/test_japp.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__init__.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/__init__.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_energybalancer.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_energybalancer.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_energycostcalculator.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_energycostcalculator.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_heatingoptimizer.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_juham.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_juham.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_leakdetector.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_leakdetector.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_spothintafi.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_spothintafi.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_watercirculator.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/__pycache__/test_watercirculator.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_energybalancer.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_energycostcalculator.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_juham.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_leakdetector.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_watercirculator.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/test_japp.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__init__.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/__init__.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_electricityprice_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_electricityprice_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_energybalancer_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_energybalancer_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_energycostcalculator_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_energycostcalculator_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_forecast_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_forecast_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_log_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_log_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_power_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_power_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_powermeter_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_powermeter_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_powerplan_ts.cpython-312-pytest-9.0.1.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_powerplan_ts.cpython-312.pyc +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_electricityprice_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_energybalancer_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_energycostcalculator_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_forecast_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_log_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_power_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_powermeter_ts.py +0 -0
- {juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/test_powerplan_ts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: juham-automation
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.20
|
|
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>
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
18
18
|
Requires-Python: >=3.8
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.rst
|
|
21
|
-
Requires-Dist: juham_core>=0.2.
|
|
21
|
+
Requires-Dist: juham_core>=0.2.6
|
|
22
22
|
Provides-Extra: dev
|
|
23
23
|
Requires-Dist: check-manifest; extra == "dev"
|
|
24
24
|
Requires-Dist: coverage>=7.0; extra == "dev"
|
|
@@ -38,7 +38,6 @@ It consists of two main sub-modules:
|
|
|
38
38
|
|
|
39
39
|
This folder contains automation classes that listen to Juham™ MQTT topics and control various home automation tasks.
|
|
40
40
|
|
|
41
|
-
- **spothintafi**: Acquires electricity prices in Finland.
|
|
42
41
|
- **watercirculator**: Automates a water circulator pump based on hot water temperature and motion detection.
|
|
43
42
|
- **heatingoptimizer**: Controls hot water radiators based on temperature sensors and electricity price data.
|
|
44
43
|
- **energycostcalculator**: Monitors power consumption and electricity prices, and computes the energy balance in euros.
|
|
@@ -12,7 +12,6 @@ It consists of two main sub-modules:
|
|
|
12
12
|
|
|
13
13
|
This folder contains automation classes that listen to Juham™ MQTT topics and control various home automation tasks.
|
|
14
14
|
|
|
15
|
-
- **spothintafi**: Acquires electricity prices in Finland.
|
|
16
15
|
- **watercirculator**: Automates a water circulator pump based on hot water temperature and motion detection.
|
|
17
16
|
- **heatingoptimizer**: Controls hot water radiators based on temperature sensors and electricity price data.
|
|
18
17
|
- **energycostcalculator**: Monitors power consumption and electricity prices, and computes the energy balance in euros.
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
[0.2.10] - December 3 2025
|
|
6
|
+
--------------------------
|
|
7
|
+
|
|
8
|
+
- Error logging improved for timeseries database writes.
|
|
9
|
+
- Dependencies to the recent masterpiece releases updated
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
[0.2.9] - December 1 2025
|
|
5
13
|
-------------------------
|
|
14
|
+
- 'spothintafi.py' removed and implemented as separate 'juham-spothintafi' plugin
|
|
15
|
+
|
|
6
16
|
|
|
17
|
+
[0.2.8] - December 1 2025
|
|
18
|
+
-------------------------
|
|
7
19
|
- LeakDetector migrated from juham-watermeter
|
|
20
|
+
- Obsolete debug logging removed from the LeakDetector
|
|
8
21
|
|
|
9
22
|
|
|
10
23
|
[0.2.6] - November 30 2025
|
|
@@ -12,7 +12,6 @@ It consists of two main sub-modules:
|
|
|
12
12
|
|
|
13
13
|
This folder contains automation classes that listen to Juham™ MQTT topics and control various home automation tasks.
|
|
14
14
|
|
|
15
|
-
- **spothintafi**: Acquires electricity prices in Finland.
|
|
16
15
|
- **watercirculator**: Automates a water circulator pump based on hot water temperature and motion detection.
|
|
17
16
|
- **heatingoptimizer**: Controls hot water radiators based on temperature sensors and electricity price data.
|
|
18
17
|
- **energycostcalculator**: Monitors power consumption and electricity prices, and computes the energy balance in euros.
|
|
@@ -7,7 +7,6 @@ Juham - Juha's Ultimate Home Automation Masterpiece
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from .automation import EnergyCostCalculator
|
|
10
|
-
from .automation import SpotHintaFi
|
|
11
10
|
from .automation import WaterCirculator
|
|
12
11
|
from .automation import HeatingOptimizer
|
|
13
12
|
from .automation import EnergyBalancer
|
|
@@ -34,7 +33,6 @@ __all__ = [
|
|
|
34
33
|
"PowerTs",
|
|
35
34
|
"PowerPlanTs",
|
|
36
35
|
"PowerMeterTs",
|
|
37
|
-
"SpotHintaFi",
|
|
38
36
|
"WaterCirculator",
|
|
39
37
|
"JApp",
|
|
40
38
|
"ElectricityPriceTs",
|
|
@@ -7,7 +7,6 @@ Juham - Juha's Ultimate Home Automation classes
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from .energycostcalculator import EnergyCostCalculator
|
|
10
|
-
from .spothintafi import SpotHintaFi
|
|
11
10
|
from .watercirculator import WaterCirculator
|
|
12
11
|
from .heatingoptimizer import HeatingOptimizer
|
|
13
12
|
from .energybalancer import EnergyBalancer
|
|
@@ -16,7 +15,6 @@ from .leakdetector import LeakDetector
|
|
|
16
15
|
__all__ = [
|
|
17
16
|
"EnergyCostCalculator",
|
|
18
17
|
"HeatingOptimizer",
|
|
19
|
-
"SpotHintaFi",
|
|
20
18
|
"WaterCirculator",
|
|
21
19
|
"EnergyBalancer",
|
|
22
20
|
"LeakDetector",
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/heatingoptimizer.py
RENAMED
|
@@ -44,15 +44,6 @@ class HeatingOptimizer(Juham):
|
|
|
44
44
|
radiator_power: float = 6000 # W
|
|
45
45
|
"""Radiator power in Watts. This is the maximum power that the radiator can consume."""
|
|
46
46
|
|
|
47
|
-
heating_slots_per_day: float = 4
|
|
48
|
-
""" Number of slots per day the radiator is allowed to heat."""
|
|
49
|
-
|
|
50
|
-
schedule_start_slot: float = 0
|
|
51
|
-
"""Start slots of the heating schedule."""
|
|
52
|
-
|
|
53
|
-
schedule_stop_slot: float = 0
|
|
54
|
-
"""Stop slot of the heating schedule. Heating is allowed only between start-stop slots."""
|
|
55
|
-
|
|
56
47
|
timezone: str = "Europe/Helsinki"
|
|
57
48
|
""" Timezone of the heating system. This is used to convert UTC timestamps to local time."""
|
|
58
49
|
|
|
@@ -147,7 +138,7 @@ class HeatingOptimizer(Juham):
|
|
|
147
138
|
super().__init__(name)
|
|
148
139
|
|
|
149
140
|
self.heating_slots_per_day = num_hours * ( 3600 / self.energy_balancing_interval)
|
|
150
|
-
self.
|
|
141
|
+
self.schedule_start_slot = start_hour * (3600 / self.energy_balancing_interval)
|
|
151
142
|
self.spot_limit = spot_limit
|
|
152
143
|
|
|
153
144
|
self.topic_in_spot = self.make_topic_name("spot")
|
|
@@ -464,6 +455,8 @@ class HeatingOptimizer(Juham):
|
|
|
464
455
|
"""
|
|
465
456
|
self.spot_prices = m
|
|
466
457
|
self.ranked_spot_prices = self.sort_by_rank(m, ts_quantized)
|
|
458
|
+
# reset the current state of the relay
|
|
459
|
+
self.current_relay_state = -1
|
|
467
460
|
|
|
468
461
|
def on_forecast(
|
|
469
462
|
self, forecast: list[dict[str, Any]], ts_utc_quantized: float
|
|
@@ -572,11 +565,7 @@ class HeatingOptimizer(Juham):
|
|
|
572
565
|
if not self.heating_plan:
|
|
573
566
|
self.error(f"{self.name} failed to create heating plan")
|
|
574
567
|
return
|
|
575
|
-
|
|
576
|
-
self.info(
|
|
577
|
-
f"{self.name} heating plan of length {len(self.heating_plan)} created",
|
|
578
|
-
"",
|
|
579
|
-
)
|
|
568
|
+
self.info(f"{self.name} heating plan of length {len(self.heating_plan)} created", str(self.heating_plan))
|
|
580
569
|
|
|
581
570
|
self.publish_heating_plan(self.heating_plan)
|
|
582
571
|
|
|
@@ -630,6 +619,7 @@ class HeatingOptimizer(Juham):
|
|
|
630
619
|
|
|
631
620
|
# check if we have excess energy to spent within the current slot
|
|
632
621
|
if self.net_energy_balance_mode:
|
|
622
|
+
self.debug("Energy balancing active, bypass plan")
|
|
633
623
|
return 1
|
|
634
624
|
|
|
635
625
|
slot : int = self.timestamp_slot(ts)
|
|
@@ -644,7 +634,7 @@ class HeatingOptimizer(Juham):
|
|
|
644
634
|
break
|
|
645
635
|
|
|
646
636
|
if state == -1:
|
|
647
|
-
self.error(f"
|
|
637
|
+
self.error(f"Cannot find heating plan for slot {slot}, no heating")
|
|
648
638
|
return 0
|
|
649
639
|
|
|
650
640
|
# don't heat if the current temperature is already high enough
|
|
@@ -673,13 +663,13 @@ class HeatingOptimizer(Juham):
|
|
|
673
663
|
"""
|
|
674
664
|
|
|
675
665
|
if not self.is_slot_within_schedule(
|
|
676
|
-
slot, self.schedule_start_slot, self.
|
|
666
|
+
slot, self.schedule_start_slot, self.schedule_start_slot + self.heating_slots_per_day
|
|
677
667
|
):
|
|
678
668
|
return 0.0
|
|
679
669
|
|
|
680
|
-
if price < 0.
|
|
670
|
+
if price < 0.01:
|
|
681
671
|
return 1.0 # use
|
|
682
|
-
elif price > self.expected_average_price:
|
|
672
|
+
elif price > 3*self.expected_average_price:
|
|
683
673
|
return 0.0 # try not to use
|
|
684
674
|
else:
|
|
685
675
|
fom = self.expected_average_price / price
|
|
@@ -880,8 +870,6 @@ class HeatingOptimizer(Juham):
|
|
|
880
870
|
# factor = 0 -> uses monthly_temp_max (full monthly default capacity)
|
|
881
871
|
max_temp_today = (factor * T_max_target) + ((1 - factor) * monthly_temp_max)
|
|
882
872
|
|
|
883
|
-
|
|
884
|
-
|
|
885
873
|
# adjust the temperature limits based on the electricity prices
|
|
886
874
|
ts : float = timestamp()
|
|
887
875
|
num_hours : float = self.heating_slots_per_day * self.energy_balancing_interval / 3600.0
|
|
@@ -901,10 +889,7 @@ class HeatingOptimizer(Juham):
|
|
|
901
889
|
self, slot: int, spot: float, fom: float, end_slot: int
|
|
902
890
|
) -> bool:
|
|
903
891
|
return (
|
|
904
|
-
slot >= self.
|
|
905
|
-
and slot < end_slot
|
|
906
|
-
and float(spot) < self.spot_limit
|
|
907
|
-
and fom > self.uoi_threshold
|
|
892
|
+
slot >= self.schedule_start_slot and slot < end_slot
|
|
908
893
|
)
|
|
909
894
|
|
|
910
895
|
def create_heating_plan(self) -> list[dict[str, Any]]:
|
|
@@ -916,17 +901,17 @@ class HeatingOptimizer(Juham):
|
|
|
916
901
|
|
|
917
902
|
state = 0
|
|
918
903
|
heating_plan: list[dict[str, Any]] = []
|
|
919
|
-
|
|
904
|
+
|
|
920
905
|
for hp in self.power_plan:
|
|
921
906
|
ts: float = hp["Timestamp"]
|
|
922
907
|
fom = hp["FOM"]
|
|
923
908
|
spot = hp["Spot"]
|
|
924
|
-
end_slot:
|
|
925
|
-
slot:
|
|
909
|
+
end_slot: int = self.schedule_start_slot + self.heating_slots_per_day
|
|
910
|
+
slot: int = self.timestamp_slot(ts)
|
|
926
911
|
schedule_on: bool = self.is_slot_within_schedule(
|
|
927
|
-
slot, self.schedule_start_slot, self.
|
|
912
|
+
slot, self.schedule_start_slot, self.schedule_start_slot + self.heating_slots_per_day
|
|
928
913
|
)
|
|
929
|
-
|
|
914
|
+
|
|
930
915
|
if self.enable_relay(slot, spot, fom, end_slot) and schedule_on:
|
|
931
916
|
state = 1
|
|
932
917
|
else:
|
|
@@ -939,11 +924,12 @@ class HeatingOptimizer(Juham):
|
|
|
939
924
|
"UOI": fom,
|
|
940
925
|
"Spot": spot,
|
|
941
926
|
}
|
|
927
|
+
print(f"Slot {slot}, start_slot={self.schedule_start_slot}, end_slot={end_slot}, schedule on {schedule_on} and enable_relay {self.enable_relay(slot, spot, fom, end_slot)}")
|
|
942
928
|
|
|
943
929
|
heating_plan.append(heat)
|
|
944
|
-
slot = slot + 1
|
|
945
930
|
|
|
946
|
-
|
|
931
|
+
|
|
932
|
+
self.info(f"{self.name} heating plan of {len(heating_plan)} slots created", str(heating_plan))
|
|
947
933
|
return heating_plan
|
|
948
934
|
|
|
949
935
|
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/leakdetector.py
RENAMED
|
@@ -144,8 +144,6 @@ class LeakDetector(Juham):
|
|
|
144
144
|
data (dict): Motion sensor data containing timestamp.
|
|
145
145
|
"""
|
|
146
146
|
if "motion" in data and data["motion"]:
|
|
147
|
-
readable :str = datetime.fromtimestamp(self.motion_last_detected_ts).strftime("%Y-%m-%d %H:%M:%S")
|
|
148
|
-
self.warning(f"Leak suspect reset due to detected motion", f"Last detected motion {readable}")
|
|
149
147
|
self.motion_last_detected_ts = data["ts"]
|
|
150
148
|
|
|
151
149
|
@override
|
|
@@ -10,7 +10,6 @@ from .ts import EnergyBalancerTs
|
|
|
10
10
|
from .ts import LogTs
|
|
11
11
|
from .ts import EnergyCostCalculatorTs
|
|
12
12
|
from .ts import ElectricityPriceTs
|
|
13
|
-
from .automation import SpotHintaFi
|
|
14
13
|
from .automation import EnergyCostCalculator
|
|
15
14
|
|
|
16
15
|
|
|
@@ -40,7 +39,6 @@ class JApp(Application):
|
|
|
40
39
|
self.add(PowerPlanTs())
|
|
41
40
|
self.add(PowerMeterTs())
|
|
42
41
|
self.add(LogTs())
|
|
43
|
-
self.add(SpotHintaFi())
|
|
44
42
|
self.add(EnergyCostCalculator())
|
|
45
43
|
self.add(EnergyCostCalculatorTs())
|
|
46
44
|
self.add(ElectricityPriceTs())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: juham-automation
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.20
|
|
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>
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
18
18
|
Requires-Python: >=3.8
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.rst
|
|
21
|
-
Requires-Dist: juham_core>=0.2.
|
|
21
|
+
Requires-Dist: juham_core>=0.2.6
|
|
22
22
|
Provides-Extra: dev
|
|
23
23
|
Requires-Dist: check-manifest; extra == "dev"
|
|
24
24
|
Requires-Dist: coverage>=7.0; extra == "dev"
|
|
@@ -38,7 +38,6 @@ It consists of two main sub-modules:
|
|
|
38
38
|
|
|
39
39
|
This folder contains automation classes that listen to Juham™ MQTT topics and control various home automation tasks.
|
|
40
40
|
|
|
41
|
-
- **spothintafi**: Acquires electricity prices in Finland.
|
|
42
41
|
- **watercirculator**: Automates a water circulator pump based on hot water temperature and motion detection.
|
|
43
42
|
- **heatingoptimizer**: Controls hot water radiators based on temperature sensors and electricity price data.
|
|
44
43
|
- **energycostcalculator**: Monitors power consumption and electricity prices, and computes the energy balance in euros.
|
|
@@ -6,7 +6,6 @@ docs/source/CHANGELOG.rst
|
|
|
6
6
|
docs/source/CONTRIBUTING.rst
|
|
7
7
|
docs/source/LICENSE.rst
|
|
8
8
|
docs/source/README.rst
|
|
9
|
-
examples/myapp.log
|
|
10
9
|
examples/myapp.py
|
|
11
10
|
juham_automation/__init__.py
|
|
12
11
|
juham_automation/japp.py
|
|
@@ -22,7 +21,6 @@ juham_automation/automation/energybalancer.py
|
|
|
22
21
|
juham_automation/automation/energycostcalculator.py
|
|
23
22
|
juham_automation/automation/heatingoptimizer.py
|
|
24
23
|
juham_automation/automation/leakdetector.py
|
|
25
|
-
juham_automation/automation/spothintafi.py
|
|
26
24
|
juham_automation/automation/watercirculator.py
|
|
27
25
|
juham_automation/ts/__init__.py
|
|
28
26
|
juham_automation/ts/electricityprice_ts.py
|
|
@@ -44,7 +42,6 @@ tests/automation/test_energycostcalculator.py
|
|
|
44
42
|
tests/automation/test_heatingoptimizer.py
|
|
45
43
|
tests/automation/test_juham.py
|
|
46
44
|
tests/automation/test_leakdetector.py
|
|
47
|
-
tests/automation/test_spothintafi.py
|
|
48
45
|
tests/automation/test_watercirculator.py
|
|
49
46
|
tests/automation/__pycache__/__init__.cpython-312.pyc
|
|
50
47
|
tests/automation/__pycache__/test_energybalancer.cpython-312-pytest-9.0.1.pyc
|
|
@@ -7,7 +7,7 @@ include-package-data = true
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "juham-automation"
|
|
10
|
-
version = "0.2.
|
|
10
|
+
version = "0.2.20"
|
|
11
11
|
description = "Juha's Ultimate Home Automation Masterpiece"
|
|
12
12
|
readme = {file = "README.rst", content-type = "text/markdown"}
|
|
13
13
|
requires-python = ">=3.8"
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
dependencies = [
|
|
31
|
-
"juham_core >= 0.2.
|
|
31
|
+
"juham_core >= 0.2.6",
|
|
32
32
|
]
|
|
33
33
|
|
|
34
34
|
|
|
@@ -45,7 +45,6 @@ powerts_plugin = "juham_automation:PowerTs"
|
|
|
45
45
|
powerplan_plugin = "juham_automation:PowerPlanTs"
|
|
46
46
|
energybalancerts_plugin = "juham_automation:EnergyBalancerTs"
|
|
47
47
|
logts_plugin = "juham_automation:LogTs"
|
|
48
|
-
spothintafi_plugin = "juham_automation:SpotHintaFi"
|
|
49
48
|
rwatercirculator_plugin = "juham_automation:WaterCirculator"
|
|
50
49
|
energycostcalculator_plugin = "juham_automation:EnergyCostCalculator"
|
|
51
50
|
energybalancer_plugin = "juham_automation:EnergyBalancer"
|
|
Binary file
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_heatingoptimizer.py
RENAMED
|
@@ -85,8 +85,6 @@ class HeatingOptimizerTest2(unittest.TestCase):
|
|
|
85
85
|
self.assertGreater(uoi, 0)
|
|
86
86
|
# Price above expected_average_price
|
|
87
87
|
self.ho.expected_average_price = 0.2
|
|
88
|
-
uoi2 = self.ho.compute_uoi(price=0.3, slot=7 * 3600/self.ho.energy_balancing_interval)
|
|
89
|
-
self.assertEqual(uoi2, 0.0)
|
|
90
88
|
|
|
91
89
|
def test_compute_effective_price(self):
|
|
92
90
|
price = self.ho.compute_effective_price(requested_power=6000, available_solpower=3000, spot=0.2)
|
|
@@ -159,7 +157,7 @@ class TestHeatingOptimizer(unittest.TestCase):
|
|
|
159
157
|
|
|
160
158
|
def test_initialization(self) -> None:
|
|
161
159
|
self.assertEqual(self.optimizer.heating_slots_per_day, 3*3600 // self.optimizer.energy_balancing_interval)
|
|
162
|
-
self.assertEqual(self.optimizer.
|
|
160
|
+
self.assertEqual(self.optimizer.schedule_start_slot, 5 * 3600 // self.optimizer.energy_balancing_interval)
|
|
163
161
|
self.assertEqual(self.optimizer.spot_limit, 0.25)
|
|
164
162
|
self.assertEqual(self.optimizer.current_temperature, 100)
|
|
165
163
|
self.assertFalse(self.optimizer.relay)
|
|
@@ -354,33 +352,29 @@ class HeatingOptimizerOnPowerPlanTest(unittest.TestCase):
|
|
|
354
352
|
min_a, max_a = self.ho.calculate_target_temps(
|
|
355
353
|
MIN_MONTHLY, MAX_MONTHLY, -30.0, TARGET_HOME_TEMP
|
|
356
354
|
)
|
|
357
|
-
|
|
355
|
+
|
|
358
356
|
self.assertEqual(70.0, max_a);
|
|
359
357
|
|
|
360
358
|
# --- Scenario 2: 0C Forecast (Low Demand) ---
|
|
361
359
|
min_b, max_b = self.ho.calculate_target_temps(
|
|
362
360
|
MIN_MONTHLY, MAX_MONTHLY, -20.0, TARGET_HOME_TEMP
|
|
363
361
|
)
|
|
364
|
-
self.assertEqual(40.0, min_b);
|
|
365
362
|
self.assertEqual(64.0, max_b);
|
|
366
363
|
|
|
367
364
|
# --- Scenario 3: Warm Forecast (Very Low Demand) ---
|
|
368
365
|
min_c, max_c = self.ho.calculate_target_temps(
|
|
369
366
|
MIN_MONTHLY, MAX_MONTHLY, -10, TARGET_HOME_TEMP
|
|
370
367
|
)
|
|
371
|
-
self.assertEqual(40.0, min_c);
|
|
372
368
|
self.assertEqual(58.0, max_c);
|
|
373
369
|
|
|
374
370
|
# --- Scenario 4: Hot Forecast ---
|
|
375
371
|
min_d, max_d = self.ho.calculate_target_temps(
|
|
376
372
|
MIN_MONTHLY, MAX_MONTHLY, 0, TARGET_HOME_TEMP
|
|
377
373
|
)
|
|
378
|
-
self.assertEqual(40.0, min_d);
|
|
379
374
|
self.assertEqual(52.0, max_d);
|
|
380
375
|
min_e, max_e = self.ho.calculate_target_temps(
|
|
381
376
|
MIN_MONTHLY, MAX_MONTHLY, 30, TARGET_HOME_TEMP
|
|
382
377
|
)
|
|
383
|
-
self.assertEqual(40.0, min_e);
|
|
384
378
|
self.assertEqual(40.0, max_e);
|
|
385
379
|
|
|
386
380
|
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
2025-11-24 20:05:06,895 [INFO ] MyApp : Configuration files in ~/.myapp/config
|
|
2
|
-
2025-11-24 20:05:06,899 [INFO ] MyApp : myapp : Starting up 0 forecast_ts
|
|
3
|
-
2025-11-24 20:05:06,899 [WARNI] ForecastTs : forecast_ts : Suspicious configuration: no database_class_id set
|
|
4
|
-
2025-11-24 20:05:06,899 [WARNI] ForecastTs : forecast_ts : Suscpicious configuration: no mqtt_class_id set for forecast_ts:ForecastTs
|
|
5
|
-
2025-11-24 20:05:06,899 [INFO ] MyApp : myapp : Starting up 1 power_ts
|
|
6
|
-
2025-11-24 20:05:06,899 [WARNI] PowerTs : power_ts : Suspicious configuration: no database_class_id set
|
|
7
|
-
2025-11-24 20:05:06,899 [WARNI] PowerTs : power_ts : Suscpicious configuration: no mqtt_class_id set for power_ts:PowerTs
|
|
8
|
-
2025-11-24 20:05:06,900 [INFO ] MyApp : myapp : Starting up 2 powerplan_ts
|
|
9
|
-
2025-11-24 20:05:06,900 [WARNI] PowerPlanTs : powerplan_ts : Suspicious configuration: no database_class_id set
|
|
10
|
-
2025-11-24 20:05:06,900 [WARNI] PowerPlanTs : powerplan_ts : Suscpicious configuration: no mqtt_class_id set for powerplan_ts:PowerPlanTs
|
|
11
|
-
2025-11-24 20:05:06,900 [INFO ] MyApp : myapp : Starting up 3 powermeter_record
|
|
12
|
-
2025-11-24 20:05:06,900 [WARNI] PowerMeterTs : powermeter_record : Suspicious configuration: no database_class_id set
|
|
13
|
-
2025-11-24 20:05:06,900 [WARNI] PowerMeterTs : powermeter_record : Suscpicious configuration: no mqtt_class_id set for powermeter_record:PowerMeterTs
|
|
14
|
-
2025-11-24 20:05:06,900 [INFO ] MyApp : myapp : Starting up 4 log_ts
|
|
15
|
-
2025-11-24 20:05:06,900 [WARNI] LogTs : log_ts : Suspicious configuration: no database_class_id set
|
|
16
|
-
2025-11-24 20:05:06,900 [WARNI] LogTs : log_ts : Suscpicious configuration: no mqtt_class_id set for log_ts:LogTs
|
|
17
|
-
2025-11-24 20:05:06,900 [INFO ] MyApp : myapp : Starting up 5 rspothintafi
|
|
18
|
-
2025-11-24 20:05:06,900 [WARNI] SpotHintaFi : rspothintafi : Suspicious configuration: no database_class_id set
|
|
19
|
-
2025-11-24 20:05:06,900 [WARNI] SpotHintaFi : rspothintafi : Suscpicious configuration: no mqtt_class_id set for rspothintafi:SpotHintaFi
|
|
20
|
-
2025-11-24 20:05:06,901 [INFO ] SpotHintaFi : rspothintafi : Starting up rspothintafi - <class 'juham_automation.automation.spothintafi.SpotHintaFiThread'>
|
|
21
|
-
2025-11-24 20:05:06,901 [INFO ] MyApp : myapp : Starting up 6 ecc
|
|
22
|
-
2025-11-24 20:05:06,901 [WARNI] EnergyCostCalculator : ecc : Suscpicious configuration: no mqtt_class_id set for ecc:EnergyCostCalculator
|
|
23
|
-
2025-11-24 20:05:06,901 [INFO ] MyApp : myapp : Starting up 7 ecc_ts
|
|
24
|
-
2025-11-24 20:05:06,901 [WARNI] EnergyCostCalculatorTs : ecc_ts : Suspicious configuration: no database_class_id set
|
|
25
|
-
2025-11-24 20:05:06,901 [WARNI] EnergyCostCalculatorTs : ecc_ts : Suscpicious configuration: no mqtt_class_id set for ecc_ts:EnergyCostCalculatorTs
|
|
26
|
-
2025-11-24 20:05:06,902 [INFO ] MyApp : myapp : Starting up 8 electricityprice_ts
|
|
27
|
-
2025-11-24 20:05:06,902 [WARNI] ElectricityPriceTs : electricityprice_ts : Suspicious configuration: no database_class_id set
|
|
28
|
-
2025-11-24 20:05:06,902 [WARNI] ElectricityPriceTs : electricityprice_ts : Suscpicious configuration: no mqtt_class_id set for electricityprice_ts:ElectricityPriceTs
|
|
29
|
-
2025-11-24 20:05:06,902 [INFO ] MyApp : myapp : Starting up 9 energybalancer
|
|
30
|
-
2025-11-24 20:05:06,902 [WARNI] EnergyBalancer : energybalancer : Suscpicious configuration: no mqtt_class_id set for energybalancer:EnergyBalancer
|
|
31
|
-
2025-11-24 20:05:06,902 [INFO ] MyApp : myapp : Starting up 10 energybalancer_ts
|
|
32
|
-
2025-11-24 20:05:06,902 [WARNI] EnergyBalancerTs : energybalancer_ts : Suspicious configuration: no database_class_id set
|
|
33
|
-
2025-11-24 20:05:06,905 [WARNI] EnergyBalancerTs : energybalancer_ts : Suscpicious configuration: no mqtt_class_id set for energybalancer_ts:EnergyBalancerTs
|
|
34
|
-
2025-11-24 20:05:06,905 [INFO ] MyApp : myapp : Starting up 11 boiler
|
|
35
|
-
2025-11-24 20:05:06,905 [WARNI] HeatingOptimizer : boiler : Suscpicious configuration: no mqtt_class_id set for boiler:HeatingOptimizer
|
|
36
|
-
2025-11-24 20:05:06,905 [INFO ] MyApp : myapp : All 12 children successfully started
|
|
37
|
-
2025-11-24 20:05:06,905 [WARNI] Juham : myapp : Suscpicious configuration: no mqtt_class_id set for myapp:Juham
|
|
38
|
-
2025-11-24 20:05:06,905 [ERROR] Juham : myapp : myapp does NOT have mqtt client, cannot run_forever, giving up
|
|
39
|
-
2025-11-24 20:05:06,905 [INFO ] MyApp : myapp : Shutting down children
|
|
40
|
-
2025-11-24 20:05:06,906 [INFO ] MyApp : myapp : Shutting down thread 0 forecast_ts
|
|
41
|
-
2025-11-24 20:05:06,906 [INFO ] MyApp : myapp : Shutting down thread 1 power_ts
|
|
42
|
-
2025-11-24 20:05:06,906 [INFO ] MyApp : myapp : Shutting down thread 2 powerplan_ts
|
|
43
|
-
2025-11-24 20:05:06,906 [INFO ] MyApp : myapp : Shutting down thread 3 powermeter_record
|
|
44
|
-
2025-11-24 20:05:06,906 [INFO ] MyApp : myapp : Shutting down thread 4 log_ts
|
|
45
|
-
2025-11-24 20:05:06,906 [INFO ] MyApp : myapp : Shutting down thread 5 rspothintafi
|
|
46
|
-
2025-11-24 20:05:07,298 [INFO ] MyApp : myapp : Shutting down thread 6 ecc
|
|
47
|
-
2025-11-24 20:05:07,298 [INFO ] MyApp : myapp : Shutting down thread 7 ecc_ts
|
|
48
|
-
2025-11-24 20:05:07,298 [INFO ] MyApp : myapp : Shutting down thread 8 electricityprice_ts
|
|
49
|
-
2025-11-24 20:05:07,298 [INFO ] MyApp : myapp : Shutting down thread 9 energybalancer
|
|
50
|
-
2025-11-24 20:05:07,298 [INFO ] MyApp : myapp : Shutting down thread 10 energybalancer_ts
|
|
51
|
-
2025-11-24 20:05:07,298 [INFO ] MyApp : myapp : Shutting down thread 11 boiler
|
|
52
|
-
2025-11-24 20:05:07,298 [INFO ] MyApp : myapp : All 12 children successfully shut down
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
import time
|
|
3
|
-
import json
|
|
4
|
-
from typing import Any, Dict, Optional, cast
|
|
5
|
-
from typing_extensions import override
|
|
6
|
-
|
|
7
|
-
from masterpiece.mqtt import Mqtt, MqttMsg
|
|
8
|
-
from juham_core import JuhamCloudThread, JuhamThread
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class SpotHintaFiThread(JuhamCloudThread):
|
|
12
|
-
"""Thread running SpotHinta.fi.
|
|
13
|
-
|
|
14
|
-
Periodically fetches the spot electricity prices and publishes them
|
|
15
|
-
to 'spot' topic.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
_spot_topic: str = ""
|
|
19
|
-
_url: str = ""
|
|
20
|
-
_interval: float = 12 * 3600
|
|
21
|
-
grid_cost_day: float = 0.0314
|
|
22
|
-
grid_cost_night: float = 0.0132
|
|
23
|
-
grid_cost_tax: float = 0.028272
|
|
24
|
-
|
|
25
|
-
def __init__(self, client: Optional[Mqtt] = None) -> None:
|
|
26
|
-
super().__init__(client)
|
|
27
|
-
self._interval = 60
|
|
28
|
-
|
|
29
|
-
def init(self, topic: str, url: str, interval: float) -> None:
|
|
30
|
-
self._spot_topic = topic
|
|
31
|
-
self._url = url
|
|
32
|
-
self._interval = interval
|
|
33
|
-
|
|
34
|
-
@override
|
|
35
|
-
def make_weburl(self) -> str:
|
|
36
|
-
return self._url
|
|
37
|
-
|
|
38
|
-
@override
|
|
39
|
-
def update_interval(self) -> float:
|
|
40
|
-
return self._interval
|
|
41
|
-
|
|
42
|
-
@override
|
|
43
|
-
def process_data(self, rawdata: Any) -> None:
|
|
44
|
-
"""Publish electricity price message to Juham topic.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
rawdata (dict): electricity prices
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
super().process_data(rawdata)
|
|
51
|
-
data = rawdata.json()
|
|
52
|
-
|
|
53
|
-
spot = []
|
|
54
|
-
for e in data:
|
|
55
|
-
dt = datetime.fromisoformat(e["DateTime"]) # Correct timezone handling
|
|
56
|
-
ts = int(dt.timestamp()) # Ensure integer timestamps like in the test
|
|
57
|
-
|
|
58
|
-
hour = dt.strftime("%H") # Correctly extract hour
|
|
59
|
-
|
|
60
|
-
if 6 <= int(hour) < 22:
|
|
61
|
-
grid_cost = self.grid_cost_day
|
|
62
|
-
else:
|
|
63
|
-
grid_cost = self.grid_cost_night
|
|
64
|
-
|
|
65
|
-
total_price = round(e["PriceWithTax"] + grid_cost + self.grid_cost_tax, 6)
|
|
66
|
-
grid_cost_total = round(grid_cost + self.grid_cost_tax, 6)
|
|
67
|
-
|
|
68
|
-
h = {
|
|
69
|
-
"Timestamp": ts,
|
|
70
|
-
"hour": hour,
|
|
71
|
-
"Rank": e["Rank"],
|
|
72
|
-
"PriceWithTax": total_price,
|
|
73
|
-
"GridCost": grid_cost_total,
|
|
74
|
-
}
|
|
75
|
-
spot.append(h)
|
|
76
|
-
|
|
77
|
-
self.publish(self._spot_topic, json.dumps(spot), 1, True)
|
|
78
|
-
# self.info(f"Spot electricity prices published for the next {len(spot)} days")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class SpotHintaFi(JuhamThread):
|
|
82
|
-
"""Spot electricity price for reading hourly electricity prices from
|
|
83
|
-
https://api.spot-hinta.fi site.
|
|
84
|
-
"""
|
|
85
|
-
|
|
86
|
-
_SPOTHINTAFI: str = "_spothintafi"
|
|
87
|
-
worker_thread_id = SpotHintaFiThread.get_class_id()
|
|
88
|
-
url = "https://api.spot-hinta.fi/TodayAndDayForward"
|
|
89
|
-
update_interval = 12 * 3600
|
|
90
|
-
|
|
91
|
-
def __init__(self, name: str = "rspothintafi") -> None:
|
|
92
|
-
super().__init__(name)
|
|
93
|
-
self.active_liter_lpm = -1
|
|
94
|
-
self.update_ts = None
|
|
95
|
-
self.spot_topic = self.make_topic_name("spot")
|
|
96
|
-
|
|
97
|
-
@override
|
|
98
|
-
def on_connect(self, client: object, userdata: Any, flags: int, rc: int) -> None:
|
|
99
|
-
super().on_connect(client, userdata, flags, rc)
|
|
100
|
-
if rc == 0:
|
|
101
|
-
self.subscribe(self.spot_topic)
|
|
102
|
-
|
|
103
|
-
@override
|
|
104
|
-
def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None:
|
|
105
|
-
if msg.topic == self.spot_topic:
|
|
106
|
-
em = json.loads(msg.payload.decode())
|
|
107
|
-
self.on_spot(em)
|
|
108
|
-
else:
|
|
109
|
-
super().on_message(client, userdata, msg)
|
|
110
|
-
|
|
111
|
-
def on_spot(self, m: dict[Any, Any]) -> None:
|
|
112
|
-
"""Write hourly spot electricity prices to time series database.
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
m (dict): holding hourly spot electricity prices
|
|
116
|
-
"""
|
|
117
|
-
pass
|
|
118
|
-
|
|
119
|
-
@override
|
|
120
|
-
def run(self) -> None:
|
|
121
|
-
self.worker = cast(SpotHintaFiThread, self.instantiate(self.worker_thread_id))
|
|
122
|
-
self.worker.init(self.spot_topic, self.url, self.update_interval)
|
|
123
|
-
super().run()
|
|
124
|
-
|
|
125
|
-
@override
|
|
126
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
127
|
-
data: Dict[str, Any] = super().to_dict()
|
|
128
|
-
data[self._SPOTHINTAFI] = {
|
|
129
|
-
"topic": self.spot_topic,
|
|
130
|
-
"url": self.url,
|
|
131
|
-
"interval": self.update_interval,
|
|
132
|
-
}
|
|
133
|
-
return data
|
|
134
|
-
|
|
135
|
-
@override
|
|
136
|
-
def from_dict(self, data: Dict[str, Any]) -> None:
|
|
137
|
-
super().from_dict(data)
|
|
138
|
-
if self._SPOTHINTAFI in data:
|
|
139
|
-
for key, value in data[self._SPOTHINTAFI].items():
|
|
140
|
-
setattr(self, key, value)
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from unittest import TestCase, mock
|
|
3
|
-
from unittest.mock import MagicMock, patch
|
|
4
|
-
import json
|
|
5
|
-
from typing import Dict, Any
|
|
6
|
-
from juham_core import JuhamCloudThread
|
|
7
|
-
|
|
8
|
-
from juham_automation.automation.spothintafi import (
|
|
9
|
-
SpotHintaFiThread,
|
|
10
|
-
SpotHintaFi,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class TestSpotHintaFiThread(TestCase):
|
|
15
|
-
|
|
16
|
-
@patch("juham_automation.automation.spothintafi.Mqtt")
|
|
17
|
-
def test_make_weburl(self, mock_mqtt) -> None:
|
|
18
|
-
|
|
19
|
-
thread = SpotHintaFiThread(mock_mqtt)
|
|
20
|
-
thread.init("test/topic", "http://test.url", 60)
|
|
21
|
-
|
|
22
|
-
# Test make_weburl method
|
|
23
|
-
self.assertEqual(thread.make_weburl(), "http://test.url")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class TestSpotHintaFi(unittest.TestCase):
|
|
27
|
-
|
|
28
|
-
def test_to_dict(self) -> None:
|
|
29
|
-
spot_hinta = SpotHintaFi("spot")
|
|
30
|
-
spot_hinta.url = "http://test.url"
|
|
31
|
-
expected_dict = {
|
|
32
|
-
"_class": "SpotHintaFi",
|
|
33
|
-
"_version": 0,
|
|
34
|
-
"_object": {"name": "spot", "payload": None},
|
|
35
|
-
"_base": {},
|
|
36
|
-
"_spothintafi": {
|
|
37
|
-
"topic": "/spot",
|
|
38
|
-
"url": "http://test.url",
|
|
39
|
-
"interval": 43200,
|
|
40
|
-
},
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
actual_dict: Dict[str, Any] = spot_hinta.to_dict()
|
|
44
|
-
self.assertEqual(actual_dict, expected_dict)
|
|
45
|
-
|
|
46
|
-
def test_from_dict(self) -> None:
|
|
47
|
-
spot_hinta = SpotHintaFi()
|
|
48
|
-
data = {
|
|
49
|
-
"_class": "SpotHintaFi",
|
|
50
|
-
"_object": {"name": "rspothintafi"},
|
|
51
|
-
"_base": {},
|
|
52
|
-
"_spothintafi": {
|
|
53
|
-
"topic": "spot/topic",
|
|
54
|
-
"url": "http://test.url",
|
|
55
|
-
"interval": 60,
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
spot_hinta.from_dict(data)
|
|
60
|
-
|
|
61
|
-
self.assertEqual(spot_hinta.spot_topic, "/spot")
|
|
62
|
-
self.assertEqual(spot_hinta.url, "http://test.url")
|
|
63
|
-
self.assertEqual(spot_hinta.update_interval, 43200)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if __name__ == "__main__":
|
|
67
|
-
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/energybalancer.py
RENAMED
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/automation/watercirculator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/electricityprice_ts.py
RENAMED
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation/ts/energycostcalculator_ts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/juham_automation.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/tests/__pycache__/__init__.cpython-312.pyc
RENAMED
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/tests/__pycache__/test_japp.cpython-312.pyc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/tests/automation/test_energycostcalculator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/__init__.cpython-312.pyc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{juham_automation-0.2.7 → juham_automation-0.2.20}/tests/ts/__pycache__/test_log_ts.cpython-312.pyc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|