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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arthexis
3
- Version: 0.1.26
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.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
@@ -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==11.3.0
68
- Requires-Dist: prompt_toolkit==3.0.51
69
- Requires-Dist: psutil==7.1.2
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.2.2
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.6.1
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: `CancelReservation`, `ChangeConfiguration`, `ClearCache`, `ClearChargingProfile`, `GetCompositeSchedule`, `GetDiagnostics`, `GetLocalListVersion`, `SendLocalList`, `SetChargingProfile`, `UnlockConnector`, `UpdateFirmware`.
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, and EVCS confirmation tracking.
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: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.
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: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.
236
241
 
237
242
  ## Sigils
238
243
 
@@ -1,4 +1,4 @@
1
- arthexis-0.1.26.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
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=1vKOC0VwPOMXXnpUr4OIDRS1C_l_PXY-yyiDo7jmflM,20970
14
- config/settings_helpers.py,sha256=0BdBciUHIkwsWa0vV_RKAd4wDuEzgE7G-42XYiES4YQ,3127
15
- config/urls.py,sha256=RtoLsMBdsuKi_hxrlUW3GFCtiAZI_zGxrgXCxqpVVYE,6949
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=IT9nfHKNRWSMmQeCVtCaNCU--aLdAGkViwIx-MZaZJ8,145219
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=S6fySxtxUzfvz8FI9dii0KI4wSyLhh5API_oeERLIsc,14084
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=-vZjeIEFplX0uvFuh0Ctl1oxYLvx0GOvBvSZa1zZwe0,174650
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=RVQA66t43Vt-jn5jIZUzYZ63FANs4onk8bXNJRl-rWo,44104
47
- core/tasks.py,sha256=nhXLn_kE5r7VMraMIG22tBNgKrVTMnvUpuVyHlmUoHg,16176
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=IMPz21KEs6OC5YbL7YaIBdmJVLjRY6MgPuZpldJB5OI,6935
50
- core/tests.py,sha256=PuxoarDS4reHNV4EDIyVRW7xIOFxZJYou1K_LI9ZNHY,105265
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=4pheHB5RqLJtmWMql30CLaCpuVqSyShXb7Sy-crRk_4,22400
54
- core/views.py,sha256=s_a2tu9xwOpVSo7vo0iDDV4RN23jxGZNR-o7JNHs1Wk,88256
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=H4wgxrVOvMhtFcl4JbLsyLklaM8WUAjS1xdF0ROkGAA,79911
58
- nodes/apps.py,sha256=oi_M2Ya8CAR8N_MoYU68u7_9u-9SlIMelzLOgYM9tDs,3059
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=27e4PCkZ8BGWnJCOwMcY2Bo9z7LoeZWiTZuISWGnrzk,3996
62
+ nodes/feature_checks.py,sha256=8gAKUZTnC480z302SPkUBfnbYri8_vySMxuf260Qatc,5018
62
63
  nodes/lcd.py,sha256=iKA8Wmq85KZD52aTzAU8ZmS144_gbdGMOXcE8yuECps,5758
63
- nodes/models.py,sha256=x_s7b2smjEsHE6O-fAz6u8gPxXM5QzwaswhLp56nm1E,83613
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=TKSUE4eILV644iPtn2Xr_UL3ZFYgzjzSAIGUYmhg3Sk,5111
68
- nodes/tests.py,sha256=-cnhtAh8bWepy9RZU_j_fFROen9GNLe-bQI4qLyngPk,199544
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=4uktDPCpkN779Uj5LSnH1PSx4cNim7UfUz8q9eX69NI,60081
72
+ nodes/views.py,sha256=EF3BVcMXrqAT0Ii37AZsJo7hiBgtii1JgDGbshyjA44,65255
72
73
  ocpp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
- ocpp/admin.py,sha256=BSfS4bwqxcC0a57xXOyIw9hFRxB0y7bLsF2gP5rRMfg,60175
74
+ ocpp/admin.py,sha256=OlWeQ1J-s2QrbYv3PEz7iUjbHv5UkhV-ErT0NryyCvQ,96322
74
75
  ocpp/apps.py,sha256=i3NqrmIamNEQBT33CIqh7HOSOPmJXCMKrZ-DUd3whqg,842
75
- ocpp/consumers.py,sha256=XfrtwJR8OLvYzxj0NIIEa_ky36UBjHROUtoEFqFK39I,75577
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=z5uy3zuTCICkrIg7rvBdnnYJzwHUyFrUP1AE-8LX8mk,47889
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=gLCSaP9KKF7li2ALlE3O3RW5eVJtoe-_YHfKhdf0VOM,18943
85
- ocpp/tasks.py,sha256=AgxSpWQtal6va_KbJsoNEOWug3NRn571ycwsSmwuJC4,15203
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=IhFSlvsI8A8D3S32sRE298nYfrmqxbv7GfVErtNU3DQ,39137
88
- ocpp/tests.py,sha256=pEVega4N2gJctjOiO8CSvMqGKrcrUtZrLSLBNSigRGU,214558
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=5ZomUtznJe3kfs8E-DtVp12eFva5jUuJdpTEczIsQ5w,1730
91
- ocpp/views.py,sha256=6p-xSN6umchsDt-Vub_rUeykC5mWAWRnV4AchkrU0wM,77246
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=vrgMu4vYCOonZ8eZ27gQvGU74PBpMi47T512Lu1__sA,5297
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=9LdIoIK2Epp3YDUk8LUWyhLW5pJ-NiuYTzO_-xKjg0c,23636
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=_bVEijMfjVq46hNeGRDZprbQXAAUyS2LhXqZ5_Tkryg,155739
105
- pages/urls.py,sha256=Oe88tm67iVHRFcGJLSBidZ0rkRQPRZ_vRt6ahxNqPek,1499
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=Yd7JRD0OQhhvYsYZLVDUxJz9zjba84jLmyhZ1K1RE0w,65286
108
- arthexis-0.1.26.dist-info/METADATA,sha256=F6dYnw0KaarTV4KJZ19kzT5p9WR2lMNLewUNSEpEumQ,13888
109
- arthexis-0.1.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
- arthexis-0.1.26.dist-info/top_level.txt,sha256=J2a2q8_BWrCZ8H2WFUNMBfO2jz8j2gax6zZh-_1QDac,29
111
- arthexis-0.1.26.dist-info/RECORD,,
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(seconds=5),
680
+ "schedule": timedelta(minutes=10),
675
681
  },
676
682
  }
@@ -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
- if _graphql_feature_enabled():
208
- urlpatterns.append(path("graphql/", EnergyGraphQLView.as_view(), name="graphql"))
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