arthexis 0.1.8__py3-none-any.whl → 0.1.10__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 arthexis might be problematic. Click here for more details.
- {arthexis-0.1.8.dist-info → arthexis-0.1.10.dist-info}/METADATA +87 -6
- arthexis-0.1.10.dist-info/RECORD +95 -0
- arthexis-0.1.10.dist-info/licenses/LICENSE +674 -0
- config/__init__.py +0 -1
- config/auth_app.py +0 -1
- config/celery.py +1 -2
- config/context_processors.py +1 -1
- config/offline.py +2 -0
- config/settings.py +352 -37
- config/urls.py +71 -6
- core/admin.py +1601 -200
- core/admin_history.py +50 -0
- core/admindocs.py +108 -1
- core/apps.py +161 -3
- core/auto_upgrade.py +57 -0
- core/backends.py +123 -7
- core/entity.py +62 -48
- core/fields.py +98 -0
- core/github_helper.py +25 -0
- core/github_issues.py +172 -0
- core/lcd_screen.py +1 -0
- core/liveupdate.py +25 -0
- core/log_paths.py +100 -0
- core/mailer.py +83 -0
- core/middleware.py +57 -0
- core/models.py +1279 -267
- core/notifications.py +11 -1
- core/public_wifi.py +227 -0
- core/reference_utils.py +97 -0
- core/release.py +27 -20
- core/sigil_builder.py +144 -0
- core/sigil_context.py +20 -0
- core/sigil_resolver.py +284 -0
- core/system.py +162 -29
- core/tasks.py +269 -27
- core/test_system_info.py +59 -1
- core/tests.py +644 -73
- core/tests_liveupdate.py +17 -0
- core/urls.py +2 -2
- core/user_data.py +425 -168
- core/views.py +627 -59
- core/widgets.py +51 -0
- core/workgroup_urls.py +7 -3
- core/workgroup_views.py +43 -6
- nodes/actions.py +0 -2
- nodes/admin.py +168 -285
- nodes/apps.py +9 -15
- nodes/backends.py +145 -0
- nodes/lcd.py +24 -10
- nodes/models.py +579 -179
- nodes/tasks.py +1 -5
- nodes/tests.py +894 -130
- nodes/utils.py +13 -2
- nodes/views.py +204 -28
- ocpp/admin.py +212 -63
- ocpp/apps.py +1 -1
- ocpp/consumers.py +642 -68
- ocpp/evcs.py +30 -10
- ocpp/models.py +452 -70
- ocpp/simulator.py +75 -11
- ocpp/store.py +288 -30
- ocpp/tasks.py +11 -7
- ocpp/test_export_import.py +8 -7
- ocpp/test_rfid.py +211 -16
- ocpp/tests.py +1576 -137
- ocpp/transactions_io.py +68 -22
- ocpp/urls.py +35 -2
- ocpp/views.py +701 -123
- pages/admin.py +173 -13
- pages/checks.py +0 -1
- pages/context_processors.py +39 -6
- pages/forms.py +131 -0
- pages/middleware.py +153 -0
- pages/models.py +37 -9
- pages/tests.py +1182 -42
- pages/urls.py +4 -0
- pages/utils.py +0 -1
- pages/views.py +844 -51
- arthexis-0.1.8.dist-info/RECORD +0 -80
- arthexis-0.1.8.dist-info/licenses/LICENSE +0 -21
- config/workgroup_app.py +0 -7
- core/checks.py +0 -29
- {arthexis-0.1.8.dist-info → arthexis-0.1.10.dist-info}/WHEEL +0 -0
- {arthexis-0.1.8.dist-info → arthexis-0.1.10.dist-info}/top_level.txt +0 -0
ocpp/admin.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
from django.contrib import admin
|
|
1
|
+
from django.contrib import admin, messages
|
|
2
2
|
from django import forms
|
|
3
3
|
|
|
4
4
|
import asyncio
|
|
5
5
|
from datetime import timedelta
|
|
6
6
|
import json
|
|
7
7
|
|
|
8
|
+
from django.shortcuts import redirect
|
|
8
9
|
from django.utils import timezone
|
|
9
10
|
from django.urls import path
|
|
10
11
|
from django.http import HttpResponse, HttpResponseRedirect
|
|
@@ -13,10 +14,9 @@ from django.template.response import TemplateResponse
|
|
|
13
14
|
from .models import (
|
|
14
15
|
Charger,
|
|
15
16
|
Simulator,
|
|
16
|
-
|
|
17
|
+
MeterValue,
|
|
17
18
|
Transaction,
|
|
18
19
|
Location,
|
|
19
|
-
ElectricVehicle,
|
|
20
20
|
)
|
|
21
21
|
from .simulator import ChargePointSimulator
|
|
22
22
|
from . import store
|
|
@@ -24,8 +24,7 @@ from .transactions_io import (
|
|
|
24
24
|
export_transactions,
|
|
25
25
|
import_transactions as import_transactions_data,
|
|
26
26
|
)
|
|
27
|
-
from core.
|
|
28
|
-
from .models import RFID
|
|
27
|
+
from core.user_data import EntityModelAdmin
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
class LocationAdminForm(forms.ModelForm):
|
|
@@ -39,9 +38,7 @@ class LocationAdminForm(forms.ModelForm):
|
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
class Media:
|
|
42
|
-
css = {
|
|
43
|
-
"all": ("https://unpkg.com/leaflet@1.9.4/dist/leaflet.css",)
|
|
44
|
-
}
|
|
41
|
+
css = {"all": ("https://unpkg.com/leaflet@1.9.4/dist/leaflet.css",)}
|
|
45
42
|
js = (
|
|
46
43
|
"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js",
|
|
47
44
|
"ocpp/charger_map.js",
|
|
@@ -60,29 +57,91 @@ class TransactionImportForm(forms.Form):
|
|
|
60
57
|
file = forms.FileField()
|
|
61
58
|
|
|
62
59
|
|
|
60
|
+
class LogViewAdminMixin:
|
|
61
|
+
"""Mixin providing an admin view to display charger or simulator logs."""
|
|
62
|
+
|
|
63
|
+
log_type = "charger"
|
|
64
|
+
log_template_name = "admin/ocpp/log_view.html"
|
|
65
|
+
|
|
66
|
+
def get_log_identifier(self, obj): # pragma: no cover - mixin hook
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
def get_log_title(self, obj):
|
|
70
|
+
return f"Log for {obj}"
|
|
71
|
+
|
|
72
|
+
def get_urls(self):
|
|
73
|
+
urls = super().get_urls()
|
|
74
|
+
info = self.model._meta.app_label, self.model._meta.model_name
|
|
75
|
+
custom = [
|
|
76
|
+
path(
|
|
77
|
+
"<path:object_id>/log/",
|
|
78
|
+
self.admin_site.admin_view(self.log_view),
|
|
79
|
+
name=f"{info[0]}_{info[1]}_log",
|
|
80
|
+
),
|
|
81
|
+
]
|
|
82
|
+
return custom + urls
|
|
83
|
+
|
|
84
|
+
def log_view(self, request, object_id):
|
|
85
|
+
obj = self.get_object(request, object_id)
|
|
86
|
+
if obj is None:
|
|
87
|
+
self.message_user(request, "Log is not available.", messages.ERROR)
|
|
88
|
+
return redirect("..")
|
|
89
|
+
identifier = self.get_log_identifier(obj)
|
|
90
|
+
log_entries = store.get_logs(identifier, log_type=self.log_type)
|
|
91
|
+
log_file = store._file_path(identifier, log_type=self.log_type)
|
|
92
|
+
context = {
|
|
93
|
+
**self.admin_site.each_context(request),
|
|
94
|
+
"opts": self.model._meta,
|
|
95
|
+
"original": obj,
|
|
96
|
+
"title": self.get_log_title(obj),
|
|
97
|
+
"log_entries": log_entries,
|
|
98
|
+
"log_file": str(log_file),
|
|
99
|
+
"log_identifier": identifier,
|
|
100
|
+
}
|
|
101
|
+
return TemplateResponse(request, self.log_template_name, context)
|
|
102
|
+
|
|
103
|
+
|
|
63
104
|
@admin.register(Location)
|
|
64
|
-
class LocationAdmin(
|
|
105
|
+
class LocationAdmin(EntityModelAdmin):
|
|
65
106
|
form = LocationAdminForm
|
|
66
107
|
list_display = ("name", "latitude", "longitude")
|
|
108
|
+
change_form_template = "admin/ocpp/location/change_form.html"
|
|
67
109
|
|
|
68
110
|
|
|
69
111
|
@admin.register(Charger)
|
|
70
|
-
class ChargerAdmin(
|
|
112
|
+
class ChargerAdmin(LogViewAdminMixin, EntityModelAdmin):
|
|
71
113
|
fieldsets = (
|
|
72
114
|
(
|
|
73
115
|
"General",
|
|
74
116
|
{
|
|
75
117
|
"fields": (
|
|
76
118
|
"charger_id",
|
|
119
|
+
"display_name",
|
|
77
120
|
"connector_id",
|
|
78
|
-
"
|
|
121
|
+
"location",
|
|
122
|
+
"last_path",
|
|
79
123
|
"last_heartbeat",
|
|
80
124
|
"last_meter_values",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
125
|
+
"firmware_status",
|
|
126
|
+
"firmware_status_info",
|
|
127
|
+
"firmware_timestamp",
|
|
83
128
|
)
|
|
84
129
|
},
|
|
85
130
|
),
|
|
131
|
+
(
|
|
132
|
+
"Diagnostics",
|
|
133
|
+
{
|
|
134
|
+
"fields": (
|
|
135
|
+
"diagnostics_status",
|
|
136
|
+
"diagnostics_timestamp",
|
|
137
|
+
"diagnostics_location",
|
|
138
|
+
)
|
|
139
|
+
},
|
|
140
|
+
),
|
|
141
|
+
(
|
|
142
|
+
"Configuration",
|
|
143
|
+
{"fields": ("public_display", "require_rfid")},
|
|
144
|
+
),
|
|
86
145
|
(
|
|
87
146
|
"References",
|
|
88
147
|
{
|
|
@@ -90,13 +149,22 @@ class ChargerAdmin(admin.ModelAdmin):
|
|
|
90
149
|
},
|
|
91
150
|
),
|
|
92
151
|
)
|
|
93
|
-
readonly_fields = (
|
|
152
|
+
readonly_fields = (
|
|
153
|
+
"last_heartbeat",
|
|
154
|
+
"last_meter_values",
|
|
155
|
+
"firmware_status",
|
|
156
|
+
"firmware_status_info",
|
|
157
|
+
"firmware_timestamp",
|
|
158
|
+
)
|
|
94
159
|
list_display = (
|
|
95
160
|
"charger_id",
|
|
96
161
|
"connector_id",
|
|
97
162
|
"location_name",
|
|
98
|
-
"
|
|
163
|
+
"require_rfid_display",
|
|
164
|
+
"public_display",
|
|
99
165
|
"last_heartbeat",
|
|
166
|
+
"firmware_status",
|
|
167
|
+
"firmware_timestamp",
|
|
100
168
|
"session_kw",
|
|
101
169
|
"total_kw_display",
|
|
102
170
|
"page_link",
|
|
@@ -109,6 +177,12 @@ class ChargerAdmin(admin.ModelAdmin):
|
|
|
109
177
|
def get_view_on_site_url(self, obj=None):
|
|
110
178
|
return obj.get_absolute_url() if obj else None
|
|
111
179
|
|
|
180
|
+
def require_rfid_display(self, obj):
|
|
181
|
+
return obj.require_rfid
|
|
182
|
+
|
|
183
|
+
require_rfid_display.boolean = True
|
|
184
|
+
require_rfid_display.short_description = "RFID Auth"
|
|
185
|
+
|
|
112
186
|
def page_link(self, obj):
|
|
113
187
|
from django.utils.html import format_html
|
|
114
188
|
|
|
@@ -116,7 +190,7 @@ class ChargerAdmin(admin.ModelAdmin):
|
|
|
116
190
|
'<a href="{}" target="_blank">open</a>', obj.get_absolute_url()
|
|
117
191
|
)
|
|
118
192
|
|
|
119
|
-
page_link.short_description = "Landing
|
|
193
|
+
page_link.short_description = "Landing"
|
|
120
194
|
|
|
121
195
|
def qr_link(self, obj):
|
|
122
196
|
from django.utils.html import format_html
|
|
@@ -133,19 +207,25 @@ class ChargerAdmin(admin.ModelAdmin):
|
|
|
133
207
|
from django.utils.html import format_html
|
|
134
208
|
from django.urls import reverse
|
|
135
209
|
|
|
136
|
-
url = reverse("
|
|
210
|
+
url = reverse("admin:ocpp_charger_log", args=[obj.pk])
|
|
137
211
|
return format_html('<a href="{}" target="_blank">view</a>', url)
|
|
138
212
|
|
|
139
213
|
log_link.short_description = "Log"
|
|
140
|
-
|
|
214
|
+
|
|
215
|
+
def get_log_identifier(self, obj):
|
|
216
|
+
return store.identity_key(obj.charger_id, obj.connector_id)
|
|
217
|
+
|
|
141
218
|
def status_link(self, obj):
|
|
142
219
|
from django.utils.html import format_html
|
|
143
220
|
from django.urls import reverse
|
|
144
221
|
|
|
145
|
-
url = reverse(
|
|
222
|
+
url = reverse(
|
|
223
|
+
"charger-status-connector",
|
|
224
|
+
args=[obj.charger_id, obj.connector_slug],
|
|
225
|
+
)
|
|
146
226
|
return format_html('<a href="{}" target="_blank">status</a>', url)
|
|
147
227
|
|
|
148
|
-
status_link.short_description = "Status
|
|
228
|
+
status_link.short_description = "Status"
|
|
149
229
|
|
|
150
230
|
def location_name(self, obj):
|
|
151
231
|
return obj.location.name if obj.location else ""
|
|
@@ -169,7 +249,7 @@ class ChargerAdmin(admin.ModelAdmin):
|
|
|
169
249
|
total_kw_display.short_description = "Total kW"
|
|
170
250
|
|
|
171
251
|
def session_kw(self, obj):
|
|
172
|
-
tx = store.
|
|
252
|
+
tx = store.get_transaction(obj.charger_id, obj.connector_id)
|
|
173
253
|
if tx:
|
|
174
254
|
return round(tx.kw, 2)
|
|
175
255
|
return 0.0
|
|
@@ -178,7 +258,7 @@ class ChargerAdmin(admin.ModelAdmin):
|
|
|
178
258
|
|
|
179
259
|
|
|
180
260
|
@admin.register(Simulator)
|
|
181
|
-
class SimulatorAdmin(
|
|
261
|
+
class SimulatorAdmin(LogViewAdminMixin, EntityModelAdmin):
|
|
182
262
|
list_display = (
|
|
183
263
|
"name",
|
|
184
264
|
"cp_path",
|
|
@@ -197,28 +277,100 @@ class SimulatorAdmin(admin.ModelAdmin):
|
|
|
197
277
|
"rfid",
|
|
198
278
|
("duration", "interval", "pre_charge_delay"),
|
|
199
279
|
"kw_max",
|
|
200
|
-
"repeat",
|
|
280
|
+
("repeat", "door_open"),
|
|
201
281
|
("username", "password"),
|
|
202
282
|
)
|
|
203
|
-
actions = ("start_simulator", "stop_simulator")
|
|
283
|
+
actions = ("start_simulator", "stop_simulator", "send_open_door")
|
|
284
|
+
|
|
285
|
+
log_type = "simulator"
|
|
286
|
+
|
|
287
|
+
def save_model(self, request, obj, form, change):
|
|
288
|
+
previous_door_open = False
|
|
289
|
+
if change and obj.pk:
|
|
290
|
+
previous_door_open = (
|
|
291
|
+
type(obj)
|
|
292
|
+
.objects.filter(pk=obj.pk)
|
|
293
|
+
.values_list("door_open", flat=True)
|
|
294
|
+
.first()
|
|
295
|
+
or False
|
|
296
|
+
)
|
|
297
|
+
super().save_model(request, obj, form, change)
|
|
298
|
+
if obj.door_open and not previous_door_open:
|
|
299
|
+
triggered = self._queue_door_open(request, obj)
|
|
300
|
+
if not triggered:
|
|
301
|
+
type(obj).objects.filter(pk=obj.pk).update(door_open=False)
|
|
302
|
+
obj.door_open = False
|
|
303
|
+
|
|
304
|
+
def _queue_door_open(self, request, obj) -> bool:
|
|
305
|
+
sim = store.simulators.get(obj.pk)
|
|
306
|
+
if not sim:
|
|
307
|
+
self.message_user(
|
|
308
|
+
request,
|
|
309
|
+
f"{obj.name}: simulator is not running",
|
|
310
|
+
level=messages.ERROR,
|
|
311
|
+
)
|
|
312
|
+
return False
|
|
313
|
+
type(obj).objects.filter(pk=obj.pk).update(door_open=True)
|
|
314
|
+
obj.door_open = True
|
|
315
|
+
store.add_log(
|
|
316
|
+
obj.cp_path,
|
|
317
|
+
"Door open event requested from admin",
|
|
318
|
+
log_type="simulator",
|
|
319
|
+
)
|
|
320
|
+
if hasattr(sim, "trigger_door_open"):
|
|
321
|
+
sim.trigger_door_open()
|
|
322
|
+
else: # pragma: no cover - unexpected condition
|
|
323
|
+
self.message_user(
|
|
324
|
+
request,
|
|
325
|
+
f"{obj.name}: simulator cannot send door open event",
|
|
326
|
+
level=messages.ERROR,
|
|
327
|
+
)
|
|
328
|
+
type(obj).objects.filter(pk=obj.pk).update(door_open=False)
|
|
329
|
+
obj.door_open = False
|
|
330
|
+
return False
|
|
331
|
+
type(obj).objects.filter(pk=obj.pk).update(door_open=False)
|
|
332
|
+
obj.door_open = False
|
|
333
|
+
self.message_user(
|
|
334
|
+
request,
|
|
335
|
+
f"{obj.name}: DoorOpen status notification sent",
|
|
336
|
+
)
|
|
337
|
+
return True
|
|
204
338
|
|
|
205
339
|
def running(self, obj):
|
|
206
340
|
return obj.pk in store.simulators
|
|
207
341
|
|
|
208
342
|
running.boolean = True
|
|
209
343
|
|
|
344
|
+
@admin.action(description="Send Open Door")
|
|
345
|
+
def send_open_door(self, request, queryset):
|
|
346
|
+
for obj in queryset:
|
|
347
|
+
self._queue_door_open(request, obj)
|
|
348
|
+
|
|
210
349
|
def start_simulator(self, request, queryset):
|
|
350
|
+
from django.urls import reverse
|
|
351
|
+
from django.utils.html import format_html
|
|
352
|
+
|
|
211
353
|
for obj in queryset:
|
|
212
354
|
if obj.pk in store.simulators:
|
|
213
355
|
self.message_user(request, f"{obj.name}: already running")
|
|
214
356
|
continue
|
|
357
|
+
type(obj).objects.filter(pk=obj.pk).update(door_open=False)
|
|
358
|
+
obj.door_open = False
|
|
215
359
|
store.register_log_name(obj.cp_path, obj.name, log_type="simulator")
|
|
216
360
|
sim = ChargePointSimulator(obj.as_config())
|
|
217
361
|
started, status, log_file = sim.start()
|
|
218
362
|
if started:
|
|
219
363
|
store.simulators[obj.pk] = sim
|
|
364
|
+
log_url = reverse("admin:ocpp_simulator_log", args=[obj.pk])
|
|
220
365
|
self.message_user(
|
|
221
|
-
request,
|
|
366
|
+
request,
|
|
367
|
+
format_html(
|
|
368
|
+
'{}: {}. Log: <code>{}</code> (<a href="{}" target="_blank">View Log</a>)',
|
|
369
|
+
obj.name,
|
|
370
|
+
status,
|
|
371
|
+
log_file,
|
|
372
|
+
log_url,
|
|
373
|
+
),
|
|
222
374
|
)
|
|
223
375
|
|
|
224
376
|
start_simulator.short_description = "Start selected simulators"
|
|
@@ -239,22 +391,35 @@ class SimulatorAdmin(admin.ModelAdmin):
|
|
|
239
391
|
from django.utils.html import format_html
|
|
240
392
|
from django.urls import reverse
|
|
241
393
|
|
|
242
|
-
url = reverse("
|
|
394
|
+
url = reverse("admin:ocpp_simulator_log", args=[obj.pk])
|
|
243
395
|
return format_html('<a href="{}" target="_blank">view</a>', url)
|
|
244
396
|
|
|
245
397
|
log_link.short_description = "Log"
|
|
246
398
|
|
|
399
|
+
def get_log_identifier(self, obj):
|
|
400
|
+
return obj.cp_path
|
|
247
401
|
|
|
248
|
-
|
|
249
|
-
|
|
402
|
+
|
|
403
|
+
class MeterValueInline(admin.TabularInline):
|
|
404
|
+
model = MeterValue
|
|
250
405
|
extra = 0
|
|
251
|
-
fields = (
|
|
406
|
+
fields = (
|
|
407
|
+
"timestamp",
|
|
408
|
+
"context",
|
|
409
|
+
"energy",
|
|
410
|
+
"voltage",
|
|
411
|
+
"current_import",
|
|
412
|
+
"current_offered",
|
|
413
|
+
"temperature",
|
|
414
|
+
"soc",
|
|
415
|
+
"connector_id",
|
|
416
|
+
)
|
|
252
417
|
readonly_fields = fields
|
|
253
418
|
can_delete = False
|
|
254
419
|
|
|
255
420
|
|
|
256
421
|
@admin.register(Transaction)
|
|
257
|
-
class TransactionAdmin(
|
|
422
|
+
class TransactionAdmin(EntityModelAdmin):
|
|
258
423
|
change_list_template = "admin/ocpp/transaction/change_list.html"
|
|
259
424
|
list_display = (
|
|
260
425
|
"charger",
|
|
@@ -269,7 +434,7 @@ class TransactionAdmin(admin.ModelAdmin):
|
|
|
269
434
|
readonly_fields = ("kw",)
|
|
270
435
|
list_filter = ("charger", "account")
|
|
271
436
|
date_hierarchy = "start_time"
|
|
272
|
-
inlines = [
|
|
437
|
+
inlines = [MeterValueInline]
|
|
273
438
|
|
|
274
439
|
def get_urls(self):
|
|
275
440
|
urls = super().get_urls()
|
|
@@ -301,17 +466,15 @@ class TransactionAdmin(admin.ModelAdmin):
|
|
|
301
466
|
json.dumps(data, indent=2, ensure_ascii=False),
|
|
302
467
|
content_type="application/json",
|
|
303
468
|
)
|
|
304
|
-
response[
|
|
305
|
-
"
|
|
306
|
-
|
|
469
|
+
response["Content-Disposition"] = (
|
|
470
|
+
"attachment; filename=transactions.json"
|
|
471
|
+
)
|
|
307
472
|
return response
|
|
308
473
|
else:
|
|
309
474
|
form = TransactionExportForm()
|
|
310
475
|
context = self.admin_site.each_context(request)
|
|
311
476
|
context["form"] = form
|
|
312
|
-
return TemplateResponse(
|
|
313
|
-
request, "admin/ocpp/transaction/export.html", context
|
|
314
|
-
)
|
|
477
|
+
return TemplateResponse(request, "admin/ocpp/transaction/export.html", context)
|
|
315
478
|
|
|
316
479
|
def import_view(self, request):
|
|
317
480
|
if request.method == "POST":
|
|
@@ -325,12 +488,10 @@ class TransactionAdmin(admin.ModelAdmin):
|
|
|
325
488
|
form = TransactionImportForm()
|
|
326
489
|
context = self.admin_site.each_context(request)
|
|
327
490
|
context["form"] = form
|
|
328
|
-
return TemplateResponse(
|
|
329
|
-
request, "admin/ocpp/transaction/import.html", context
|
|
330
|
-
)
|
|
491
|
+
return TemplateResponse(request, "admin/ocpp/transaction/import.html", context)
|
|
331
492
|
|
|
332
493
|
|
|
333
|
-
class
|
|
494
|
+
class MeterValueDateFilter(admin.SimpleListFilter):
|
|
334
495
|
title = "Timestamp"
|
|
335
496
|
parameter_name = "timestamp_range"
|
|
336
497
|
|
|
@@ -361,32 +522,20 @@ class MeterReadingDateFilter(admin.SimpleListFilter):
|
|
|
361
522
|
return queryset
|
|
362
523
|
|
|
363
524
|
|
|
364
|
-
@admin.register(
|
|
365
|
-
class
|
|
525
|
+
@admin.register(MeterValue)
|
|
526
|
+
class MeterValueAdmin(EntityModelAdmin):
|
|
366
527
|
list_display = (
|
|
367
528
|
"charger",
|
|
368
529
|
"timestamp",
|
|
369
|
-
"
|
|
370
|
-
"
|
|
530
|
+
"context",
|
|
531
|
+
"energy",
|
|
532
|
+
"voltage",
|
|
533
|
+
"current_import",
|
|
534
|
+
"current_offered",
|
|
535
|
+
"temperature",
|
|
536
|
+
"soc",
|
|
371
537
|
"connector_id",
|
|
372
538
|
"transaction",
|
|
373
539
|
)
|
|
374
540
|
date_hierarchy = "timestamp"
|
|
375
|
-
list_filter = ("charger",
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
@admin.register(ElectricVehicle)
|
|
379
|
-
class ElectricVehicleAdmin(admin.ModelAdmin):
|
|
380
|
-
list_display = ("vin", "license_plate", "brand", "model", "account")
|
|
381
|
-
search_fields = (
|
|
382
|
-
"vin",
|
|
383
|
-
"license_plate",
|
|
384
|
-
"brand__name",
|
|
385
|
-
"model__name",
|
|
386
|
-
"account__name",
|
|
387
|
-
)
|
|
388
|
-
fields = ("account", "vin", "license_plate", "brand", "model")
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
admin.site.register(RFID, RFIDAdmin)
|
|
392
|
-
|
|
541
|
+
list_filter = ("charger", MeterValueDateFilter)
|
ocpp/apps.py
CHANGED
|
@@ -6,7 +6,7 @@ from django.conf import settings
|
|
|
6
6
|
class OcppConfig(AppConfig):
|
|
7
7
|
default_auto_field = "django.db.models.BigAutoField"
|
|
8
8
|
name = "ocpp"
|
|
9
|
-
verbose_name = "3.
|
|
9
|
+
verbose_name = "3. Protocol"
|
|
10
10
|
|
|
11
11
|
def ready(self): # pragma: no cover - startup side effects
|
|
12
12
|
control_lock = Path(settings.BASE_DIR) / "locks" / "control.lck"
|