arthexis 0.1.16__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.

Files changed (67) hide show
  1. {arthexis-0.1.16.dist-info → arthexis-0.1.28.dist-info}/METADATA +95 -41
  2. arthexis-0.1.28.dist-info/RECORD +112 -0
  3. config/asgi.py +1 -15
  4. config/middleware.py +47 -1
  5. config/settings.py +21 -30
  6. config/settings_helpers.py +176 -1
  7. config/urls.py +69 -1
  8. core/admin.py +805 -473
  9. core/apps.py +6 -8
  10. core/auto_upgrade.py +19 -4
  11. core/backends.py +13 -3
  12. core/celery_utils.py +73 -0
  13. core/changelog.py +66 -5
  14. core/environment.py +4 -5
  15. core/models.py +1825 -218
  16. core/notifications.py +1 -1
  17. core/reference_utils.py +10 -11
  18. core/release.py +55 -7
  19. core/sigil_builder.py +2 -2
  20. core/sigil_resolver.py +1 -66
  21. core/system.py +285 -4
  22. core/tasks.py +439 -138
  23. core/test_system_info.py +43 -5
  24. core/tests.py +516 -18
  25. core/user_data.py +94 -21
  26. core/views.py +348 -186
  27. nodes/admin.py +904 -67
  28. nodes/apps.py +12 -1
  29. nodes/feature_checks.py +30 -0
  30. nodes/models.py +800 -127
  31. nodes/rfid_sync.py +1 -1
  32. nodes/tasks.py +98 -3
  33. nodes/tests.py +1381 -152
  34. nodes/urls.py +15 -1
  35. nodes/utils.py +51 -3
  36. nodes/views.py +1382 -152
  37. ocpp/admin.py +1970 -152
  38. ocpp/consumers.py +839 -34
  39. ocpp/models.py +968 -17
  40. ocpp/network.py +398 -0
  41. ocpp/store.py +411 -43
  42. ocpp/tasks.py +261 -3
  43. ocpp/test_export_import.py +1 -0
  44. ocpp/test_rfid.py +194 -6
  45. ocpp/tests.py +1918 -87
  46. ocpp/transactions_io.py +9 -1
  47. ocpp/urls.py +8 -3
  48. ocpp/views.py +700 -53
  49. pages/admin.py +262 -30
  50. pages/apps.py +35 -0
  51. pages/context_processors.py +28 -21
  52. pages/defaults.py +1 -1
  53. pages/forms.py +31 -8
  54. pages/middleware.py +6 -2
  55. pages/models.py +86 -2
  56. pages/module_defaults.py +5 -5
  57. pages/site_config.py +137 -0
  58. pages/tests.py +1050 -126
  59. pages/urls.py +14 -2
  60. pages/utils.py +70 -0
  61. pages/views.py +622 -56
  62. arthexis-0.1.16.dist-info/RECORD +0 -111
  63. core/workgroup_urls.py +0 -17
  64. core/workgroup_views.py +0 -94
  65. {arthexis-0.1.16.dist-info → arthexis-0.1.28.dist-info}/WHEEL +0 -0
  66. {arthexis-0.1.16.dist-info → arthexis-0.1.28.dist-info}/licenses/LICENSE +0 -0
  67. {arthexis-0.1.16.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.16
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
@@ -15,7 +15,7 @@ Requires-Dist: amqp==5.3.1
15
15
  Requires-Dist: annotated-types==0.7.0
16
16
  Requires-Dist: anyio==4.9.0
17
17
  Requires-Dist: asgiref==3.10.0
18
- Requires-Dist: atproto==0.0.61
18
+ Requires-Dist: atproto<0.1.0,>=0.0.63
19
19
  Requires-Dist: attrs==25.3.0
20
20
  Requires-Dist: autobahn==24.4.2
21
21
  Requires-Dist: Automat==25.4.16
@@ -26,14 +26,14 @@ Requires-Dist: celery==5.5.3
26
26
  Requires-Dist: certifi==2025.7.14
27
27
  Requires-Dist: cffi==2.0.0
28
28
  Requires-Dist: channels==4.1.0
29
- Requires-Dist: charset-normalizer==3.4.2
29
+ Requires-Dist: charset-normalizer==3.4.4
30
30
  Requires-Dist: click==8.2.1
31
31
  Requires-Dist: click-didyoumean==0.3.1
32
32
  Requires-Dist: click-plugins==1.1.1.2
33
33
  Requires-Dist: click-repl==0.3.0
34
34
  Requires-Dist: colorama==0.4.6
35
35
  Requires-Dist: constantly==23.10.4
36
- Requires-Dist: cron-descriptor==1.4.5
36
+ Requires-Dist: cron-descriptor==2.0.6
37
37
  Requires-Dist: cryptography==45.0.5
38
38
  Requires-Dist: daphne==4.2.1
39
39
  Requires-Dist: diff-match-patch==20241021
@@ -41,47 +41,48 @@ Requires-Dist: defusedxml==0.7.1
41
41
  Requires-Dist: Django==5.2.7
42
42
  Requires-Dist: django-celery-beat==2.8.1
43
43
  Requires-Dist: django-debug-toolbar==6.0.0
44
- Requires-Dist: django-import-export==4.3.9
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.5.4
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
50
50
  Requires-Dist: gpiozero==2.0.1; sys_platform == "linux"
51
+ Requires-Dist: graphene-django==3.2.2
51
52
  Requires-Dist: graphviz==0.21
53
+ Requires-Dist: geckodriver-autoinstaller==0.1.0
52
54
  Requires-Dist: h11==0.16.0
53
55
  Requires-Dist: httpcore==1.0.9
54
56
  Requires-Dist: httpx==0.28.1
55
57
  Requires-Dist: hyperlink==21.0.0
56
- Requires-Dist: idna==3.10
58
+ Requires-Dist: idna==3.11
57
59
  Requires-Dist: incremental==24.7.2
58
60
  Requires-Dist: kombu==5.5.4
59
- Requires-Dist: libipld==3.1.1
60
- Requires-Dist: Markdown==3.8.2
61
+ Requires-Dist: libipld==3.2.0
62
+ Requires-Dist: Markdown==3.9
61
63
  Requires-Dist: mdx_truly_sane_lists==1.3
62
- Requires-Dist: mcp==1.16.0
63
64
  Requires-Dist: mfrc522==0.0.7; sys_platform == "linux"
64
65
  Requires-Dist: outcome==1.3.0.post0
65
66
  Requires-Dist: packaging==25.0
66
- Requires-Dist: pillow==11.3.0
67
- Requires-Dist: prompt_toolkit==3.0.51
68
- Requires-Dist: psutil==5.9.8
67
+ Requires-Dist: pillow==12.0.0
68
+ Requires-Dist: prompt_toolkit==3.0.52
69
+ Requires-Dist: psutil==7.1.3
69
70
  Requires-Dist: psycopg==3.2.9
70
- Requires-Dist: psycopg-binary==3.2.9
71
+ Requires-Dist: psycopg-binary==3.2.12
71
72
  Requires-Dist: pyasn1==0.6.1
72
73
  Requires-Dist: pyasn1_modules==0.4.2
73
74
  Requires-Dist: pycparser==2.22
74
- Requires-Dist: pydantic==2.11.7
75
- Requires-Dist: pydantic_core==2.33.2
75
+ Requires-Dist: pydantic==2.12.3
76
+ Requires-Dist: pydantic_core==2.41.4
76
77
  Requires-Dist: pyOpenSSL==25.1.0
77
- Requires-Dist: pyperclip==1.9.0
78
+ Requires-Dist: pyperclip==1.11.0
78
79
  Requires-Dist: PySocks==1.7.1
79
80
  Requires-Dist: python-crontab==3.3.0
80
81
  Requires-Dist: python-dateutil==2.9.0.post0
81
82
  Requires-Dist: python-dotenv==1.1.1
82
83
  Requires-Dist: qrcode==8.2
83
- Requires-Dist: redis==5.0.8
84
- Requires-Dist: reportlab==4.2.2
84
+ Requires-Dist: redis==7.0.1
85
+ Requires-Dist: reportlab==4.4.4
85
86
  Requires-Dist: requests==2.32.5
86
87
  Requires-Dist: selenium==4.34.2
87
88
  Requires-Dist: service-identity==24.2.0
@@ -98,20 +99,20 @@ Requires-Dist: trio==0.30.0
98
99
  Requires-Dist: trio-websocket==0.12.2
99
100
  Requires-Dist: Twisted==25.5.0
100
101
  Requires-Dist: twine==6.1.0
101
- Requires-Dist: txaio==25.6.1
102
- Requires-Dist: typing-inspection==0.4.1
102
+ Requires-Dist: txaio==25.9.2
103
+ Requires-Dist: typing-inspection==0.4.2
103
104
  Requires-Dist: typing_extensions==4.14.1
104
105
  Requires-Dist: tzdata==2025.2
105
106
  Requires-Dist: urllib3==2.5.0
106
107
  Requires-Dist: vine==5.1.0
107
- Requires-Dist: wcwidth==0.2.13
108
+ Requires-Dist: wcwidth==0.2.14
108
109
  Requires-Dist: webencodings==0.5.1
109
110
  Requires-Dist: websocket-client==1.8.0
110
- Requires-Dist: websockets==13.1
111
- Requires-Dist: whitenoise==6.9.0
111
+ Requires-Dist: websockets==15.0.1
112
+ Requires-Dist: whitenoise==6.11.0
112
113
  Requires-Dist: plyer==2.1.0; sys_platform == "win32"
113
114
  Requires-Dist: wsproto==1.2.0
114
- Requires-Dist: zope.interface==7.2
115
+ Requires-Dist: zope.interface==8.0.1
115
116
  Dynamic: license-file
116
117
 
117
118
  # Arthexis Constellation
@@ -125,19 +126,43 @@ Arthexis Constellation is a [narrative-driven](https://en.wikipedia.org/wiki/Nar
125
126
 
126
127
  ## Current Features
127
128
 
128
- - Compatible with the [Open Charge Point Protocol (OCPP) 1.6](https://www.openchargealliance.org/protocols/ocpp-16/) central system, handling:
129
- - Lifecycle & sessions
130
- - `BootNotification`
131
- - `Heartbeat`
132
- - `StatusNotification`
133
- - `StartTransaction`
134
- - `StopTransaction`
135
- - Access & metering
136
- - `Authorize`
137
- - `MeterValues`
138
- - Maintenance & firmware
139
- - `DiagnosticsStatusNotification`
140
- - `FirmwareStatusNotification`
129
+ - Compatible with the [Open Charge Point Protocol (OCPP) 1.6](https://www.openchargealliance.org/protocols/ocpp-16/) central system. Supported actions are summarized below.
130
+
131
+ **Charge point → CSMS**
132
+
133
+ | Action | What we do |
134
+ | --- | --- |
135
+ | `Authorize` | Validate RFID or token authorization requests before a session starts. |
136
+ | `BootNotification` | Register the charge point and update identity, firmware, and status details. |
137
+ | `DataTransfer` | Accept vendor-specific payloads and record the results. |
138
+ | `DiagnosticsStatusNotification` | Track the progress of diagnostic uploads kicked off from the back office. |
139
+ | `FirmwareStatusNotification` | Track firmware update lifecycle events from charge points. |
140
+ | `Heartbeat` | Keep the websocket session alive and update last-seen timestamps. |
141
+ | `MeterValues` | Persist periodic energy and power readings while a transaction is active. |
142
+ | `StartTransaction` | Create charging sessions with initial meter values and identification data. |
143
+ | `StatusNotification` | Reflect connector availability and fault states in real time. |
144
+ | `StopTransaction` | Close charging sessions, capturing closing meter values and stop reasons. |
145
+
146
+ **CSMS → Charge point**
147
+
148
+ | Action | What we do |
149
+ | --- | --- |
150
+ | `CancelReservation` | Withdraw pending reservations and release connectors directly from the control center. |
151
+ | `ChangeAvailability` | Switch connectors or the whole station between operative and inoperative states. |
152
+ | `DataTransfer` | Send vendor-specific commands and log the charge point response. |
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. |
155
+ | `RemoteStartTransaction` | Initiate a charging session remotely for an identified customer or token. |
156
+ | `RemoteStopTransaction` | Terminate active charging sessions from the control center. |
157
+ | `ReserveNow` | Reserve connectors for upcoming sessions with automatic connector selection and confirmation tracking. |
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. |
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. |
162
+
163
+ **OCPP 1.6 roadmap.** The following catalogue actions are in our backlog: `ChangeConfiguration`, `ClearCache`, `ClearChargingProfile`, `GetCompositeSchedule`, `GetDiagnostics`, `SetChargingProfile`, `UnlockConnector`.
164
+
165
+ - Charge point reservations with automated connector assignment, energy account and RFID linkage, EVCS confirmation tracking, and control-center cancellation support.
141
166
  - [API](https://en.wikipedia.org/wiki/API) integration with [Odoo](https://www.odoo.com/), syncing:
142
167
  - Employee credentials via `res.users`
143
168
  - Product catalog lookups via `product.product`
@@ -171,7 +196,7 @@ Arthexis Constellation ships in four node roles tailored to different deployment
171
196
  <td valign="top"><strong>Multi-Device Edge, Network &amp; Data Acquisition</strong><br />Features: AP Router, Celery Queue, NGINX Server, RFID Scanner</td>
172
197
  </tr>
173
198
  <tr>
174
- <td valign="top"><strong>Constellation</strong></td>
199
+ <td valign="top"><strong>Watchtower</strong></td>
175
200
  <td valign="top"><strong>Multi-User Cloud &amp; Orchestration</strong><br />Features: Celery Queue, NGINX Server</td>
176
201
  </tr>
177
202
  </tbody>
@@ -184,7 +209,7 @@ Arthexis Constellation ships in four node roles tailored to different deployment
184
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.
185
210
 
186
211
  ### 2. Start and stop
187
- Terminal nodes can start directly with the scripts below without installing; Control, Satellite, and Constellation roles require installation first. Both approaches listen on [`http://localhost:8000/`](http://localhost:8000/) by default.
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.
188
213
 
189
214
  - **[VS Code](https://code.visualstudio.com/)**
190
215
  - Open the folder and go to the **Run and Debug** panel (`Ctrl+Shift+D`).
@@ -204,18 +229,47 @@ Terminal nodes can start directly with the scripts below without installing; Con
204
229
  - `--constellation` – enables the multi-user orchestration stack.
205
230
  - Use `./install.sh --help` to list every available flag if you need to customize the node beyond the role defaults.
206
231
  - Upgrade with [`./upgrade.sh`](upgrade.sh).
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.
207
234
 
208
235
  - **Windows:**
209
236
  - Run [`install.bat`](install.bat) to install (Terminal role) and [`upgrade.bat`](upgrade.bat) to upgrade.
210
237
  - Installation is not required to start in Terminal mode (the default).
211
238
 
212
239
  ### 4. Administration
213
- Visit [`http://localhost:8000/admin/`](http://localhost:8000/admin/) for the [Django admin](https://docs.djangoproject.com/en/stable/ref/contrib/admin/) and [`http://localhost:8000/admindocs/`](http://localhost:8000/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.
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.
241
+
242
+ ## Sigils
243
+
244
+ Sigils are bracketed tokens such as `[ENV.SMTP_PASSWORD]` that Arthexis expands at runtime. They make it possible to reference configuration secrets, system metadata, or records stored in other apps without duplicating values across the project.
245
+
246
+ ### Syntax at a glance
247
+
248
+ - `[PREFIX.KEY]` &mdash; returns a field or attribute. Hyphens and casing are normalized automatically.
249
+ - `[PREFIX=IDENTIFIER.FIELD]` &mdash; selects a specific record by primary key or any unique field.
250
+ - `[PREFIX:FIELD=VALUE.ATTRIBUTE]` &mdash; filters by a custom field instead of the primary key.
251
+ - `[PREFIX.FIELD=[OTHER.SIGIL]]` &mdash; nests sigils so the value after `=` resolves before the outer token.
252
+ - `[PREFIX]` &mdash; for entity prefixes, returns the serialized object in JSON; for configuration prefixes, resolves to an empty string when the key is missing.
253
+
254
+ The platform ships with three configuration prefixes:
255
+
256
+ - `ENV` reads environment variables.
257
+ - `CONF` reads Django settings.
258
+ - `SYS` exposes computed system information such as build metadata.
259
+
260
+ Additional prefixes are defined through **Sigil Roots**, which map a short code (for example `ROLE`, `ODOO`, or `USER`) to a Django model. You can review them from **Admin &rarr; Sigil Builder** (`/admin/sigil-builder/`), where a test console is also available.
261
+
262
+ Unknown prefixes remain in place (e.g. `[UNKNOWN.VALUE]`) and are logged.
214
263
 
215
264
  ## Support
216
265
 
217
266
  Contact us at [tecnologia@gelectriic.com](mailto:tecnologia@gelectriic.com) or visit our [web page](https://www.gelectriic.com/) for [professional services](https://en.wikipedia.org/wiki/Professional_services) and [commercial support](https://en.wikipedia.org/wiki/Technical_support).
218
267
 
268
+ ## Project Guidelines
269
+
270
+ - [AGENTS](AGENTS.md) – operating handbook for repository workflows, testing, and release management.
271
+ - [DESIGN](DESIGN.md) – visual, UX, and branding guidance that all interfaces must follow.
272
+
219
273
  ## About Me
220
274
 
221
275
  > "What, you want to know about me too? Well, I enjoy [developing software](https://en.wikipedia.org/wiki/Software_development), [role-playing games](https://en.wikipedia.org/wiki/Role-playing_game), long walks on the [beach](https://en.wikipedia.org/wiki/Beach) and a fourth secret thing."
@@ -0,0 +1,112 @@
1
+ arthexis-0.1.28.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
2
+ config/__init__.py,sha256=AwpOX7il-DAOmkdJ5dVfVJ3CWWebn1lHyQNmkw1EkDw,103
3
+ config/active_app.py,sha256=KJqYh-o91nPQjVXPEdbiJHzsI6cN9IZsBZ9O3iZ6Hyc,373
4
+ config/asgi.py,sha256=Z2HjWrxOxVU9BXcqS7dMEfOGJC48H-WPwFwokRdermY,774
5
+ config/auth_app.py,sha256=cLlKgFYV4VWsMACinKrpP-nZhXXbWZp1aj1RayDszRc,198
6
+ config/celery.py,sha256=c8fPkjfhKw0UQBr6FolzfdvRX2MQAV-dMokSlJ1Afgg,819
7
+ config/context_processors.py,sha256=p74ocuzPRFI9vKSeIaJ42Vu0V2GtGph1t-2DkRo4NMw,2449
8
+ config/horologia_app.py,sha256=puO_hObEYcLvE7PqcY_sGv1thnxJ018YKHKZWqNXha4,187
9
+ config/loadenv.py,sha256=CjXx-wBaTt1wixub4GJ5CMSMFqtiK5JURc7cPXpqO7s,287
10
+ config/logging.py,sha256=1cIbPgRshHuMKnVEEH0jKpRAlJSpewvLFbYDz7sCBG4,2104
11
+ config/middleware.py,sha256=zF8Cma0n5G8NNdh2LVeNJi7Hgl1G4mF9msRE2eRi1RU,2328
12
+ config/offline.py,sha256=X-yDcyoI4C44Y27lpkUwszY_09GwwFfazEsthKJpQ70,1382
13
+ config/settings.py,sha256=SCBjA9hxVGr7pKXsfCj94dsRdJ5NlCc8b1QTt0B_EtE,21127
14
+ config/settings_helpers.py,sha256=8bNs4eB7VHNFZyu3TfeLSu5ajnNCErc2EtqSouCIEQY,8328
15
+ config/urls.py,sha256=fB5VrIbN6zuyJADcMSOdGDb6VanS7XsDo7WTspSdCm0,7424
16
+ config/wsgi.py,sha256=zU_mKlya6hejQ21PxKacTui3dUWd4ca_-YJNSYAoMX0,433
17
+ core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ core/admin.py,sha256=7QZ-Km6vwtveKyWJBWelvwfsq1_up1aoYRL4bAEmowo,154608
19
+ core/admin_history.py,sha256=XZ4b0ryufIka-xcwboK3DzmOL-INSx5Y2fJO-aJdV70,1783
20
+ core/admindocs.py,sha256=ycD0bJ_VE6rTGf9ebXTiKdYkD8Y8hD2oQ4HxxoBURCM,6756
21
+ core/apps.py,sha256=-rOizcfR0tbjmo5N66QkT6gu2o9aXcbCPWGEkG_M_VI,14290
22
+ core/auto_upgrade.py,sha256=8JpTWPH3-bQpmtOnTa5h3wdTVAbdthr0IqJze4deDUk,2242
23
+ core/backends.py,sha256=O6QzNsX3OXi0QeIO7PCXI57VLz7HoY72ouBGTpzsSPM,10793
24
+ core/celery_utils.py,sha256=-m6m9mpQwhE788lvxoWjIr35IJS2akepVgAArjgn4Kc,2645
25
+ core/changelog.py,sha256=SRn37i5N-qb-RYV4Gpu9fg7Kv8gu4TH8ZwEmDRgN-Vo,12594
26
+ core/entity.py,sha256=o4VteOXePGEsIWJFZ3fpq3DZsdWr3hpQ9A6kFbKosSE,4844
27
+ core/environment.py,sha256=QoiMjFVmwCEln2DEa10Hht6Ymop0H6wbDsyO6-ebXp4,1651
28
+ core/fields.py,sha256=d-qGahdcv4SRcO4fwCJ6_-NnEAP5xW0k3kODdAAAHSA,5412
29
+ core/form_fields.py,sha256=h2xT8sO8EWbznsiARkxukFk69yoW6mQwqpgonA-d6aA,2496
30
+ core/github_helper.py,sha256=fkjoUPwOB19zbGuk39LNLJ5AbIVKFf3rNCtnu-JISIc,5733
31
+ core/github_issues.py,sha256=qIygOk1ZCki0eB-9o1poJ2BnaMnbHH4ewVE36hqHUuo,5223
32
+ core/github_repos.py,sha256=8KCxcEiO2Ltgde7UDTAFOyHTm_eBeZYUIZegEbrjkWA,1690
33
+ core/lcd_screen.py,sha256=WtHMlSoZXKOsdM0d-v-f8ul-LSA6FA1bEWFwho1t6s8,2573
34
+ core/liveupdate.py,sha256=22m0ueQ10-6b-9pQJHY0_5WRYA98fysXKEXOWzIr550,691
35
+ core/log_paths.py,sha256=lxvgXPgJtVNZ-kYrqV8VFle4GFQrSxG-yRTglqvclmU,3318
36
+ core/mailer.py,sha256=JpW0RnD9uZ4O-wvlqeW7CMw95IFeCSkdvbankJDwHq0,2886
37
+ core/middleware.py,sha256=j19K9SX-Emkv7BDDtAacR9g6RWsxhKHwCc8w23JFvMM,3388
38
+ core/models.py,sha256=E9QLiInbgbSIdPJBfj9K11B1Y98TVe58vWGb9OA2KZg,184233
39
+ core/notifications.py,sha256=jNLSuSCrhb8x5cDu_APeDlkrmbMejufk5eJOhssAC4I,3917
40
+ core/public_wifi.py,sha256=yydLgxOo9DmJJbM4X_23wGR3gxL3YzHno54v9GssuFA,7213
41
+ core/reference_utils.py,sha256=tffCoyE1w4_SmYzXVWOsW8aR_ZVVTSPzrGhBq8K2xzA,3631
42
+ core/release.py,sha256=tEMcM7qubmFGmER3TD_MG5j4RceHcwtBjczArZWXEWE,31357
43
+ core/rfid_import_export.py,sha256=petyhPvL0WUpehc6uGUDUhjYQ9AVvc6O49zuhDs6YFw,3516
44
+ core/sigil_builder.py,sha256=SmmYRjl11u2GpU8rt3va-TP5hwAOobJTWTUGqVAikDk,4854
45
+ core/sigil_context.py,sha256=GCzjfM6fcVvBtSbVNfmE6sx3HU8QnxnXrCIytnNpQzM,439
46
+ core/sigil_resolver.py,sha256=06Zt4g3KCJSeQJAg_NYCIbXhNejpfFclosweur0C4W4,9182
47
+ core/system.py,sha256=xpiq5w3VcTjCCdWbdc7LttGtv4h9f8Wwayl7URzGJz4,44508
48
+ core/tasks.py,sha256=oddBp2j8pi3NJsELtret4xhFXgmSMq2mgAiSlQcbKQQ,22333
49
+ core/temp_passwords.py,sha256=FieUnIUeQHmA1DoXvfJ5U6-Ayv3oDz-hSln5s_vNbA4,5271
50
+ core/test_system_info.py,sha256=KLdEgBpye54sb9iY6t5TkfvNFAnj1gcF23T7i8WYUSo,8285
51
+ core/tests.py,sha256=qkLlkhAghSsxNY60R5GzJ738JFjAMrYunJ03GCiu3II,113674
52
+ core/tests_liveupdate.py,sha256=IquU8ztk6zbzC1bQu3Nrr3RzGzuujtPwDkANJHbxg98,510
53
+ core/urls.py,sha256=YPippON1MAP2KeZZ8jHpcLO6mvbnKn1q7fdMv5Vm9dY,425
54
+ core/user_data.py,sha256=wIpOeOLhBcj8_InNZdFVTwAKZTQ-0NEWjj-4y0jDmEg,23463
55
+ core/views.py,sha256=BBlLRttTXUohVuaPuUnF58XR8G8iYPitv9npR0BC0go,90363
56
+ core/widgets.py,sha256=vlR9PlFfZGlkHm5X2cqNXuEBZSj8gmWaR6MO1mMy6kg,6904
57
+ nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
+ nodes/admin.py,sha256=QDe9wKfeW3UAk8qFvfuFOQ7W-kkcc2DVmahjDd_yvDw,92910
59
+ nodes/apps.py,sha256=jGG4xNxrZOFcL_Lh1VywsaAiAymuHFn6i0icXQ7uyjU,3059
60
+ nodes/backends.py,sha256=dmmbS0X2YIlCDz2KjoDf_L62dy--nuqZF1rEDoi2JHM,5921
61
+ nodes/dns.py,sha256=D5smXD7Rkh6E4MdL6TBL2WY8GgJg7Rx9z88LZrcMbTw,7048
62
+ nodes/feature_checks.py,sha256=8gAKUZTnC480z302SPkUBfnbYri8_vySMxuf260Qatc,5018
63
+ nodes/lcd.py,sha256=iKA8Wmq85KZD52aTzAU8ZmS144_gbdGMOXcE8yuECps,5758
64
+ nodes/models.py,sha256=D44gXggMNYrdmjly4SQcM6VNoEharLww-Dl5ootDgS4,88092
65
+ nodes/reports.py,sha256=NRYh3Y0SlZFhx31Zh2K03yO12ZrpxEHEY6T-dODA6WE,12059
66
+ nodes/rfid_sync.py,sha256=oeblawcp6xeLApdIuhsJS83OAk58Eu7pVVmgpAc0Nt8,6953
67
+ nodes/signals.py,sha256=PtOKdQfb08mV1LgSZvn7ZAcfOyy2c3Xkq4AOpBQyUdE,622
68
+ nodes/tasks.py,sha256=2mMRf9Q-tqzFk3SXGHz-d6r_sw_ibuJhQAyfbYrDudk,5111
69
+ nodes/tests.py,sha256=uoNU-F0S0bbDiMN1qpbcKP1kIuGDEIW4IUTKXLK284Y,204825
70
+ nodes/urls.py,sha256=9uMhDq-b-EWZz0u-NvPRVSPXJeXfuS-BAACvsCs6gaE,1267
71
+ nodes/utils.py,sha256=x7l8Fz8UwWBumeNrHemmLXXRW_5bkMHaaRnE-VLZFwo,6337
72
+ nodes/views.py,sha256=EF3BVcMXrqAT0Ii37AZsJo7hiBgtii1JgDGbshyjA44,65255
73
+ ocpp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ ocpp/admin.py,sha256=OlWeQ1J-s2QrbYv3PEz7iUjbHv5UkhV-ErT0NryyCvQ,96322
75
+ ocpp/apps.py,sha256=i3NqrmIamNEQBT33CIqh7HOSOPmJXCMKrZ-DUd3whqg,842
76
+ ocpp/consumers.py,sha256=F0EGa87L7QgTg-m9pzHSPYn0frGveeZfA8aphW-KfQY,97651
77
+ ocpp/evcs.py,sha256=q1mZrCVSZxXTrtYsDqH6lkeEcJ6tfSC7p9YxkDmpSCw,28883
78
+ ocpp/evcs_discovery.py,sha256=OmrzgaOHwveDRJs8AIhrM3apX8_k2PPXh_oYaYpNW3c,3876
79
+ ocpp/models.py,sha256=oB7Rx-nJ9qKmMKARRXKExXRo8gW3jwlzuZbsMsD2geE,65098
80
+ ocpp/network.py,sha256=N3je0wXckSqlHLJNQazpxrBvv0yAR7DdjfAR-hTcWDk,14149
81
+ ocpp/reference_utils.py,sha256=_UR82GfE93kv4766mHyVIfdhhyYvrT59660r3H6W55M,1072
82
+ ocpp/routing.py,sha256=3kQya-MdJ00778xDmX0esQLBP05P200V45asg-CGNoo,438
83
+ ocpp/simulator.py,sha256=vnyd59QffT79AaPhmfM_jipni_nqfG57X5tXyx1rBoc,28016
84
+ ocpp/status_display.py,sha256=YGFosd5HJETA0DcLdsjvx6EfhZSnI8Aa3cMnHG2WsBE,939
85
+ ocpp/store.py,sha256=W-vDD5HQZc1j_Hqslcs1BFgENkfBhZAZXsN9ERXqKG0,30068
86
+ ocpp/tasks.py,sha256=x-9GhhhPx1yZps6QsAfFhCS5CivawfbSa2z87U4MkdY,13966
87
+ ocpp/test_export_import.py,sha256=ouQbTCp4mxfqoK6gondlu3PPcyrT9jSbWAX5gqqgaNk,4561
88
+ ocpp/test_rfid.py,sha256=Y3J9mvGoStOZkvT1v7ZDq1abw28U-nY1yVBA4IzEFuE,41962
89
+ ocpp/tests.py,sha256=4wvJYGx7DaJacqusQm7pOdO23WTKaEUQ884LlPPTNyo,249895
90
+ ocpp/transactions_io.py,sha256=p2aUsKlCDYnZ4ZBrOM7pxXoW_w3Tbm-tvRFSjnR3x24,7738
91
+ ocpp/urls.py,sha256=JFXjnC8-k559M1XGhafiJLc2_ATmIANoq--2YlfTbRA,1871
92
+ ocpp/views.py,sha256=m8ZJgvOu00lzkC1S6odD8KNRhMEHB5yL0SauVeRbfbg,81089
93
+ pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
+ pages/admin.py,sha256=VbxkwgjrFx7lXVhwbZuPSvCkS7EC-flBpwOztHejWtE,35834
95
+ pages/apps.py,sha256=0qcTFKVX9_QgqexJtGeph1sHRqq7khJf4x5ZtkWwblg,1424
96
+ pages/checks.py,sha256=sM8_hUVM_HOIocvtTb2sY3AaSEvbTnOlO46UchGVd-0,1527
97
+ pages/context_processors.py,sha256=QHMrFX7OT89kbV5_4BOZfk-aqVYy0vsdFqkI0t7nPl0,5392
98
+ pages/defaults.py,sha256=3tjv3nFPxwpFu6poJ1Ez1MP92Q6ZvyRluftKHlU-zeI,522
99
+ pages/forms.py,sha256=r3JM5qp3_4RR01-u6XV8WDOaeiRe4OvCN8Y52FcsAwI,7909
100
+ pages/middleware.py,sha256=-tXFju1siXvzVsHcgjClfTtryw-5-PwW0171DQQxKu4,7115
101
+ pages/models.py,sha256=c_u1GSPFksXXvMq6iPKu4a7_ty-ugEoHgnTvrz83kh4,23904
102
+ pages/module_defaults.py,sha256=rCAY8aTyxYNL0M5zDr393rX-Gi-svXqKtuLXm0rILrQ,5444
103
+ pages/site_config.py,sha256=f1Me0GFdHeGbIeyMlQNzD2e6hym59YHqbz92U_ppffY,4057
104
+ pages/tasks.py,sha256=ivcba_3wSQ1-cku0oDplzw6vLeQ9hBq3R4TG-LmR5gs,1913
105
+ pages/tests.py,sha256=K3KLPXJREi5zCZsiQCgvxuZ6HaJJNOHthtj0MdDAh3Y,161499
106
+ pages/urls.py,sha256=-6Ym5YOQ0oHr3YdK8BXZbSRMydWXM2sj7LoQ8_zBk6E,1566
107
+ pages/utils.py,sha256=vEFrXSzN-3wsK2H687_oVKSwsSOP_NB7DXg1hHwHink,2471
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/asgi.py CHANGED
@@ -9,35 +9,21 @@ https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
9
9
 
10
10
  import os
11
11
  from config.loadenv import loadenv
12
- from typing import Any, Awaitable, Callable, Dict, MutableMapping
13
12
  from channels.auth import AuthMiddlewareStack
14
13
  from channels.routing import ProtocolTypeRouter, URLRouter
15
14
  from django.core.asgi import get_asgi_application
16
15
  import ocpp.routing
17
16
 
18
- from core.mcp.asgi import application as mcp_application
19
- from core.mcp.asgi import is_mcp_scope
20
-
21
17
  loadenv()
22
18
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
23
19
 
24
20
  django_asgi_app = get_asgi_application()
25
21
 
26
- Scope = MutableMapping[str, Any]
27
- Receive = Callable[[], Awaitable[Dict[str, Any]]]
28
- Send = Callable[[Dict[str, Any]], Awaitable[None]]
29
-
30
22
  websocket_patterns = ocpp.routing.websocket_urlpatterns
31
23
 
32
- async def http_application(scope: Scope, receive: Receive, send: Send) -> None:
33
- if is_mcp_scope(scope):
34
- await mcp_application(scope, receive, send)
35
- else:
36
- await django_asgi_app(scope, receive, send)
37
-
38
24
  application = ProtocolTypeRouter(
39
25
  {
40
- "http": http_application,
26
+ "http": django_asgi_app,
41
27
  "websocket": AuthMiddlewareStack(URLRouter(websocket_patterns)),
42
28
  }
43
29
  )
config/middleware.py CHANGED
@@ -1,6 +1,8 @@
1
- from utils.sites import get_site
2
1
  import socket
2
+ from django.core.exceptions import DisallowedHost
3
+ from django.http import HttpResponsePermanentRedirect
3
4
  from nodes.models import Node
5
+ from utils.sites import get_site
4
6
 
5
7
  from .active_app import set_active_app
6
8
 
@@ -17,9 +19,53 @@ class ActiveAppMiddleware:
17
19
  role_name = node.role.name if node and node.role else "Terminal"
18
20
  active = site.name or role_name
19
21
  set_active_app(active)
22
+ request.site = site
20
23
  request.active_app = active
21
24
  try:
22
25
  response = self.get_response(request)
23
26
  finally:
24
27
  set_active_app(socket.gethostname())
25
28
  return response
29
+
30
+
31
+ def _is_https_request(request) -> bool:
32
+ if request.is_secure():
33
+ return True
34
+
35
+ forwarded_proto = request.META.get("HTTP_X_FORWARDED_PROTO", "")
36
+ if forwarded_proto:
37
+ candidate = forwarded_proto.split(",")[0].strip().lower()
38
+ if candidate == "https":
39
+ return True
40
+
41
+ forwarded_header = request.META.get("HTTP_FORWARDED", "")
42
+ for forwarded_part in forwarded_header.split(","):
43
+ for element in forwarded_part.split(";"):
44
+ key, _, value = element.partition("=")
45
+ if key.strip().lower() == "proto" and value.strip().strip('"').lower() == "https":
46
+ return True
47
+
48
+ return False
49
+
50
+
51
+ class SiteHttpsRedirectMiddleware:
52
+ """Redirect HTTP traffic to HTTPS for sites that require it."""
53
+
54
+ def __init__(self, get_response):
55
+ self.get_response = get_response
56
+
57
+ def __call__(self, request):
58
+ site = getattr(request, "site", None)
59
+ if site is None:
60
+ site = get_site(request)
61
+ request.site = site
62
+
63
+ if getattr(site, "require_https", False) and not _is_https_request(request):
64
+ try:
65
+ host = request.get_host()
66
+ except DisallowedHost: # pragma: no cover - defensive guard
67
+ host = request.META.get("HTTP_HOST", "")
68
+ redirect_url = f"https://{host}{request.get_full_path()}"
69
+ return HttpResponsePermanentRedirect(redirect_url)
70
+
71
+ return self.get_response(request)
config/settings.py CHANGED
@@ -18,6 +18,8 @@ import ipaddress
18
18
  import socket
19
19
  from core.log_paths import select_log_dir
20
20
  from django.utils.translation import gettext_lazy as _
21
+ from datetime import timedelta
22
+
21
23
  from celery.schedules import crontab
22
24
  from django.http import request as http_request
23
25
  from django.http.request import split_domain_port
@@ -29,6 +31,7 @@ from urllib.parse import urlsplit
29
31
  import django.utils.encoding as encoding
30
32
 
31
33
  from config.settings_helpers import (
34
+ discover_local_ip_addresses,
32
35
  extract_ip_from_host,
33
36
  install_validate_host_with_subnets,
34
37
  load_secret_key,
@@ -141,6 +144,11 @@ for host in _iter_local_hostnames(_local_hostname, _local_fqdn):
141
144
  ALLOWED_HOSTS.append(host)
142
145
 
143
146
 
147
+ for address in discover_local_ip_addresses():
148
+ if address not in ALLOWED_HOSTS:
149
+ ALLOWED_HOSTS.append(address)
150
+
151
+
144
152
  # Allow CSRF origin verification for hosts within allowed subnets.
145
153
  _original_origin_verified = CsrfViewMiddleware._origin_verified
146
154
  _original_check_referer = CsrfViewMiddleware._check_referer
@@ -333,6 +341,7 @@ CsrfViewMiddleware._check_referer = _check_referer_with_forwarded
333
341
  # Application definition
334
342
 
335
343
  LOCAL_APPS = [
344
+ "api",
336
345
  "nodes",
337
346
  "core",
338
347
  "ocpp",
@@ -390,6 +399,7 @@ MIDDLEWARE = [
390
399
  "whitenoise.middleware.WhiteNoiseMiddleware",
391
400
  "django.contrib.sessions.middleware.SessionMiddleware",
392
401
  "config.middleware.ActiveAppMiddleware",
402
+ "config.middleware.SiteHttpsRedirectMiddleware",
393
403
  "django.middleware.locale.LocaleMiddleware",
394
404
  "django.middleware.common.CommonMiddleware",
395
405
  "django.middleware.csrf.CsrfViewMiddleware",
@@ -443,32 +453,6 @@ ASGI_APPLICATION = "config.asgi.application"
443
453
  CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
444
454
 
445
455
 
446
- # MCP sigil resolver configuration
447
- def _env_int(name: str, default: int) -> int:
448
- try:
449
- return int(os.environ.get(name, default))
450
- except (TypeError, ValueError): # pragma: no cover - defensive
451
- return default
452
-
453
-
454
- def _split_env_list(name: str) -> list[str]:
455
- raw = os.environ.get(name)
456
- if not raw:
457
- return []
458
- return [item.strip() for item in raw.split(",") if item.strip()]
459
-
460
-
461
- MCP_SIGIL_SERVER = {
462
- "host": os.environ.get("MCP_SIGIL_HOST", "127.0.0.1"),
463
- "port": _env_int("MCP_SIGIL_PORT", 8800),
464
- "api_keys": _split_env_list("MCP_SIGIL_API_KEYS"),
465
- "required_scopes": ["sigils:read"],
466
- "issuer_url": os.environ.get("MCP_SIGIL_ISSUER_URL"),
467
- "resource_server_url": os.environ.get("MCP_SIGIL_RESOURCE_URL"),
468
- "mount_path": os.environ.get("MCP_SIGIL_MOUNT_PATH", "/mcp"),
469
- }
470
-
471
-
472
456
  # Custom user model
473
457
  AUTH_USER_MODEL = "core.User"
474
458
 
@@ -480,6 +464,9 @@ AUTHENTICATION_BACKENDS = [
480
464
  "core.backends.RFIDBackend",
481
465
  ]
482
466
 
467
+ # Use the custom login view for all authentication redirects.
468
+ LOGIN_URL = "pages:login"
469
+
483
470
  # Issuer name used when generating otpauth URLs for authenticator apps.
484
471
  OTP_TOTP_ISSUER = os.environ.get("OTP_TOTP_ISSUER", "Arthexis")
485
472
 
@@ -581,7 +568,7 @@ AUTH_PASSWORD_VALIDATORS = [
581
568
  LANGUAGE_CODE = "en-us"
582
569
 
583
570
  LANGUAGES = [
584
- ("es", _("Spanish")),
571
+ ("es", _("Spanish (Latin America)")),
585
572
  ("en", _("English")),
586
573
  ("it", _("Italian")),
587
574
  ("de", _("German")),
@@ -684,8 +671,12 @@ CELERY_BEAT_SCHEDULE = {
684
671
  "task": "core.tasks.heartbeat",
685
672
  "schedule": crontab(minute="*/5"),
686
673
  },
687
- "birthday_greetings": {
688
- "task": "core.tasks.birthday_greetings",
689
- "schedule": crontab(hour=9, minute=0),
674
+ "ocpp_configuration_check": {
675
+ "task": "ocpp.tasks.schedule_daily_charge_point_configuration_checks",
676
+ "schedule": crontab(minute=0, hour=0),
677
+ },
678
+ "ocpp_forwarding_push": {
679
+ "task": "ocpp.tasks.push_forwarded_charge_points",
680
+ "schedule": timedelta(minutes=10),
690
681
  },
691
682
  }