arthexis 0.1.26__py3-none-any.whl → 0.1.28__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.26.dist-info → arthexis-0.1.28.dist-info}/METADATA +16 -11
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/RECORD +39 -38
- config/settings.py +7 -1
- config/settings_helpers.py +176 -1
- config/urls.py +18 -2
- core/admin.py +265 -23
- core/apps.py +6 -2
- core/celery_utils.py +73 -0
- core/models.py +307 -63
- core/system.py +17 -2
- core/tasks.py +304 -129
- core/test_system_info.py +43 -5
- core/tests.py +202 -2
- core/user_data.py +52 -19
- core/views.py +70 -3
- nodes/admin.py +348 -3
- nodes/apps.py +1 -1
- nodes/feature_checks.py +30 -0
- nodes/models.py +146 -18
- nodes/tasks.py +1 -1
- nodes/tests.py +181 -48
- nodes/views.py +148 -3
- ocpp/admin.py +1001 -10
- ocpp/consumers.py +572 -7
- ocpp/models.py +499 -33
- ocpp/store.py +406 -40
- ocpp/tasks.py +109 -145
- ocpp/test_rfid.py +73 -2
- ocpp/tests.py +982 -90
- ocpp/urls.py +5 -0
- ocpp/views.py +172 -70
- pages/context_processors.py +2 -0
- pages/models.py +9 -0
- pages/tests.py +166 -18
- pages/urls.py +1 -0
- pages/views.py +66 -3
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/WHEEL +0 -0
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/licenses/LICENSE +0 -0
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arthexis
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.28
|
|
4
4
|
Summary: Power & Energy Infrastructure
|
|
5
5
|
Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -43,7 +43,7 @@ Requires-Dist: django-celery-beat==2.8.1
|
|
|
43
43
|
Requires-Dist: django-debug-toolbar==6.0.0
|
|
44
44
|
Requires-Dist: django-import-export==4.3.12
|
|
45
45
|
Requires-Dist: django-object-actions==5.0.0
|
|
46
|
-
Requires-Dist: django-otp==1.
|
|
46
|
+
Requires-Dist: django-otp==1.6.3
|
|
47
47
|
Requires-Dist: django-timezone-field==7.1
|
|
48
48
|
Requires-Dist: dnspython==2.7.0
|
|
49
49
|
Requires-Dist: docutils==0.22.2
|
|
@@ -64,9 +64,9 @@ Requires-Dist: mdx_truly_sane_lists==1.3
|
|
|
64
64
|
Requires-Dist: mfrc522==0.0.7; sys_platform == "linux"
|
|
65
65
|
Requires-Dist: outcome==1.3.0.post0
|
|
66
66
|
Requires-Dist: packaging==25.0
|
|
67
|
-
Requires-Dist: pillow==
|
|
68
|
-
Requires-Dist: prompt_toolkit==3.0.
|
|
69
|
-
Requires-Dist: psutil==7.1.
|
|
67
|
+
Requires-Dist: pillow==12.0.0
|
|
68
|
+
Requires-Dist: prompt_toolkit==3.0.52
|
|
69
|
+
Requires-Dist: psutil==7.1.3
|
|
70
70
|
Requires-Dist: psycopg==3.2.9
|
|
71
71
|
Requires-Dist: psycopg-binary==3.2.12
|
|
72
72
|
Requires-Dist: pyasn1==0.6.1
|
|
@@ -82,7 +82,7 @@ Requires-Dist: python-dateutil==2.9.0.post0
|
|
|
82
82
|
Requires-Dist: python-dotenv==1.1.1
|
|
83
83
|
Requires-Dist: qrcode==8.2
|
|
84
84
|
Requires-Dist: redis==7.0.1
|
|
85
|
-
Requires-Dist: reportlab==4.
|
|
85
|
+
Requires-Dist: reportlab==4.4.4
|
|
86
86
|
Requires-Dist: requests==2.32.5
|
|
87
87
|
Requires-Dist: selenium==4.34.2
|
|
88
88
|
Requires-Dist: service-identity==24.2.0
|
|
@@ -99,7 +99,7 @@ Requires-Dist: trio==0.30.0
|
|
|
99
99
|
Requires-Dist: trio-websocket==0.12.2
|
|
100
100
|
Requires-Dist: Twisted==25.5.0
|
|
101
101
|
Requires-Dist: twine==6.1.0
|
|
102
|
-
Requires-Dist: txaio==25.
|
|
102
|
+
Requires-Dist: txaio==25.9.2
|
|
103
103
|
Requires-Dist: typing-inspection==0.4.2
|
|
104
104
|
Requires-Dist: typing_extensions==4.14.1
|
|
105
105
|
Requires-Dist: tzdata==2025.2
|
|
@@ -147,18 +147,22 @@ Arthexis Constellation is a [narrative-driven](https://en.wikipedia.org/wiki/Nar
|
|
|
147
147
|
|
|
148
148
|
| Action | What we do |
|
|
149
149
|
| --- | --- |
|
|
150
|
+
| `CancelReservation` | Withdraw pending reservations and release connectors directly from the control center. |
|
|
150
151
|
| `ChangeAvailability` | Switch connectors or the whole station between operative and inoperative states. |
|
|
151
152
|
| `DataTransfer` | Send vendor-specific commands and log the charge point response. |
|
|
152
153
|
| `GetConfiguration` | Poll the device for the current values of tracked configuration keys. |
|
|
154
|
+
| `GetLocalListVersion` | Retrieve the current RFID whitelist version and synchronize entries reported by the charge point. |
|
|
153
155
|
| `RemoteStartTransaction` | Initiate a charging session remotely for an identified customer or token. |
|
|
154
156
|
| `RemoteStopTransaction` | Terminate active charging sessions from the control center. |
|
|
155
157
|
| `ReserveNow` | Reserve connectors for upcoming sessions with automatic connector selection and confirmation tracking. |
|
|
156
158
|
| `Reset` | Request a soft or hard reboot to recover from faults. |
|
|
159
|
+
| `SendLocalList` | Publish released and approved RFIDs as the charge point's local authorization list. |
|
|
157
160
|
| `TriggerMessage` | Ask the device to send an immediate update (for example status or diagnostics). |
|
|
161
|
+
| `UpdateFirmware` | Deliver firmware packages to chargers with secure download tokens and track installation responses. |
|
|
158
162
|
|
|
159
|
-
**OCPP 1.6 roadmap.** The following catalogue actions are in our backlog: `
|
|
163
|
+
**OCPP 1.6 roadmap.** The following catalogue actions are in our backlog: `ChangeConfiguration`, `ClearCache`, `ClearChargingProfile`, `GetCompositeSchedule`, `GetDiagnostics`, `SetChargingProfile`, `UnlockConnector`.
|
|
160
164
|
|
|
161
|
-
- Charge point reservations with automated connector assignment, energy account and RFID linkage,
|
|
165
|
+
- Charge point reservations with automated connector assignment, energy account and RFID linkage, EVCS confirmation tracking, and control-center cancellation support.
|
|
162
166
|
- [API](https://en.wikipedia.org/wiki/API) integration with [Odoo](https://www.odoo.com/), syncing:
|
|
163
167
|
- Employee credentials via `res.users`
|
|
164
168
|
- Product catalog lookups via `product.product`
|
|
@@ -205,7 +209,7 @@ Arthexis Constellation ships in four node roles tailored to different deployment
|
|
|
205
209
|
- **[Windows](https://en.wikipedia.org/wiki/Microsoft_Windows)**: open [PowerShell](https://learn.microsoft.com/powershell/) or [Git Bash](https://gitforwindows.org/) and run the same command.
|
|
206
210
|
|
|
207
211
|
### 2. Start and stop
|
|
208
|
-
Terminal nodes can start directly with the scripts below without installing; Control, Satellite, and Watchtower roles require installation first. Both approaches listen on [`http://localhost:
|
|
212
|
+
Terminal nodes can start directly with the scripts below without installing; Control, Satellite, and Watchtower roles require installation first. Both approaches listen on [`http://localhost:8888/`](http://localhost:8888/) by default.
|
|
209
213
|
|
|
210
214
|
- **[VS Code](https://code.visualstudio.com/)**
|
|
211
215
|
- Open the folder and go to the **Run and Debug** panel (`Ctrl+Shift+D`).
|
|
@@ -226,13 +230,14 @@ Terminal nodes can start directly with the scripts below without installing; Con
|
|
|
226
230
|
- Use `./install.sh --help` to list every available flag if you need to customize the node beyond the role defaults.
|
|
227
231
|
- Upgrade with [`./upgrade.sh`](upgrade.sh).
|
|
228
232
|
- Consult the [Install & Lifecycle Scripts Manual](docs/development/install-lifecycle-scripts-manual.md) for complete flag descriptions and operational notes.
|
|
233
|
+
- Review the [Upgrade Guide](docs/UPGRADE.md) for manual steps required after releases that stop automating specific migrations.
|
|
229
234
|
|
|
230
235
|
- **Windows:**
|
|
231
236
|
- Run [`install.bat`](install.bat) to install (Terminal role) and [`upgrade.bat`](upgrade.bat) to upgrade.
|
|
232
237
|
- Installation is not required to start in Terminal mode (the default).
|
|
233
238
|
|
|
234
239
|
### 4. Administration
|
|
235
|
-
Visit [`http://localhost:
|
|
240
|
+
Visit [`http://localhost:8888/admin/`](http://localhost:8888/admin/) for the [Django admin](https://docs.djangoproject.com/en/stable/ref/contrib/admin/) and [`http://localhost:8888/admindocs/`](http://localhost:8888/admindocs/) for the [admindocs](https://docs.djangoproject.com/en/stable/ref/contrib/admin/admindocs/). Use `--port` with the start scripts or installer when you need to expose a different port.
|
|
236
241
|
|
|
237
242
|
## Sigils
|
|
238
243
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
arthexis-0.1.
|
|
1
|
+
arthexis-0.1.28.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
2
2
|
config/__init__.py,sha256=AwpOX7il-DAOmkdJ5dVfVJ3CWWebn1lHyQNmkw1EkDw,103
|
|
3
3
|
config/active_app.py,sha256=KJqYh-o91nPQjVXPEdbiJHzsI6cN9IZsBZ9O3iZ6Hyc,373
|
|
4
4
|
config/asgi.py,sha256=Z2HjWrxOxVU9BXcqS7dMEfOGJC48H-WPwFwokRdermY,774
|
|
@@ -10,17 +10,18 @@ config/loadenv.py,sha256=CjXx-wBaTt1wixub4GJ5CMSMFqtiK5JURc7cPXpqO7s,287
|
|
|
10
10
|
config/logging.py,sha256=1cIbPgRshHuMKnVEEH0jKpRAlJSpewvLFbYDz7sCBG4,2104
|
|
11
11
|
config/middleware.py,sha256=zF8Cma0n5G8NNdh2LVeNJi7Hgl1G4mF9msRE2eRi1RU,2328
|
|
12
12
|
config/offline.py,sha256=X-yDcyoI4C44Y27lpkUwszY_09GwwFfazEsthKJpQ70,1382
|
|
13
|
-
config/settings.py,sha256=
|
|
14
|
-
config/settings_helpers.py,sha256=
|
|
15
|
-
config/urls.py,sha256=
|
|
13
|
+
config/settings.py,sha256=SCBjA9hxVGr7pKXsfCj94dsRdJ5NlCc8b1QTt0B_EtE,21127
|
|
14
|
+
config/settings_helpers.py,sha256=8bNs4eB7VHNFZyu3TfeLSu5ajnNCErc2EtqSouCIEQY,8328
|
|
15
|
+
config/urls.py,sha256=fB5VrIbN6zuyJADcMSOdGDb6VanS7XsDo7WTspSdCm0,7424
|
|
16
16
|
config/wsgi.py,sha256=zU_mKlya6hejQ21PxKacTui3dUWd4ca_-YJNSYAoMX0,433
|
|
17
17
|
core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
core/admin.py,sha256=
|
|
18
|
+
core/admin.py,sha256=7QZ-Km6vwtveKyWJBWelvwfsq1_up1aoYRL4bAEmowo,154608
|
|
19
19
|
core/admin_history.py,sha256=XZ4b0ryufIka-xcwboK3DzmOL-INSx5Y2fJO-aJdV70,1783
|
|
20
20
|
core/admindocs.py,sha256=ycD0bJ_VE6rTGf9ebXTiKdYkD8Y8hD2oQ4HxxoBURCM,6756
|
|
21
|
-
core/apps.py,sha256
|
|
21
|
+
core/apps.py,sha256=-rOizcfR0tbjmo5N66QkT6gu2o9aXcbCPWGEkG_M_VI,14290
|
|
22
22
|
core/auto_upgrade.py,sha256=8JpTWPH3-bQpmtOnTa5h3wdTVAbdthr0IqJze4deDUk,2242
|
|
23
23
|
core/backends.py,sha256=O6QzNsX3OXi0QeIO7PCXI57VLz7HoY72ouBGTpzsSPM,10793
|
|
24
|
+
core/celery_utils.py,sha256=-m6m9mpQwhE788lvxoWjIr35IJS2akepVgAArjgn4Kc,2645
|
|
24
25
|
core/changelog.py,sha256=SRn37i5N-qb-RYV4Gpu9fg7Kv8gu4TH8ZwEmDRgN-Vo,12594
|
|
25
26
|
core/entity.py,sha256=o4VteOXePGEsIWJFZ3fpq3DZsdWr3hpQ9A6kFbKosSE,4844
|
|
26
27
|
core/environment.py,sha256=QoiMjFVmwCEln2DEa10Hht6Ymop0H6wbDsyO6-ebXp4,1651
|
|
@@ -34,7 +35,7 @@ core/liveupdate.py,sha256=22m0ueQ10-6b-9pQJHY0_5WRYA98fysXKEXOWzIr550,691
|
|
|
34
35
|
core/log_paths.py,sha256=lxvgXPgJtVNZ-kYrqV8VFle4GFQrSxG-yRTglqvclmU,3318
|
|
35
36
|
core/mailer.py,sha256=JpW0RnD9uZ4O-wvlqeW7CMw95IFeCSkdvbankJDwHq0,2886
|
|
36
37
|
core/middleware.py,sha256=j19K9SX-Emkv7BDDtAacR9g6RWsxhKHwCc8w23JFvMM,3388
|
|
37
|
-
core/models.py,sha256
|
|
38
|
+
core/models.py,sha256=E9QLiInbgbSIdPJBfj9K11B1Y98TVe58vWGb9OA2KZg,184233
|
|
38
39
|
core/notifications.py,sha256=jNLSuSCrhb8x5cDu_APeDlkrmbMejufk5eJOhssAC4I,3917
|
|
39
40
|
core/public_wifi.py,sha256=yydLgxOo9DmJJbM4X_23wGR3gxL3YzHno54v9GssuFA,7213
|
|
40
41
|
core/reference_utils.py,sha256=tffCoyE1w4_SmYzXVWOsW8aR_ZVVTSPzrGhBq8K2xzA,3631
|
|
@@ -43,69 +44,69 @@ core/rfid_import_export.py,sha256=petyhPvL0WUpehc6uGUDUhjYQ9AVvc6O49zuhDs6YFw,35
|
|
|
43
44
|
core/sigil_builder.py,sha256=SmmYRjl11u2GpU8rt3va-TP5hwAOobJTWTUGqVAikDk,4854
|
|
44
45
|
core/sigil_context.py,sha256=GCzjfM6fcVvBtSbVNfmE6sx3HU8QnxnXrCIytnNpQzM,439
|
|
45
46
|
core/sigil_resolver.py,sha256=06Zt4g3KCJSeQJAg_NYCIbXhNejpfFclosweur0C4W4,9182
|
|
46
|
-
core/system.py,sha256=
|
|
47
|
-
core/tasks.py,sha256=
|
|
47
|
+
core/system.py,sha256=xpiq5w3VcTjCCdWbdc7LttGtv4h9f8Wwayl7URzGJz4,44508
|
|
48
|
+
core/tasks.py,sha256=oddBp2j8pi3NJsELtret4xhFXgmSMq2mgAiSlQcbKQQ,22333
|
|
48
49
|
core/temp_passwords.py,sha256=FieUnIUeQHmA1DoXvfJ5U6-Ayv3oDz-hSln5s_vNbA4,5271
|
|
49
|
-
core/test_system_info.py,sha256=
|
|
50
|
-
core/tests.py,sha256=
|
|
50
|
+
core/test_system_info.py,sha256=KLdEgBpye54sb9iY6t5TkfvNFAnj1gcF23T7i8WYUSo,8285
|
|
51
|
+
core/tests.py,sha256=qkLlkhAghSsxNY60R5GzJ738JFjAMrYunJ03GCiu3II,113674
|
|
51
52
|
core/tests_liveupdate.py,sha256=IquU8ztk6zbzC1bQu3Nrr3RzGzuujtPwDkANJHbxg98,510
|
|
52
53
|
core/urls.py,sha256=YPippON1MAP2KeZZ8jHpcLO6mvbnKn1q7fdMv5Vm9dY,425
|
|
53
|
-
core/user_data.py,sha256=
|
|
54
|
-
core/views.py,sha256=
|
|
54
|
+
core/user_data.py,sha256=wIpOeOLhBcj8_InNZdFVTwAKZTQ-0NEWjj-4y0jDmEg,23463
|
|
55
|
+
core/views.py,sha256=BBlLRttTXUohVuaPuUnF58XR8G8iYPitv9npR0BC0go,90363
|
|
55
56
|
core/widgets.py,sha256=vlR9PlFfZGlkHm5X2cqNXuEBZSj8gmWaR6MO1mMy6kg,6904
|
|
56
57
|
nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
-
nodes/admin.py,sha256=
|
|
58
|
-
nodes/apps.py,sha256=
|
|
58
|
+
nodes/admin.py,sha256=QDe9wKfeW3UAk8qFvfuFOQ7W-kkcc2DVmahjDd_yvDw,92910
|
|
59
|
+
nodes/apps.py,sha256=jGG4xNxrZOFcL_Lh1VywsaAiAymuHFn6i0icXQ7uyjU,3059
|
|
59
60
|
nodes/backends.py,sha256=dmmbS0X2YIlCDz2KjoDf_L62dy--nuqZF1rEDoi2JHM,5921
|
|
60
61
|
nodes/dns.py,sha256=D5smXD7Rkh6E4MdL6TBL2WY8GgJg7Rx9z88LZrcMbTw,7048
|
|
61
|
-
nodes/feature_checks.py,sha256=
|
|
62
|
+
nodes/feature_checks.py,sha256=8gAKUZTnC480z302SPkUBfnbYri8_vySMxuf260Qatc,5018
|
|
62
63
|
nodes/lcd.py,sha256=iKA8Wmq85KZD52aTzAU8ZmS144_gbdGMOXcE8yuECps,5758
|
|
63
|
-
nodes/models.py,sha256=
|
|
64
|
+
nodes/models.py,sha256=D44gXggMNYrdmjly4SQcM6VNoEharLww-Dl5ootDgS4,88092
|
|
64
65
|
nodes/reports.py,sha256=NRYh3Y0SlZFhx31Zh2K03yO12ZrpxEHEY6T-dODA6WE,12059
|
|
65
66
|
nodes/rfid_sync.py,sha256=oeblawcp6xeLApdIuhsJS83OAk58Eu7pVVmgpAc0Nt8,6953
|
|
66
67
|
nodes/signals.py,sha256=PtOKdQfb08mV1LgSZvn7ZAcfOyy2c3Xkq4AOpBQyUdE,622
|
|
67
|
-
nodes/tasks.py,sha256=
|
|
68
|
-
nodes/tests.py,sha256
|
|
68
|
+
nodes/tasks.py,sha256=2mMRf9Q-tqzFk3SXGHz-d6r_sw_ibuJhQAyfbYrDudk,5111
|
|
69
|
+
nodes/tests.py,sha256=uoNU-F0S0bbDiMN1qpbcKP1kIuGDEIW4IUTKXLK284Y,204825
|
|
69
70
|
nodes/urls.py,sha256=9uMhDq-b-EWZz0u-NvPRVSPXJeXfuS-BAACvsCs6gaE,1267
|
|
70
71
|
nodes/utils.py,sha256=x7l8Fz8UwWBumeNrHemmLXXRW_5bkMHaaRnE-VLZFwo,6337
|
|
71
|
-
nodes/views.py,sha256=
|
|
72
|
+
nodes/views.py,sha256=EF3BVcMXrqAT0Ii37AZsJo7hiBgtii1JgDGbshyjA44,65255
|
|
72
73
|
ocpp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
|
-
ocpp/admin.py,sha256=
|
|
74
|
+
ocpp/admin.py,sha256=OlWeQ1J-s2QrbYv3PEz7iUjbHv5UkhV-ErT0NryyCvQ,96322
|
|
74
75
|
ocpp/apps.py,sha256=i3NqrmIamNEQBT33CIqh7HOSOPmJXCMKrZ-DUd3whqg,842
|
|
75
|
-
ocpp/consumers.py,sha256=
|
|
76
|
+
ocpp/consumers.py,sha256=F0EGa87L7QgTg-m9pzHSPYn0frGveeZfA8aphW-KfQY,97651
|
|
76
77
|
ocpp/evcs.py,sha256=q1mZrCVSZxXTrtYsDqH6lkeEcJ6tfSC7p9YxkDmpSCw,28883
|
|
77
78
|
ocpp/evcs_discovery.py,sha256=OmrzgaOHwveDRJs8AIhrM3apX8_k2PPXh_oYaYpNW3c,3876
|
|
78
|
-
ocpp/models.py,sha256=
|
|
79
|
+
ocpp/models.py,sha256=oB7Rx-nJ9qKmMKARRXKExXRo8gW3jwlzuZbsMsD2geE,65098
|
|
79
80
|
ocpp/network.py,sha256=N3je0wXckSqlHLJNQazpxrBvv0yAR7DdjfAR-hTcWDk,14149
|
|
80
81
|
ocpp/reference_utils.py,sha256=_UR82GfE93kv4766mHyVIfdhhyYvrT59660r3H6W55M,1072
|
|
81
82
|
ocpp/routing.py,sha256=3kQya-MdJ00778xDmX0esQLBP05P200V45asg-CGNoo,438
|
|
82
83
|
ocpp/simulator.py,sha256=vnyd59QffT79AaPhmfM_jipni_nqfG57X5tXyx1rBoc,28016
|
|
83
84
|
ocpp/status_display.py,sha256=YGFosd5HJETA0DcLdsjvx6EfhZSnI8Aa3cMnHG2WsBE,939
|
|
84
|
-
ocpp/store.py,sha256=
|
|
85
|
-
ocpp/tasks.py,sha256=
|
|
85
|
+
ocpp/store.py,sha256=W-vDD5HQZc1j_Hqslcs1BFgENkfBhZAZXsN9ERXqKG0,30068
|
|
86
|
+
ocpp/tasks.py,sha256=x-9GhhhPx1yZps6QsAfFhCS5CivawfbSa2z87U4MkdY,13966
|
|
86
87
|
ocpp/test_export_import.py,sha256=ouQbTCp4mxfqoK6gondlu3PPcyrT9jSbWAX5gqqgaNk,4561
|
|
87
|
-
ocpp/test_rfid.py,sha256=
|
|
88
|
-
ocpp/tests.py,sha256=
|
|
88
|
+
ocpp/test_rfid.py,sha256=Y3J9mvGoStOZkvT1v7ZDq1abw28U-nY1yVBA4IzEFuE,41962
|
|
89
|
+
ocpp/tests.py,sha256=4wvJYGx7DaJacqusQm7pOdO23WTKaEUQ884LlPPTNyo,249895
|
|
89
90
|
ocpp/transactions_io.py,sha256=p2aUsKlCDYnZ4ZBrOM7pxXoW_w3Tbm-tvRFSjnR3x24,7738
|
|
90
|
-
ocpp/urls.py,sha256=
|
|
91
|
-
ocpp/views.py,sha256=
|
|
91
|
+
ocpp/urls.py,sha256=JFXjnC8-k559M1XGhafiJLc2_ATmIANoq--2YlfTbRA,1871
|
|
92
|
+
ocpp/views.py,sha256=m8ZJgvOu00lzkC1S6odD8KNRhMEHB5yL0SauVeRbfbg,81089
|
|
92
93
|
pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
93
94
|
pages/admin.py,sha256=VbxkwgjrFx7lXVhwbZuPSvCkS7EC-flBpwOztHejWtE,35834
|
|
94
95
|
pages/apps.py,sha256=0qcTFKVX9_QgqexJtGeph1sHRqq7khJf4x5ZtkWwblg,1424
|
|
95
96
|
pages/checks.py,sha256=sM8_hUVM_HOIocvtTb2sY3AaSEvbTnOlO46UchGVd-0,1527
|
|
96
|
-
pages/context_processors.py,sha256=
|
|
97
|
+
pages/context_processors.py,sha256=QHMrFX7OT89kbV5_4BOZfk-aqVYy0vsdFqkI0t7nPl0,5392
|
|
97
98
|
pages/defaults.py,sha256=3tjv3nFPxwpFu6poJ1Ez1MP92Q6ZvyRluftKHlU-zeI,522
|
|
98
99
|
pages/forms.py,sha256=r3JM5qp3_4RR01-u6XV8WDOaeiRe4OvCN8Y52FcsAwI,7909
|
|
99
100
|
pages/middleware.py,sha256=-tXFju1siXvzVsHcgjClfTtryw-5-PwW0171DQQxKu4,7115
|
|
100
|
-
pages/models.py,sha256=
|
|
101
|
+
pages/models.py,sha256=c_u1GSPFksXXvMq6iPKu4a7_ty-ugEoHgnTvrz83kh4,23904
|
|
101
102
|
pages/module_defaults.py,sha256=rCAY8aTyxYNL0M5zDr393rX-Gi-svXqKtuLXm0rILrQ,5444
|
|
102
103
|
pages/site_config.py,sha256=f1Me0GFdHeGbIeyMlQNzD2e6hym59YHqbz92U_ppffY,4057
|
|
103
104
|
pages/tasks.py,sha256=ivcba_3wSQ1-cku0oDplzw6vLeQ9hBq3R4TG-LmR5gs,1913
|
|
104
|
-
pages/tests.py,sha256=
|
|
105
|
-
pages/urls.py,sha256
|
|
105
|
+
pages/tests.py,sha256=K3KLPXJREi5zCZsiQCgvxuZ6HaJJNOHthtj0MdDAh3Y,161499
|
|
106
|
+
pages/urls.py,sha256=-6Ym5YOQ0oHr3YdK8BXZbSRMydWXM2sj7LoQ8_zBk6E,1566
|
|
106
107
|
pages/utils.py,sha256=vEFrXSzN-3wsK2H687_oVKSwsSOP_NB7DXg1hHwHink,2471
|
|
107
|
-
pages/views.py,sha256=
|
|
108
|
-
arthexis-0.1.
|
|
109
|
-
arthexis-0.1.
|
|
110
|
-
arthexis-0.1.
|
|
111
|
-
arthexis-0.1.
|
|
108
|
+
pages/views.py,sha256=HnQwPcss_TurrmJIXQmMxX9l6LYqo3qmlBdhOH-XxbY,67576
|
|
109
|
+
arthexis-0.1.28.dist-info/METADATA,sha256=DRHoWgoYVf6Tb-7Q4Y55GIe0XWD4rUgFyKfJ9FniWgI,14454
|
|
110
|
+
arthexis-0.1.28.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
111
|
+
arthexis-0.1.28.dist-info/top_level.txt,sha256=J2a2q8_BWrCZ8H2WFUNMBfO2jz8j2gax6zZh-_1QDac,29
|
|
112
|
+
arthexis-0.1.28.dist-info/RECORD,,
|
config/settings.py
CHANGED
|
@@ -31,6 +31,7 @@ from urllib.parse import urlsplit
|
|
|
31
31
|
import django.utils.encoding as encoding
|
|
32
32
|
|
|
33
33
|
from config.settings_helpers import (
|
|
34
|
+
discover_local_ip_addresses,
|
|
34
35
|
extract_ip_from_host,
|
|
35
36
|
install_validate_host_with_subnets,
|
|
36
37
|
load_secret_key,
|
|
@@ -143,6 +144,11 @@ for host in _iter_local_hostnames(_local_hostname, _local_fqdn):
|
|
|
143
144
|
ALLOWED_HOSTS.append(host)
|
|
144
145
|
|
|
145
146
|
|
|
147
|
+
for address in discover_local_ip_addresses():
|
|
148
|
+
if address not in ALLOWED_HOSTS:
|
|
149
|
+
ALLOWED_HOSTS.append(address)
|
|
150
|
+
|
|
151
|
+
|
|
146
152
|
# Allow CSRF origin verification for hosts within allowed subnets.
|
|
147
153
|
_original_origin_verified = CsrfViewMiddleware._origin_verified
|
|
148
154
|
_original_check_referer = CsrfViewMiddleware._check_referer
|
|
@@ -671,6 +677,6 @@ CELERY_BEAT_SCHEDULE = {
|
|
|
671
677
|
},
|
|
672
678
|
"ocpp_forwarding_push": {
|
|
673
679
|
"task": "ocpp.tasks.push_forwarded_charge_points",
|
|
674
|
-
"schedule": timedelta(
|
|
680
|
+
"schedule": timedelta(minutes=10),
|
|
675
681
|
},
|
|
676
682
|
}
|
config/settings_helpers.py
CHANGED
|
@@ -5,8 +5,12 @@ from __future__ import annotations
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import ipaddress
|
|
7
7
|
import os
|
|
8
|
+
import socket
|
|
9
|
+
import subprocess
|
|
10
|
+
import urllib.error
|
|
11
|
+
import urllib.request
|
|
8
12
|
from pathlib import Path
|
|
9
|
-
from typing import Mapping, MutableMapping
|
|
13
|
+
from typing import Iterable, Mapping, MutableMapping
|
|
10
14
|
|
|
11
15
|
from django.core.management.utils import get_random_secret_key
|
|
12
16
|
from django.http import request as http_request
|
|
@@ -14,6 +18,7 @@ from django.http.request import split_domain_port
|
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
__all__ = [
|
|
21
|
+
"discover_local_ip_addresses",
|
|
17
22
|
"extract_ip_from_host",
|
|
18
23
|
"install_validate_host_with_subnets",
|
|
19
24
|
"load_secret_key",
|
|
@@ -78,6 +83,176 @@ def install_validate_host_with_subnets() -> None:
|
|
|
78
83
|
http_request.validate_host = _patched
|
|
79
84
|
|
|
80
85
|
|
|
86
|
+
def _normalize_candidate_ip(candidate: str) -> str | None:
|
|
87
|
+
"""Return a normalized IP string when *candidate* is valid."""
|
|
88
|
+
|
|
89
|
+
if not candidate:
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
normalized = strip_ipv6_brackets(candidate.strip())
|
|
93
|
+
if not normalized:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
# Drop IPv6 zone identifiers (for example ``fe80::1%eth0``)
|
|
97
|
+
if "%" in normalized:
|
|
98
|
+
normalized = normalized.split("%", 1)[0]
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
return ipaddress.ip_address(normalized).compressed
|
|
102
|
+
except ValueError:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _iter_command_addresses(command: Iterable[str]) -> Iterable[str]:
|
|
107
|
+
"""Yield IP addresses parsed from a command's stdout."""
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
result = subprocess.run(
|
|
111
|
+
list(command),
|
|
112
|
+
capture_output=True,
|
|
113
|
+
text=True,
|
|
114
|
+
check=False,
|
|
115
|
+
timeout=1.0,
|
|
116
|
+
)
|
|
117
|
+
except (FileNotFoundError, PermissionError, subprocess.SubprocessError):
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
if result.returncode != 0:
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
for token in result.stdout.split():
|
|
124
|
+
normalized = _normalize_candidate_ip(token)
|
|
125
|
+
if normalized:
|
|
126
|
+
yield normalized
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _iter_ip_addr_show() -> Iterable[str]:
|
|
130
|
+
"""Yield interface addresses from the ``ip`` command when available."""
|
|
131
|
+
|
|
132
|
+
commands = (
|
|
133
|
+
("ip", "-o", "-4", "addr", "show"),
|
|
134
|
+
("ip", "-o", "-6", "addr", "show"),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
for command in commands:
|
|
138
|
+
try:
|
|
139
|
+
result = subprocess.run(
|
|
140
|
+
list(command),
|
|
141
|
+
capture_output=True,
|
|
142
|
+
text=True,
|
|
143
|
+
check=False,
|
|
144
|
+
timeout=1.0,
|
|
145
|
+
)
|
|
146
|
+
except (FileNotFoundError, PermissionError, subprocess.SubprocessError):
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
if result.returncode != 0:
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
for line in result.stdout.splitlines():
|
|
153
|
+
parts = line.split()
|
|
154
|
+
if len(parts) < 4:
|
|
155
|
+
continue
|
|
156
|
+
# ``<ifindex>: <ifname> <family> <address>/<prefix>``
|
|
157
|
+
address = parts[3].split("/", 1)[0]
|
|
158
|
+
normalized = _normalize_candidate_ip(address)
|
|
159
|
+
if normalized:
|
|
160
|
+
yield normalized
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _iter_metadata_addresses(env: Mapping[str, str]) -> Iterable[str]:
|
|
164
|
+
"""Yield IP addresses exposed by cloud metadata endpoints when available."""
|
|
165
|
+
|
|
166
|
+
disable_env = env.get("DISABLE_METADATA_IP_DISCOVERY", "")
|
|
167
|
+
if disable_env.strip().lower() in {"1", "true", "yes", "on"}:
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
if env.get("AWS_EC2_METADATA_DISABLED", "").strip().lower() in {"1", "true", "yes", "on"}:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
endpoints = (
|
|
174
|
+
"http://169.254.169.254/latest/meta-data/local-ipv4",
|
|
175
|
+
"http://169.254.169.254/latest/meta-data/public-ipv4",
|
|
176
|
+
"http://169.254.169.254/latest/meta-data/local-ipv6",
|
|
177
|
+
"http://169.254.169.254/latest/meta-data/ipv6",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
for endpoint in endpoints:
|
|
181
|
+
try:
|
|
182
|
+
with urllib.request.urlopen(endpoint, timeout=0.5) as response:
|
|
183
|
+
payload = response.read().decode("utf-8", "ignore").strip()
|
|
184
|
+
except (urllib.error.URLError, OSError, ValueError):
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
if not payload:
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
for line in payload.splitlines():
|
|
191
|
+
normalized = _normalize_candidate_ip(line)
|
|
192
|
+
if normalized:
|
|
193
|
+
yield normalized
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def discover_local_ip_addresses(
|
|
197
|
+
env: Mapping[str, str] | MutableMapping[str, str] | None = None,
|
|
198
|
+
) -> set[str]:
|
|
199
|
+
"""Return IP addresses associated with the current host.
|
|
200
|
+
|
|
201
|
+
The discovery process aggregates several lightweight heuristics so the
|
|
202
|
+
project continues to run even when specific mechanisms fail. All
|
|
203
|
+
collectors are best-effort and errors are swallowed to avoid blocking
|
|
204
|
+
Django's startup.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
if env is None:
|
|
208
|
+
env = os.environ
|
|
209
|
+
|
|
210
|
+
addresses: set[str] = set()
|
|
211
|
+
|
|
212
|
+
def _add(candidate: str | None) -> None:
|
|
213
|
+
normalized = _normalize_candidate_ip(candidate or "")
|
|
214
|
+
if normalized:
|
|
215
|
+
addresses.add(normalized)
|
|
216
|
+
|
|
217
|
+
for loopback in ("127.0.0.1", "::1"):
|
|
218
|
+
_add(loopback)
|
|
219
|
+
|
|
220
|
+
hostnames: list[str] = []
|
|
221
|
+
with contextlib.suppress(Exception):
|
|
222
|
+
hostnames.append(socket.gethostname())
|
|
223
|
+
with contextlib.suppress(Exception):
|
|
224
|
+
hostnames.append(socket.getfqdn())
|
|
225
|
+
|
|
226
|
+
for hostname in hostnames:
|
|
227
|
+
if not hostname:
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
with contextlib.suppress(Exception):
|
|
231
|
+
_hostname, _aliases, addresses_list = socket.gethostbyname_ex(hostname)
|
|
232
|
+
for address in addresses_list:
|
|
233
|
+
_add(address)
|
|
234
|
+
|
|
235
|
+
with contextlib.suppress(Exception):
|
|
236
|
+
for info in socket.getaddrinfo(hostname, None):
|
|
237
|
+
if len(info) < 5:
|
|
238
|
+
continue
|
|
239
|
+
sock_address = info[4]
|
|
240
|
+
if not sock_address:
|
|
241
|
+
continue
|
|
242
|
+
_add(sock_address[0])
|
|
243
|
+
|
|
244
|
+
for address in _iter_ip_addr_show():
|
|
245
|
+
_add(address)
|
|
246
|
+
|
|
247
|
+
for address in _iter_command_addresses(("hostname", "-I")):
|
|
248
|
+
_add(address)
|
|
249
|
+
|
|
250
|
+
for address in _iter_metadata_addresses(env):
|
|
251
|
+
_add(address)
|
|
252
|
+
|
|
253
|
+
return addresses
|
|
254
|
+
|
|
255
|
+
|
|
81
256
|
def load_secret_key(
|
|
82
257
|
base_dir: Path,
|
|
83
258
|
env: Mapping[str, str] | MutableMapping[str, str] | None = None,
|
config/urls.py
CHANGED
|
@@ -22,6 +22,7 @@ from django.views.decorators.csrf import csrf_exempt
|
|
|
22
22
|
from django.views.generic import RedirectView
|
|
23
23
|
from django.views.i18n import set_language
|
|
24
24
|
from django.utils.translation import gettext_lazy as _
|
|
25
|
+
from django.http import Http404
|
|
25
26
|
from core import views as core_views
|
|
26
27
|
from core.admindocs import (
|
|
27
28
|
CommandsView,
|
|
@@ -204,8 +205,23 @@ urlpatterns = [
|
|
|
204
205
|
path("", include("pages.urls")),
|
|
205
206
|
]
|
|
206
207
|
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
_GRAPHQL_URLPOSITION = len(urlpatterns)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if EnergyGraphQLView is not None:
|
|
212
|
+
|
|
213
|
+
class FeatureFlaggedGraphQLView(EnergyGraphQLView):
|
|
214
|
+
"""GraphQL endpoint guarded by the node feature flag."""
|
|
215
|
+
|
|
216
|
+
def dispatch(self, request, *args, **kwargs): # type: ignore[override]
|
|
217
|
+
if not _graphql_feature_enabled():
|
|
218
|
+
raise Http404()
|
|
219
|
+
return super().dispatch(request, *args, **kwargs)
|
|
220
|
+
|
|
221
|
+
urlpatterns.insert(
|
|
222
|
+
_GRAPHQL_URLPOSITION,
|
|
223
|
+
path("graphql/", FeatureFlaggedGraphQLView.as_view(), name="graphql"),
|
|
224
|
+
)
|
|
209
225
|
|
|
210
226
|
urlpatterns += autodiscovered_urlpatterns()
|
|
211
227
|
|