carconnectivity-plugin-database 0.1a16__py3-none-any.whl → 0.1a21__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.
Potentially problematic release.
This version of carconnectivity-plugin-database might be problematic. Click here for more details.
- {carconnectivity_plugin_database-0.1a16.dist-info → carconnectivity_plugin_database-0.1a21.dist-info}/METADATA +1 -1
- {carconnectivity_plugin_database-0.1a16.dist-info → carconnectivity_plugin_database-0.1a21.dist-info}/RECORD +16 -16
- carconnectivity_plugins/database/_version.py +2 -2
- carconnectivity_plugins/database/agents/charging_agent.py +189 -88
- carconnectivity_plugins/database/agents/climatization_agent.py +13 -6
- carconnectivity_plugins/database/agents/drive_state_agent.py +148 -96
- carconnectivity_plugins/database/agents/state_agent.py +26 -14
- carconnectivity_plugins/database/agents/trip_agent.py +48 -37
- carconnectivity_plugins/database/model/charging_session.py +7 -7
- carconnectivity_plugins/database/model/drive.py +6 -6
- carconnectivity_plugins/database/model/vehicle.py +36 -34
- carconnectivity_plugins/database/plugin.py +8 -12
- {carconnectivity_plugin_database-0.1a16.dist-info → carconnectivity_plugin_database-0.1a21.dist-info}/WHEEL +0 -0
- {carconnectivity_plugin_database-0.1a16.dist-info → carconnectivity_plugin_database-0.1a21.dist-info}/entry_points.txt +0 -0
- {carconnectivity_plugin_database-0.1a16.dist-info → carconnectivity_plugin_database-0.1a21.dist-info}/licenses/LICENSE +0 -0
- {carconnectivity_plugin_database-0.1a16.dist-info → carconnectivity_plugin_database-0.1a21.dist-info}/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
|
|
|
4
4
|
import threading
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
|
-
from datetime import timedelta
|
|
7
|
+
from datetime import timedelta, datetime, timezone
|
|
8
8
|
|
|
9
9
|
from sqlalchemy.exc import DatabaseError
|
|
10
10
|
|
|
@@ -27,8 +27,9 @@ if TYPE_CHECKING:
|
|
|
27
27
|
from sqlalchemy.orm import scoped_session
|
|
28
28
|
from sqlalchemy.orm.session import Session
|
|
29
29
|
|
|
30
|
-
from carconnectivity.attributes import EnumAttribute, SpeedAttribute, PowerAttribute
|
|
30
|
+
from carconnectivity.attributes import EnumAttribute, SpeedAttribute, PowerAttribute, FloatAttribute
|
|
31
31
|
|
|
32
|
+
from carconnectivity_plugins.database.plugin import Plugin
|
|
32
33
|
from carconnectivity_plugins.database.model.vehicle import Vehicle
|
|
33
34
|
from carconnectivity.drive import ElectricDrive
|
|
34
35
|
|
|
@@ -37,13 +38,16 @@ LOG: logging.Logger = logging.getLogger("carconnectivity.plugins.database.agents
|
|
|
37
38
|
|
|
38
39
|
class ChargingAgent(BaseAgent):
|
|
39
40
|
|
|
40
|
-
def __init__(self, session_factory: scoped_session[Session], vehicle: Vehicle
|
|
41
|
-
|
|
41
|
+
def __init__(self, database_plugin: Plugin, session_factory: scoped_session[Session], vehicle: Vehicle,
|
|
42
|
+
carconnectivity_vehicle: ElectricVehicle) -> None:
|
|
43
|
+
if vehicle is None or carconnectivity_vehicle is None:
|
|
42
44
|
raise ValueError("Vehicle or its carconnectivity_vehicle attribute is None")
|
|
43
|
-
if not isinstance(
|
|
45
|
+
if not isinstance(carconnectivity_vehicle, ElectricVehicle):
|
|
44
46
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is not an ElectricVehicle")
|
|
47
|
+
self.database_plugin: Plugin = database_plugin
|
|
45
48
|
self.session_factory: scoped_session[Session] = session_factory
|
|
46
49
|
self.vehicle: Vehicle = vehicle
|
|
50
|
+
self.carconnectivity_vehicle: ElectricVehicle = carconnectivity_vehicle
|
|
47
51
|
|
|
48
52
|
with self.session_factory() as session:
|
|
49
53
|
self.last_charging_session: Optional[ChargingSession] = session.query(ChargingSession).filter(ChargingSession.vehicle == vehicle) \
|
|
@@ -52,24 +56,22 @@ class ChargingAgent(BaseAgent):
|
|
|
52
56
|
ChargingSession.plug_connected_date.desc().nulls_first()).first()
|
|
53
57
|
|
|
54
58
|
self.last_charging_session_lock: threading.RLock = threading.RLock()
|
|
55
|
-
self.carconnectivity_last_charging_state: Optional[Charging.ChargingState] =
|
|
56
|
-
self.carconnectivity_last_connector_state: Optional[ChargingConnector.ChargingConnectorConnectionState] =
|
|
59
|
+
self.carconnectivity_last_charging_state: Optional[Charging.ChargingState] = self.carconnectivity_vehicle.charging.state.value
|
|
60
|
+
self.carconnectivity_last_connector_state: Optional[ChargingConnector.ChargingConnectorConnectionState] = self.carconnectivity_vehicle.charging\
|
|
57
61
|
.connector.connection_state.value
|
|
58
|
-
self.carconnectivity_last_connector_lock_state: Optional[ChargingConnector.ChargingConnectorLockState] =
|
|
62
|
+
self.carconnectivity_last_connector_lock_state: Optional[ChargingConnector.ChargingConnectorLockState] = self.carconnectivity_vehicle.charging\
|
|
59
63
|
.connector.lock_state.value
|
|
60
64
|
if self.last_charging_session is not None and not self.last_charging_session.is_closed():
|
|
61
|
-
if
|
|
62
|
-
or (
|
|
63
|
-
and
|
|
65
|
+
if self.carconnectivity_vehicle.charging.state.value in (Charging.ChargingState.CHARGING, Charging.ChargingState.CONSERVATION) \
|
|
66
|
+
or (self.carconnectivity_vehicle.charging.connector.connection_state.enabled
|
|
67
|
+
and self.carconnectivity_vehicle.charging.connector.connection_state.value ==
|
|
64
68
|
ChargingConnector.ChargingConnectorConnectionState.CONNECTED) \
|
|
65
|
-
or (
|
|
66
|
-
and
|
|
69
|
+
or (self.carconnectivity_vehicle.charging.connector.lock_state.enabled
|
|
70
|
+
and self.carconnectivity_vehicle.charging.connector.lock_state.value == ChargingConnector.ChargingConnectorLockState.LOCKED):
|
|
67
71
|
LOG.info("Last charging session for vehicle %s is still open during startup, will continue this session", vehicle.vin)
|
|
68
72
|
else:
|
|
69
73
|
LOG.info("Last charging session for vehicle %s is still open during startup, but we are not charging, ignoring it", vehicle.vin)
|
|
70
74
|
self.last_charging_session = None
|
|
71
|
-
else:
|
|
72
|
-
self.last_charging_session = None
|
|
73
75
|
|
|
74
76
|
self.last_charging_state: Optional[ChargingState] = session.query(ChargingState).filter(ChargingState.vehicle == vehicle)\
|
|
75
77
|
.order_by(ChargingState.first_date.desc()).first()
|
|
@@ -83,30 +85,36 @@ class ChargingAgent(BaseAgent):
|
|
|
83
85
|
.order_by(ChargingPower.first_date.desc()).first()
|
|
84
86
|
self.last_charging_power_lock: threading.RLock = threading.RLock()
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
if
|
|
88
|
-
self.__on_connector_state_change(
|
|
88
|
+
self.carconnectivity_vehicle.charging.connector.connection_state.add_observer(self.__on_connector_state_change, Observable.ObserverEvent.UPDATED)
|
|
89
|
+
if self.carconnectivity_vehicle.charging.connector.connection_state.enabled:
|
|
90
|
+
self.__on_connector_state_change(self.carconnectivity_vehicle.charging.connector.connection_state, Observable.ObserverEvent.UPDATED)
|
|
89
91
|
|
|
90
|
-
|
|
91
|
-
if
|
|
92
|
-
self.__on_connector_lock_state_change(
|
|
92
|
+
self.carconnectivity_vehicle.charging.connector.lock_state.add_observer(self.__on_connector_lock_state_change, Observable.ObserverEvent.UPDATED)
|
|
93
|
+
if self.carconnectivity_vehicle.charging.connector.lock_state.enabled:
|
|
94
|
+
self.__on_connector_lock_state_change(self.carconnectivity_vehicle.charging.connector.lock_state, Observable.ObserverEvent.UPDATED)
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
if
|
|
96
|
-
self.__on_charging_state_change(
|
|
96
|
+
self.carconnectivity_vehicle.charging.state.add_observer(self.__on_charging_state_change, Observable.ObserverEvent.UPDATED)
|
|
97
|
+
if self.carconnectivity_vehicle.charging.state.enabled:
|
|
98
|
+
self.__on_charging_state_change(self.carconnectivity_vehicle.charging.state, Observable.ObserverEvent.UPDATED)
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
if
|
|
100
|
-
self.__on_charging_rate_change(
|
|
100
|
+
self.carconnectivity_vehicle.charging.rate.add_observer(self.__on_charging_rate_change, Observable.ObserverEvent.UPDATED)
|
|
101
|
+
if self.carconnectivity_vehicle.charging.rate.enabled:
|
|
102
|
+
self.__on_charging_rate_change(self.carconnectivity_vehicle.charging.rate, Observable.ObserverEvent.UPDATED)
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
if
|
|
104
|
-
self.__on_charging_power_change(
|
|
104
|
+
self.carconnectivity_vehicle.charging.power.add_observer(self.__on_charging_power_change, Observable.ObserverEvent.UPDATED)
|
|
105
|
+
if self.carconnectivity_vehicle.charging.power.enabled:
|
|
106
|
+
self.__on_charging_power_change(self.carconnectivity_vehicle.charging.power, Observable.ObserverEvent.UPDATED)
|
|
107
|
+
|
|
108
|
+
self.carconnectivity_vehicle.charging.type.add_observer(self._on_charging_type_change, Observable.ObserverEvent.VALUE_CHANGED)
|
|
109
|
+
|
|
110
|
+
electric_drive: Optional[ElectricDrive] = self.carconnectivity_vehicle.get_electric_drive()
|
|
111
|
+
if electric_drive is not None:
|
|
112
|
+
electric_drive.level.add_observer(self._on_battery_level_change, Observable.ObserverEvent.VALUE_CHANGED)
|
|
105
113
|
self.session_factory.remove()
|
|
106
114
|
|
|
107
115
|
def __on_charging_state_change(self, element: EnumAttribute[Charging.ChargingState], flags: Observable.ObserverEvent) -> None:
|
|
108
116
|
del flags
|
|
109
|
-
if self.
|
|
117
|
+
if self.carconnectivity_vehicle is None:
|
|
110
118
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
111
119
|
|
|
112
120
|
if element.enabled:
|
|
@@ -115,8 +123,9 @@ class ChargingAgent(BaseAgent):
|
|
|
115
123
|
if self.last_charging_state is not None:
|
|
116
124
|
self.last_charging_state = session.merge(self.last_charging_state)
|
|
117
125
|
session.refresh(self.last_charging_state)
|
|
118
|
-
if
|
|
119
|
-
and
|
|
126
|
+
if element.last_updated is not None \
|
|
127
|
+
and (self.last_charging_state is None or (self.last_charging_state.state != element.value
|
|
128
|
+
and element.last_updated > self.last_charging_state.last_date)):
|
|
120
129
|
new_charging_state: ChargingState = ChargingState(vin=self.vehicle.vin, first_date=element.last_updated,
|
|
121
130
|
last_date=element.last_updated, state=element.value)
|
|
122
131
|
try:
|
|
@@ -127,6 +136,7 @@ class ChargingAgent(BaseAgent):
|
|
|
127
136
|
except DatabaseError as err:
|
|
128
137
|
session.rollback()
|
|
129
138
|
LOG.error('DatabaseError while adding charging state for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
139
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
130
140
|
|
|
131
141
|
elif self.last_charging_state is not None and self.last_charging_state.state == element.value and element.last_updated is not None:
|
|
132
142
|
if self.last_charging_state.last_date is None or element.last_updated > self.last_charging_state.last_date:
|
|
@@ -137,6 +147,7 @@ class ChargingAgent(BaseAgent):
|
|
|
137
147
|
except DatabaseError as err:
|
|
138
148
|
session.rollback()
|
|
139
149
|
LOG.error('DatabaseError while updating charging state for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
150
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
140
151
|
|
|
141
152
|
with self.last_charging_session_lock:
|
|
142
153
|
if self.last_charging_session is not None:
|
|
@@ -161,10 +172,12 @@ class ChargingAgent(BaseAgent):
|
|
|
161
172
|
try:
|
|
162
173
|
self.last_charging_session.session_end_date = None
|
|
163
174
|
self.last_charging_session.end_level = None
|
|
175
|
+
self._update_session_charging_type(session, self.last_charging_session)
|
|
164
176
|
session.commit()
|
|
165
177
|
except DatabaseError as err:
|
|
166
178
|
session.rollback()
|
|
167
179
|
LOG.error('DatabaseError while updating charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
180
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
168
181
|
else:
|
|
169
182
|
LOG.info("Starting new charging session for vehicle %s", self.vehicle.vin)
|
|
170
183
|
new_session: ChargingSession = ChargingSession(vin=self.vehicle.vin, session_start_date=element.last_changed)
|
|
@@ -179,6 +192,7 @@ class ChargingAgent(BaseAgent):
|
|
|
179
192
|
except DatabaseError as err:
|
|
180
193
|
session.rollback()
|
|
181
194
|
LOG.error('DatabaseError while adding charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
195
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
182
196
|
else:
|
|
183
197
|
if self.last_charging_session.was_started():
|
|
184
198
|
LOG.debug("Continuing existing charging session for vehicle %s", self.vehicle.vin)
|
|
@@ -193,9 +207,10 @@ class ChargingAgent(BaseAgent):
|
|
|
193
207
|
except DatabaseError as err:
|
|
194
208
|
session.rollback()
|
|
195
209
|
LOG.error('DatabaseError while starting charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
210
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
196
211
|
# Update startlevel at beginning of charging
|
|
197
|
-
if self.last_charging_session is not None and isinstance(self.
|
|
198
|
-
electric_drive: Optional[ElectricDrive] = self.
|
|
212
|
+
if self.last_charging_session is not None and isinstance(self.carconnectivity_vehicle, ElectricVehicle):
|
|
213
|
+
electric_drive: Optional[ElectricDrive] = self.carconnectivity_vehicle.get_electric_drive()
|
|
199
214
|
if electric_drive is not None and electric_drive.level.enabled and electric_drive.level.value is not None:
|
|
200
215
|
try:
|
|
201
216
|
self.last_charging_session.start_level = electric_drive.level.value
|
|
@@ -203,6 +218,7 @@ class ChargingAgent(BaseAgent):
|
|
|
203
218
|
except DatabaseError as err:
|
|
204
219
|
session.rollback()
|
|
205
220
|
LOG.error('DatabaseError while setting start level for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
221
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
206
222
|
elif element.value not in (Charging.ChargingState.CHARGING, Charging.ChargingState.CONSERVATION) \
|
|
207
223
|
and self.carconnectivity_last_charging_state in (Charging.ChargingState.CHARGING, Charging.ChargingState.CONSERVATION):
|
|
208
224
|
if self.last_charging_session is not None and not self.last_charging_session.was_ended():
|
|
@@ -213,8 +229,9 @@ class ChargingAgent(BaseAgent):
|
|
|
213
229
|
except DatabaseError as err:
|
|
214
230
|
session.rollback()
|
|
215
231
|
LOG.error('DatabaseError while ending charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
216
|
-
|
|
217
|
-
|
|
232
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
233
|
+
if isinstance(self.carconnectivity_vehicle, ElectricVehicle):
|
|
234
|
+
electric_drive: Optional[ElectricDrive] = self.carconnectivity_vehicle.get_electric_drive()
|
|
218
235
|
if electric_drive is not None and electric_drive.level.enabled and electric_drive.level.value is not None:
|
|
219
236
|
try:
|
|
220
237
|
self.last_charging_session.end_level = electric_drive.level.value
|
|
@@ -222,12 +239,13 @@ class ChargingAgent(BaseAgent):
|
|
|
222
239
|
except DatabaseError as err:
|
|
223
240
|
session.rollback()
|
|
224
241
|
LOG.error('DatabaseError while setting start level for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
242
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
225
243
|
self.carconnectivity_last_charging_state = element.value
|
|
226
244
|
self.session_factory.remove()
|
|
227
245
|
|
|
228
246
|
def __on_charging_rate_change(self, element: SpeedAttribute, flags: Observable.ObserverEvent) -> None:
|
|
229
247
|
del flags
|
|
230
|
-
if self.
|
|
248
|
+
if self.carconnectivity_vehicle is None:
|
|
231
249
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
232
250
|
if element.enabled:
|
|
233
251
|
with self.last_charging_rate_lock:
|
|
@@ -235,8 +253,9 @@ class ChargingAgent(BaseAgent):
|
|
|
235
253
|
if self.last_charging_rate is not None:
|
|
236
254
|
self.last_charging_rate = session.merge(self.last_charging_rate)
|
|
237
255
|
session.refresh(self.last_charging_rate)
|
|
238
|
-
if
|
|
239
|
-
and
|
|
256
|
+
if element.last_updated is not None \
|
|
257
|
+
and (self.last_charging_rate is None or (self.last_charging_rate.rate != element.value
|
|
258
|
+
and element.last_updated > self.last_charging_rate.last_date)):
|
|
240
259
|
new_charging_rate: ChargingRate = ChargingRate(vin=self.vehicle.vin, first_date=element.last_updated,
|
|
241
260
|
last_date=element.last_updated, rate=element.value)
|
|
242
261
|
try:
|
|
@@ -247,6 +266,7 @@ class ChargingAgent(BaseAgent):
|
|
|
247
266
|
except DatabaseError as err:
|
|
248
267
|
session.rollback()
|
|
249
268
|
LOG.error('DatabaseError while adding charging rate for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
269
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
250
270
|
elif self.last_charging_rate is not None and self.last_charging_rate.rate == element.value and element.last_updated is not None:
|
|
251
271
|
if self.last_charging_rate.last_date is None or element.last_updated > self.last_charging_rate.last_date:
|
|
252
272
|
try:
|
|
@@ -256,11 +276,12 @@ class ChargingAgent(BaseAgent):
|
|
|
256
276
|
except DatabaseError as err:
|
|
257
277
|
session.rollback()
|
|
258
278
|
LOG.error('DatabaseError while updating charging rate for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
279
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
259
280
|
self.session_factory.remove()
|
|
260
281
|
|
|
261
282
|
def __on_charging_power_change(self, element: PowerAttribute, flags: Observable.ObserverEvent) -> None:
|
|
262
283
|
del flags
|
|
263
|
-
if self.
|
|
284
|
+
if self.carconnectivity_vehicle is None:
|
|
264
285
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
265
286
|
if element.enabled:
|
|
266
287
|
with self.last_charging_power_lock:
|
|
@@ -268,8 +289,9 @@ class ChargingAgent(BaseAgent):
|
|
|
268
289
|
if self.last_charging_power is not None:
|
|
269
290
|
self.last_charging_power = session.merge(self.last_charging_power)
|
|
270
291
|
session.refresh(self.last_charging_power)
|
|
271
|
-
if
|
|
272
|
-
and
|
|
292
|
+
if element.last_updated is not None \
|
|
293
|
+
and (self.last_charging_power is None or (self.last_charging_power.power != element.value
|
|
294
|
+
and element.last_updated > self.last_charging_power.last_date)):
|
|
273
295
|
new_charging_power: ChargingPower = ChargingPower(vin=self.vehicle.vin, first_date=element.last_updated,
|
|
274
296
|
last_date=element.last_updated, power=element.value)
|
|
275
297
|
try:
|
|
@@ -280,6 +302,7 @@ class ChargingAgent(BaseAgent):
|
|
|
280
302
|
except DatabaseError as err:
|
|
281
303
|
session.rollback()
|
|
282
304
|
LOG.error('DatabaseError while adding charging power for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
305
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
283
306
|
elif self.last_charging_power is not None and self.last_charging_power.power == element.value and element.last_updated is not None:
|
|
284
307
|
if self.last_charging_power.last_date is None or element.last_updated > self.last_charging_power.last_date:
|
|
285
308
|
try:
|
|
@@ -288,11 +311,12 @@ class ChargingAgent(BaseAgent):
|
|
|
288
311
|
except DatabaseError as err:
|
|
289
312
|
session.rollback()
|
|
290
313
|
LOG.error('DatabaseError while updating charging power for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
314
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
291
315
|
self.session_factory.remove()
|
|
292
316
|
|
|
293
317
|
def __on_connector_state_change(self, element: EnumAttribute[ChargingConnector.ChargingConnectorConnectionState], flags: Observable.ObserverEvent) -> None:
|
|
294
318
|
del flags
|
|
295
|
-
if self.
|
|
319
|
+
if self.carconnectivity_vehicle is None:
|
|
296
320
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
297
321
|
|
|
298
322
|
with self.session_factory() as session:
|
|
@@ -302,6 +326,7 @@ class ChargingAgent(BaseAgent):
|
|
|
302
326
|
session.refresh(self.last_charging_session)
|
|
303
327
|
|
|
304
328
|
if element.value == ChargingConnector.ChargingConnectorConnectionState.CONNECTED \
|
|
329
|
+
and self.carconnectivity_last_connector_state is not None \
|
|
305
330
|
and self.carconnectivity_last_connector_state != ChargingConnector.ChargingConnectorConnectionState.CONNECTED:
|
|
306
331
|
if self.last_charging_session is None or self.last_charging_session.is_closed():
|
|
307
332
|
LOG.info("Starting new charging session for vehicle %s due to connector connected state", self.vehicle.vin)
|
|
@@ -316,6 +341,7 @@ class ChargingAgent(BaseAgent):
|
|
|
316
341
|
except DatabaseError as err:
|
|
317
342
|
session.rollback()
|
|
318
343
|
LOG.error('DatabaseError while adding charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
344
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
319
345
|
elif not self.last_charging_session.was_connected():
|
|
320
346
|
LOG.debug("Continuing existing charging session for vehicle %s, writing connected date", self.vehicle.vin)
|
|
321
347
|
try:
|
|
@@ -326,6 +352,7 @@ class ChargingAgent(BaseAgent):
|
|
|
326
352
|
except DatabaseError as err:
|
|
327
353
|
session.rollback()
|
|
328
354
|
LOG.error('DatabaseError while starting charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
355
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
329
356
|
elif element.value != ChargingConnector.ChargingConnectorConnectionState.CONNECTED \
|
|
330
357
|
and self.carconnectivity_last_connector_state == ChargingConnector.ChargingConnectorConnectionState.CONNECTED:
|
|
331
358
|
if self.last_charging_session is not None and not self.last_charging_session.was_disconnected():
|
|
@@ -336,22 +363,29 @@ class ChargingAgent(BaseAgent):
|
|
|
336
363
|
except DatabaseError as err:
|
|
337
364
|
session.rollback()
|
|
338
365
|
LOG.error('DatabaseError while ending charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
366
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
339
367
|
# Create charging session when connected at startup
|
|
340
368
|
elif element.value == ChargingConnector.ChargingConnectorConnectionState.CONNECTED \
|
|
341
369
|
and self.carconnectivity_last_connector_state == ChargingConnector.ChargingConnectorConnectionState.CONNECTED:
|
|
342
370
|
if self.last_charging_session is None or self.last_charging_session.is_closed():
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
371
|
+
# when the incoming connected state was during the last session, this is a continuation
|
|
372
|
+
if self.last_charging_session is None or element.last_changed is not None and element.last_changed > \
|
|
373
|
+
(self.last_charging_session.session_end_date
|
|
374
|
+
or self.last_charging_session.plug_unlocked_date
|
|
375
|
+
or datetime.min.replace(tzinfo=timezone.utc)):
|
|
376
|
+
LOG.info("Starting new charging session for vehicle %s due to connector connected state on startup", self.vehicle.vin)
|
|
377
|
+
new_session: ChargingSession = ChargingSession(vin=self.vehicle.vin, plug_connected_date=element.last_changed)
|
|
378
|
+
try:
|
|
379
|
+
session.add(new_session)
|
|
380
|
+
self._update_session_odometer(session, new_session)
|
|
381
|
+
self._update_session_position(session, new_session)
|
|
382
|
+
session.commit()
|
|
383
|
+
LOG.debug('Added new charging session for vehicle %s to database', self.vehicle.vin)
|
|
384
|
+
self.last_charging_session = new_session
|
|
385
|
+
except DatabaseError as err:
|
|
386
|
+
session.rollback()
|
|
387
|
+
LOG.error('DatabaseError while adding charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
388
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
355
389
|
elif self.last_charging_session is not None and not self.last_charging_session.was_connected():
|
|
356
390
|
try:
|
|
357
391
|
self.last_charging_session.plug_connected_date = element.last_changed
|
|
@@ -360,12 +394,13 @@ class ChargingAgent(BaseAgent):
|
|
|
360
394
|
except DatabaseError as err:
|
|
361
395
|
session.rollback()
|
|
362
396
|
LOG.error('DatabaseError while changing charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
397
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
363
398
|
self.carconnectivity_last_connector_state = element.value
|
|
364
399
|
self.session_factory.remove()
|
|
365
400
|
|
|
366
401
|
def __on_connector_lock_state_change(self, element: EnumAttribute[ChargingConnector.ChargingConnectorLockState], flags: Observable.ObserverEvent) -> None:
|
|
367
402
|
del flags
|
|
368
|
-
if self.
|
|
403
|
+
if self.carconnectivity_vehicle is None:
|
|
369
404
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
370
405
|
|
|
371
406
|
with self.session_factory() as session:
|
|
@@ -377,18 +412,33 @@ class ChargingAgent(BaseAgent):
|
|
|
377
412
|
if element.value == ChargingConnector.ChargingConnectorLockState.LOCKED \
|
|
378
413
|
and self.carconnectivity_last_connector_lock_state != ChargingConnector.ChargingConnectorLockState.LOCKED:
|
|
379
414
|
if self.last_charging_session is None or self.last_charging_session.is_closed():
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
415
|
+
# In case this was an interrupted charging session (interrupt no longer than 24hours), continue by erasing end time
|
|
416
|
+
if self.last_charging_session is not None and not self.last_charging_session.was_disconnected() \
|
|
417
|
+
and (self.last_charging_session.plug_unlocked_date is None
|
|
418
|
+
or self.last_charging_session.plug_unlocked_date > ((element.last_changed or datetime.now(timezone.utc))
|
|
419
|
+
- timedelta(hours=24))):
|
|
420
|
+
LOG.debug("found a closed charging session that was not disconneced. This could be an interrupted session we want to continue")
|
|
421
|
+
try:
|
|
422
|
+
self.last_charging_session.plug_unlocked_date = None
|
|
423
|
+
session.commit()
|
|
424
|
+
except DatabaseError as err:
|
|
425
|
+
session.rollback()
|
|
426
|
+
LOG.error('DatabaseError while changing charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
427
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
428
|
+
else:
|
|
429
|
+
LOG.info("Starting new charging session for vehicle %s due to connector locked state", self.vehicle.vin)
|
|
430
|
+
new_session: ChargingSession = ChargingSession(vin=self.vehicle.vin, plug_locked_date=element.last_changed)
|
|
431
|
+
try:
|
|
432
|
+
session.add(new_session)
|
|
433
|
+
self._update_session_odometer(session, new_session)
|
|
434
|
+
self._update_session_position(session, new_session)
|
|
435
|
+
session.commit()
|
|
436
|
+
LOG.debug('Added new charging session for vehicle %s to database', self.vehicle.vin)
|
|
437
|
+
self.last_charging_session = new_session
|
|
438
|
+
except DatabaseError as err:
|
|
439
|
+
session.rollback()
|
|
440
|
+
LOG.error('DatabaseError while adding charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
441
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
392
442
|
elif not self.last_charging_session.was_locked():
|
|
393
443
|
LOG.debug("Continuing existing charging session for vehicle %s, writing locked date", self.vehicle.vin)
|
|
394
444
|
try:
|
|
@@ -399,6 +449,7 @@ class ChargingAgent(BaseAgent):
|
|
|
399
449
|
except DatabaseError as err:
|
|
400
450
|
session.rollback()
|
|
401
451
|
LOG.error('DatabaseError while starting charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
452
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
402
453
|
elif element.value != ChargingConnector.ChargingConnectorLockState.LOCKED \
|
|
403
454
|
and self.carconnectivity_last_connector_lock_state == ChargingConnector.ChargingConnectorLockState.LOCKED:
|
|
404
455
|
if self.last_charging_session is not None and not self.last_charging_session.was_unlocked():
|
|
@@ -409,6 +460,7 @@ class ChargingAgent(BaseAgent):
|
|
|
409
460
|
except DatabaseError as err:
|
|
410
461
|
session.rollback()
|
|
411
462
|
LOG.error('DatabaseError while ending charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
463
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
412
464
|
# Create charging session when locked at startup
|
|
413
465
|
elif element.value == ChargingConnector.ChargingConnectorLockState.LOCKED \
|
|
414
466
|
and self.carconnectivity_last_connector_lock_state == ChargingConnector.ChargingConnectorLockState.LOCKED:
|
|
@@ -425,6 +477,7 @@ class ChargingAgent(BaseAgent):
|
|
|
425
477
|
except DatabaseError as err:
|
|
426
478
|
session.rollback()
|
|
427
479
|
LOG.error('DatabaseError while adding charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
480
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
428
481
|
elif self.last_charging_session is not None and not self.last_charging_session.was_locked():
|
|
429
482
|
try:
|
|
430
483
|
self.last_charging_session.plug_locked_date = element.last_changed
|
|
@@ -433,62 +486,110 @@ class ChargingAgent(BaseAgent):
|
|
|
433
486
|
except DatabaseError as err:
|
|
434
487
|
session.rollback()
|
|
435
488
|
LOG.error('DatabaseError while changing charging session for vehicle %s to database: %s', self.vehicle.vin, err)
|
|
489
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
436
490
|
self.carconnectivity_last_connector_lock_state = element.value
|
|
437
491
|
self.session_factory.remove()
|
|
438
492
|
|
|
439
493
|
def _update_session_odometer(self, session: Session, charging_session: ChargingSession) -> None:
|
|
440
|
-
if self.
|
|
494
|
+
if self.carconnectivity_vehicle is None:
|
|
441
495
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
442
|
-
if self.
|
|
496
|
+
if self.carconnectivity_vehicle.odometer.enabled:
|
|
443
497
|
if charging_session.session_odometer is None:
|
|
444
498
|
try:
|
|
445
|
-
charging_session.session_odometer = self.
|
|
499
|
+
charging_session.session_odometer = self.carconnectivity_vehicle.odometer.value
|
|
446
500
|
except DatabaseError as err:
|
|
447
501
|
session.rollback()
|
|
448
502
|
LOG.error('DatabaseError while updating odometer for charging session of vehicle %s in database: %s', self.vehicle.vin, err)
|
|
503
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
449
504
|
|
|
450
505
|
def _update_session_charging_type(self, session: Session, charging_session: ChargingSession) -> None:
|
|
451
|
-
if self.
|
|
506
|
+
if self.carconnectivity_vehicle is None:
|
|
452
507
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
453
|
-
if isinstance(self.
|
|
454
|
-
and self.
|
|
508
|
+
if isinstance(self.carconnectivity_vehicle, ElectricVehicle) and self.carconnectivity_vehicle.charging.type.enabled \
|
|
509
|
+
and self.carconnectivity_vehicle.charging.type.value is not None:
|
|
455
510
|
if charging_session.charging_type is None:
|
|
456
511
|
try:
|
|
457
|
-
charging_session.charging_type = self.
|
|
512
|
+
charging_session.charging_type = self.carconnectivity_vehicle.charging.type.value
|
|
458
513
|
except DatabaseError as err:
|
|
459
514
|
session.rollback()
|
|
460
515
|
LOG.error('DatabaseError while updating charging type for charging session of vehicle %s in database: %s', self.vehicle.vin, err)
|
|
516
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
461
517
|
|
|
462
518
|
def _update_session_position(self, session: Session, charging_session: ChargingSession) -> None:
|
|
463
|
-
if self.
|
|
519
|
+
if self.carconnectivity_vehicle is None:
|
|
464
520
|
raise ValueError("Vehicle's carconnectivity_vehicle attribute is None")
|
|
465
|
-
if self.
|
|
466
|
-
and self.
|
|
467
|
-
and self.
|
|
468
|
-
and self.
|
|
521
|
+
if self.carconnectivity_vehicle.position.enabled and self.carconnectivity_vehicle.position.latitude.enabled \
|
|
522
|
+
and self.carconnectivity_vehicle.position.longitude.enabled \
|
|
523
|
+
and self.carconnectivity_vehicle.position.latitude.value is not None \
|
|
524
|
+
and self.carconnectivity_vehicle.position.longitude.value is not None:
|
|
469
525
|
if charging_session.session_position_latitude is None and charging_session.session_position_longitude is None:
|
|
470
526
|
try:
|
|
471
|
-
charging_session.session_position_latitude = self.
|
|
472
|
-
charging_session.session_position_longitude = self.
|
|
527
|
+
charging_session.session_position_latitude = self.carconnectivity_vehicle.position.latitude.value
|
|
528
|
+
charging_session.session_position_longitude = self.carconnectivity_vehicle.position.longitude.value
|
|
473
529
|
except DatabaseError as err:
|
|
474
530
|
session.rollback()
|
|
475
531
|
LOG.error('DatabaseError while updating position for charging session of vehicle %s in database: %s', self.vehicle.vin, err)
|
|
476
|
-
|
|
477
|
-
|
|
532
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
533
|
+
if charging_session.location is None and self.carconnectivity_vehicle.position.location.enabled:
|
|
534
|
+
location: Location = Location.from_carconnectivity_location(location=self.carconnectivity_vehicle.position.location)
|
|
478
535
|
try:
|
|
479
536
|
location = session.merge(location)
|
|
480
537
|
charging_session.location = location
|
|
481
538
|
except DatabaseError as err:
|
|
482
539
|
session.rollback()
|
|
483
540
|
LOG.error('DatabaseError while merging location for charging session of vehicle %s in database: %s', self.vehicle.vin, err)
|
|
541
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
484
542
|
if charging_session.charging_station is None \
|
|
485
|
-
and isinstance(self.
|
|
486
|
-
and self.
|
|
543
|
+
and isinstance(self.carconnectivity_vehicle, ElectricVehicle) and self.carconnectivity_vehicle.charging is not None \
|
|
544
|
+
and self.carconnectivity_vehicle.charging.enabled and self.carconnectivity_vehicle.charging.charging_station.enabled:
|
|
487
545
|
charging_station: ChargingStation = ChargingStation.from_carconnectivity_charging_station(
|
|
488
|
-
charging_station=self.
|
|
546
|
+
charging_station=self.carconnectivity_vehicle.charging.charging_station)
|
|
489
547
|
try:
|
|
490
548
|
charging_station = session.merge(charging_station)
|
|
491
549
|
charging_session.charging_station = charging_station
|
|
492
550
|
except DatabaseError as err:
|
|
493
551
|
session.rollback()
|
|
494
552
|
LOG.error('DatabaseError while merging charging station for charging session of vehicle %s in database: %s', self.vehicle.vin, err)
|
|
553
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
554
|
+
|
|
555
|
+
def _on_charging_type_change(self, element: EnumAttribute[Charging.ChargingType], flags: Observable.ObserverEvent) -> None:
|
|
556
|
+
del flags
|
|
557
|
+
if element.enabled:
|
|
558
|
+
with self.session_factory() as session:
|
|
559
|
+
with self.last_charging_session_lock:
|
|
560
|
+
if self.last_charging_session is not None:
|
|
561
|
+
self.last_charging_session = session.merge(self.last_charging_session)
|
|
562
|
+
session.refresh(self.last_charging_session)
|
|
563
|
+
if self.last_charging_session is not None and not self.last_charging_session.is_closed() \
|
|
564
|
+
and element.value in [Charging.ChargingType.AC, Charging.ChargingType.DC]:
|
|
565
|
+
try:
|
|
566
|
+
self.last_charging_session.charging_type = element.value
|
|
567
|
+
session.commit()
|
|
568
|
+
except DatabaseError as err:
|
|
569
|
+
session.rollback()
|
|
570
|
+
LOG.error('DatabaseError while updating type of charging session for vehicle %s in database: %s', self.vehicle.vin, err)
|
|
571
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
572
|
+
self.session_factory.remove()
|
|
573
|
+
|
|
574
|
+
def _on_battery_level_change(self, element: FloatAttribute, flags: Observable.ObserverEvent) -> None:
|
|
575
|
+
del flags
|
|
576
|
+
if element.enabled and element.value is not None:
|
|
577
|
+
# We try to see if there was a late battery level update for a finished session
|
|
578
|
+
with self.session_factory() as session:
|
|
579
|
+
with self.last_charging_session_lock:
|
|
580
|
+
if self.last_charging_session is not None:
|
|
581
|
+
self.last_charging_session = session.merge(self.last_charging_session)
|
|
582
|
+
session.refresh(self.last_charging_session)
|
|
583
|
+
if self.last_charging_session is not None and self.last_charging_session.session_end_date is not None:
|
|
584
|
+
if element.last_updated is not None and (element.last_updated <= (self.last_charging_session.session_end_date + timedelta(minutes=1))):
|
|
585
|
+
# Only update if we have no end level yet or the new level is higher than the previous one (this happens with late level updates)
|
|
586
|
+
if self.last_charging_session.end_level is None or self.last_charging_session.end_level < element.value:
|
|
587
|
+
try:
|
|
588
|
+
self.last_charging_session.end_level = element.value
|
|
589
|
+
session.commit()
|
|
590
|
+
except DatabaseError as err:
|
|
591
|
+
session.rollback()
|
|
592
|
+
LOG.error('DatabaseError while updating battery level of charging session for vehicle %s in database: %s',
|
|
593
|
+
self.vehicle.vin, err)
|
|
594
|
+
self.database_plugin.healthy._set_value(value=False) # pylint: disable=protected-access
|
|
595
|
+
self.session_factory.remove()
|