11from typing import TypeVar , Generic , Callable
2- from datetime import datetime , timedelta
32from helpermodules import timecheck
4- import random
53import logging
64from modules .common import store
75from modules .common .component_context import SingleComponentUpdateContext
1412TARIFF_UPDATE_HOUR = 14 # latest expected time for daily tariff update
1513ONE_HOUR_SECONDS : int = 3600
1614log = logging .getLogger (__name__ )
17- '''
18- next_query_time and internal_tariff_state are defined outside of class ConfigurableElectricityTariff because
19- for an unknown reason defining them as a class variable does not keep their values.
20- '''
21- next_query_time : datetime = datetime .fromtimestamp (1 )
22- internal_tariff_state : TariffState = None
2315
2416
2517class ConfigurableElectricityTariff (Generic [T_TARIFF_CONFIG ]):
2618 def __init__ (self ,
2719 config : T_TARIFF_CONFIG ,
2820 component_initializer : Callable [[], float ]) -> None :
29- global internal_tariff_state , next_query_time
30- next_query_time = datetime .now ()
31- internal_tariff_state = None
3221 self .config = config
3322 self .store = store .get_electricity_tariff_value_store ()
3423 self .fault_state = FaultState (ComponentInfo (None , self .config .name , ComponentType .ELECTRICITY_TARIFF .value ))
@@ -41,90 +30,30 @@ def __init__(self,
4130 def update (self ) -> None :
4231 if hasattr (self , "_component_updater" ):
4332 with SingleComponentUpdateContext (self .fault_state ):
44- tariff_state , timeslot_length_seconds = self .__update_et_provider_data (internal_tariff_state )
33+ tariff_state , timeslot_length_seconds = self .__update_et_provider_data ()
4534 self .__store_and_publish_updated_data (tariff_state )
46- self .__log_and_publish_progress (timeslot_length_seconds )
35+ self .__log_and_publish_progress (timeslot_length_seconds , tariff_state )
4736
48- def __update_et_provider_data (self , tariff_state : TariffState ) -> tuple [TariffState , int ]:
49- tariff_state = self .__query_et_provider_data_once_per_day ( internal_tariff_state )
37+ def __update_et_provider_data (self ) -> tuple [TariffState , int ]:
38+ tariff_state = self ._component_updater ( )
5039 timeslot_length_seconds = self .__calculate_price_timeslot_length (tariff_state )
5140 tariff_state = self ._remove_outdated_prices (tariff_state , timeslot_length_seconds )
5241 return tariff_state , timeslot_length_seconds
5342
54- def __query_et_provider_data_once_per_day (self , tariff_state : TariffState ) -> TariffState :
55- if datetime .now () > next_query_time :
56- return self .__query_et_provider_data (tariff_state = tariff_state )
57- else :
58- return tariff_state
59-
60- def __query_et_provider_data (self , tariff_state : TariffState ) -> TariffState :
61- def is_tomorrow (last_timestamp : str ) -> bool :
62- return (self .__day_of (date = datetime .now ()) < self .__day_of (datetime .fromtimestamp (int (last_timestamp )))
63- or self .__day_of (date = datetime .now ()).hour < TARIFF_UPDATE_HOUR )
64- global next_query_time
65- log .info (f'Wartezeit { next_query_time .strftime ("%Y%m%d-%H:%M:%S" )} '
66- ' abgelaufen, Strompreise werden abgefragt'
67- )
68- try :
69- new_tariff_state = self ._component_updater ()
70- if 0 < len (new_tariff_state .prices ):
71- if is_tomorrow (self .__get_last_entry_time_stamp (new_tariff_state )):
72- next_query_time = self .__calulate_next_query_time (new_tariff_state )
73- log .info ('Nächster Abruf der Strompreise'
74- f' { next_query_time .strftime ("%Y%m%d-%H:%M:%S" )} .' )
75- else :
76- log .info ('Keine Daten für morgen erhalten, weiterer Versuch in 5 Minuten' )
77- return new_tariff_state
78- else :
79- log .warning ('Leere Preisliste erhalten, weiterer Versuch in 5 Minuten.' )
80- return tariff_state
81- except Exception as e :
82- log .warning (f'Fehler beim Abruf der Strompreise: { e } , nächster Versuch in 5 Minuten.' )
83- self .fault_state .warning (
84- f'Fehler beim Abruf der Strompreise: { e } , nächster Versuch in 5 Minuten.' )
85- return tariff_state
86-
87- def __day_of (self , date : datetime ) -> datetime :
88- return date .replace (hour = 0 , minute = 0 , second = 0 , microsecond = 0 )
89-
90- def __next_query_message (self ) -> str :
91- tomorrow = (
92- ''
93- if self .__day_of (datetime .now ()) == self .__day_of (next_query_time )
94- else 'morgen '
95- )
96- return (
97- f'{ tomorrow } { next_query_time .strftime ("%H:%M" )} '
98- if datetime .now () < next_query_time
99- else "im nächsten Regelzyklus"
100- )
101-
102- def __log_and_publish_progress (self , timeslot_length_seconds ):
43+ def __log_and_publish_progress (self , timeslot_length_seconds , tariff_state ):
10344 def publish_info (message_extension : str ) -> None :
10445 self .fault_state .no_error (
105- f'Die Preisliste hat { message_extension } { len (internal_tariff_state .prices )} Einträge. '
106- f'Nächster Abruf der Strompreise { self .__next_query_message ()} .' )
46+ f'Die Preisliste hat { message_extension } { len (tariff_state .prices )} Einträge. ' )
10747 expected_time_slots = int (24 * ONE_HOUR_SECONDS / timeslot_length_seconds )
10848 publish_info (f'nicht { expected_time_slots } , sondern '
109- if len (internal_tariff_state .prices ) < expected_time_slots
49+ if len (tariff_state .prices ) < expected_time_slots
11050 else ''
11151 )
11252
11353 def __store_and_publish_updated_data (self , tariff_state : TariffState ) -> None :
114- global internal_tariff_state
115- internal_tariff_state = tariff_state
11654 self .store .set (tariff_state )
11755 self .store .update ()
11856
119- def __calulate_next_query_time (self , tariff_state : TariffState ) -> datetime :
120- return datetime .fromtimestamp (int (max (tariff_state .prices ))).replace (
121- hour = TARIFF_UPDATE_HOUR , minute = 0 , second = 0
122- ) + timedelta (
123- # aktually ET providers issue next day prices up to half an hour earlier then 14:00
124- # reduce serverload on their site by trying early and randomizing query time
125- minutes = random .randint (1 , 7 ) * - 5
126- )
127-
12857 def __calculate_price_timeslot_length (self , tariff_state : TariffState ) -> int :
12958 if (tariff_state is None or
13059 tariff_state .prices is None or
@@ -135,12 +64,6 @@ def __calculate_price_timeslot_length(self, tariff_state: TariffState) -> int:
13564 first_timestamps = list (tariff_state .prices .keys ())[:2 ]
13665 return int (first_timestamps [1 ]) - int (first_timestamps [0 ])
13766
138- def __get_last_entry_time_stamp (self , tariff_state : TariffState ) -> str :
139- last_known_timestamp = "0"
140- if tariff_state is not None :
141- last_known_timestamp = max (tariff_state .prices )
142- return last_known_timestamp
143-
14467 def _remove_outdated_prices (self , tariff_state : TariffState , timeslot_length_seconds : int ) -> TariffState :
14568 if tariff_state .prices is None :
14669 self .fault_state .error ("no prices to show" )
0 commit comments