niot-helfer 0.0.2__tar.gz

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.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: niot_helfer
3
+ Version: 0.0.2
4
+ Summary: Ein Helfermodul für NIT-Unterricht mit IoT Projekten
5
+ Author-email: dickschaedel <dickschaedel@example.org>
6
+ License-Expression: Unlicense
7
+ Project-URL: Homepage, https://codeberg.org/dickschaedel/niot_helfer
8
+ Project-URL: Source, https://codeberg.org/dickschaedel/niot_helfer
9
+ Keywords: education,learning
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Education
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Development Status :: 3 - Alpha
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
File without changes
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env sh
2
+ set -e
3
+
4
+ GREEN="\033[32m"
5
+ YELLOW="\033[33m"
6
+ RED="\033[31m"
7
+ RESET="\033[0m"
8
+
9
+ log() { printf "%b\n" "${GREEN}$1${RESET}"; }
10
+ warn() { printf "%b\n" "${YELLOW}$1${RESET}"; }
11
+ err() { printf "%b\n" "${RED}$1${RESET}" >&2; }
12
+
13
+ while read local_ref local_sha remote_ref remote_sha
14
+ do
15
+ case "$local_ref" in
16
+ refs/tags/v*)
17
+ # neuer Tag? (remote_sha = 40x0)
18
+ if [ "$remote_sha" != "0000000000000000000000000000000000000000" ]; then
19
+ warn "🔁 Tag existiert bereits auf dem Remote – überspringe"
20
+ continue
21
+ fi
22
+
23
+ TAG_NAME="${local_ref#refs/tags/}"
24
+
25
+ log "🔖 Neuer Versionstag erkannt: ${TAG_NAME}"
26
+ log "🧹 Alte Build-Artefakte entfernen"
27
+ rm -rf dist build *.egg-info
28
+
29
+ log "📦 Baue Paket"
30
+ python -m pip install --upgrade build twine
31
+ python -m build
32
+
33
+ log "🚀 Upload nach PyPI"
34
+ twine upload dist/*
35
+
36
+ log "✅ Release ${TAG_NAME} erfolgreich veröffentlicht"
37
+ ;;
38
+ *)
39
+ # kein Tag → nichts tun
40
+ ;;
41
+ esac
42
+ done
@@ -0,0 +1,41 @@
1
+
2
+ [build-system]
3
+ requires = [
4
+ "setuptools>=68",
5
+ "wheel",
6
+ "setuptools_scm[toml]>=8"
7
+ ]
8
+ build-backend = "setuptools.build_meta"
9
+
10
+ [project]
11
+ name = "niot_helfer"
12
+ dynamic = ["version"]
13
+ description = "Ein Helfermodul für NIT-Unterricht mit IoT Projekten"
14
+ readme = "README.md"
15
+ requires-python = ">=3.9"
16
+ license = "Unlicense"
17
+ authors = [
18
+ { name = "dickschaedel", email = "dickschaedel@example.org" }
19
+ ]
20
+ keywords = [
21
+ "education",
22
+ "learning",
23
+ ]
24
+
25
+ classifiers = [
26
+ "Programming Language :: Python :: 3",
27
+ "Topic :: Education",
28
+ "Intended Audience :: Education",
29
+ "Intended Audience :: Developers",
30
+ "Intended Audience :: End Users/Desktop",
31
+ "Development Status :: 3 - Alpha",
32
+ ]
33
+ dependencies = []
34
+
35
+ [project.urls]
36
+ Homepage = "https://codeberg.org/dickschaedel/niot_helfer"
37
+ Source = "https://codeberg.org/dickschaedel/niot_helfer"
38
+
39
+ [tool.setuptools_scm]
40
+ # akzeptiert Tags wie v1.2.3
41
+ tag_regex = "^v(?P<version>\\d+\\.\\d+\\.\\d+)$"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,19 @@
1
+ from niot_helfer import *
2
+ import machine
3
+
4
+ def handle_default(client):
5
+ return wifi.get_file("examples/schalter.html")
6
+
7
+ def handle_schalter(client):
8
+ led.value(not led.value())
9
+
10
+ # MAIN
11
+ led = machine.Pin(LED_BUILTIN, machine.Pin.OUT)
12
+
13
+ wifi = NIoTHelper(verbose=True) # wifi/Webserver Vorbereitungen
14
+ wifi.register_handler_default(handle_default)
15
+ wifi.register_handler("/", handle_default)
16
+ wifi.register_handler("/schalter", handle_schalter)
17
+ wifi.start_ap() # eigens AP aufspannen
18
+ wifi.start_webserver() # Webserver starten
19
+ wifi.run() # Kümmert sich um alle Anfragen
@@ -0,0 +1,19 @@
1
+ from niot_helfer import *
2
+ from machine import Pin
3
+
4
+ def handle_default(client):
5
+ return wifi.get_file("examples/schalter.html")
6
+
7
+ def handle_schalter(client):
8
+ led.value(not led.value())
9
+
10
+ # MAIN
11
+ led = Pin(LED_BUILTIN, Pin.OUT)
12
+
13
+ wifi = NIoTHelper(verbose=True) # wifi/Webserver Vorbereitungen
14
+ wifi.register_handler_default(handle_default)
15
+ wifi.register_handler("/", handle_default)
16
+ wifi.register_handler("/schalter", handle_schalter)
17
+ wifi.connect("ssid","pw") # mit WLAN verbinden
18
+ wifi.start_webserver() # Webserver starten
19
+ wifi.run() # Kümmert sich um alle Anfragen
@@ -0,0 +1 @@
1
+ <html><head><style>.switch { position: relative; display: inline-block; width:60px;height: 34px;}.switch input { opacity: 0; width: 0; height: 0;}.slider {position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: ccc; -webkit-transition: .4s; transition:.4s;}.slider:before { position: absolute; content: ''; height: 26px; width: 26px; left: 4px; bottom: 4px; background-color: white; -webkit-transition: .4s; transition: .4s;}input:checked + .slider { background-color: 2196F3;}input:focus + .slider { box-shadow: 0 0 1px 2196F3;}input:checked + .slider:before { -webkit-transform: translateX(26px); -ms-transform: translateX(26px); transform: translateX(26px);}/* Rounded sliders */.slider.round { border-radius: 34px;}.slider.round:before { border-radius: 50%;}</style></head><body><h2>Umschalten</h2><label class='switch' > <input type='checkbox' id='switch'> <span class='slider round'></span></label></body><script>function click() {fetch('/schalter');return true;}document.getElementById('switch').addEventListener('change',click);</script></html>
@@ -0,0 +1,100 @@
1
+ <!DOCTYPE html><html lang='en'><head>
2
+ <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no'>
3
+ <title>Einstellungen</title><script>
4
+ function waehleSSID(eintrag) {
5
+ document.getElementById('ssid').value = eintrag.innerText || eintrag.textContent;
6
+ document.getElementById('pw').focus();
7
+ }
8
+ function reqListener() {
9
+ var data = JSON.parse(this.responseText);
10
+ alert(data);
11
+ }
12
+ function reqError(err) {
13
+ document.getElementById('test').innerHTML = 'juhu';
14
+ }
15
+ function holeWLANs() {
16
+ var oReq = new XMLHttpRequest();
17
+ oReq.onload = reqListener;
18
+ oReq.onerror = reqError;
19
+ oReq.open('get', '/wlansearch', true);
20
+ oReq.send();
21
+ }
22
+ function testWifiSettings() {
23
+ fetch('/connection_test',{"method":"get"})
24
+ .then(response => {
25
+ if (!response.ok) {
26
+ throw new Error('Network response was not ok');
27
+ }
28
+ return response.json();
29
+ })
30
+ .catch(error => {
31
+ document.getElementById('msg').innerHTML = 'Fetch error';
32
+ console.error('Fetch error:', error);
33
+ });
34
+ return false;
35
+ }
36
+ async function speichernWLAN() {
37
+
38
+ const ssid = document.getElementById('ssid').value;
39
+ const pw = document.getElementById('pw').value;
40
+
41
+ try {
42
+
43
+ const response = await fetch('/speichern', {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json'
47
+ },
48
+ body: JSON.stringify({
49
+ ssid: ssid,
50
+ pw: pw
51
+ })
52
+ });
53
+
54
+ const text = await response.text();
55
+
56
+ document.getElementById('msg').innerHTML = text;
57
+
58
+ } catch(error) {
59
+
60
+ console.log(error);
61
+ alert("Fehler beim Speichern");
62
+
63
+ }
64
+
65
+ return false;
66
+ }
67
+ </script><style type='text/css'>.thinborder {border: 1px solid black;border-radius:.3rem}
68
+ button { border:0;border-radius:.3rem;background-color:#20a0f0;color:#ffffff;line-height:2.6rem;font-size:1.2rem;width:100%}
69
+ input:focus {background-color: yellow}
70
+ div,input {padding:5px;font-size:1em}
71
+ input {width:95%;margin-top:5px;margin-bottom:10px}
72
+ body {text-align:center;font-family:verdana}
73
+ .right {float:right;text-align:right}
74
+ .info {margin:10px;padding:10px;border-radius:10px;background-color:#a0e0ff}
75
+ .halbl{width:48%;float:left;padding:0pt}
76
+ .halbr{width:48%;float:right;padding:0pt}</style></head>
77
+ <body onload='holeWLANs();'>
78
+ <div style='text-align:left;display:inline-block;min-width:260px;'>
79
+ <div class='thinborder'>
80
+ <form onsubmit="return speichernWLAN()">
81
+ <div class='info'>Mit welchem WLAN soll sich der Mikrocontroller normalerweise verbinden?</div>
82
+ <label>WLAN-Name ('SSID'):</label><input id='ssid' name='ssid' length='32' autofocus placeholder='SSID eingeben oder unten ausw&auml;hlen'/>
83
+ <label>Passwort:</label><input id='pw' name='pw' length='64' type='password' placeholder='Passwort'/>
84
+ <div>
85
+ <button class='halbl' type='submit'>Speichern</button>
86
+ </form>
87
+ <div class='halbr'><button type='submit' onclick='return testWifiSettings()'>Testen</button></div>
88
+ </div>
89
+ <br style='clear:both'/>
90
+ <br/>
91
+ Verf&uuml;gbare WLANs:
92
+ <div class='thinborder'>
93
+ <!--{wlans}-->
94
+ </div>
95
+ <br/>
96
+
97
+ <div class='info' id='test'>MAC-Adresse dieses Mikrocontrollers: $MAC$</div>
98
+ </div>
99
+ </body>
100
+ </html>
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: niot_helfer
3
+ Version: 0.0.2
4
+ Summary: Ein Helfermodul für NIT-Unterricht mit IoT Projekten
5
+ Author-email: dickschaedel <dickschaedel@example.org>
6
+ License-Expression: Unlicense
7
+ Project-URL: Homepage, https://codeberg.org/dickschaedel/niot_helfer
8
+ Project-URL: Source, https://codeberg.org/dickschaedel/niot_helfer
9
+ Keywords: education,learning
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Education
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Development Status :: 3 - Alpha
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pre-push
3
+ pyproject.toml
4
+ src/index.html
5
+ src/niot_helfer.py
6
+ src/examples/example_schalter_per_wlan_als_ap.py
7
+ src/examples/example_schalter_per_wlan_als_client.py
8
+ src/examples/schalter.html
9
+ src/niot_helfer.egg-info/PKG-INFO
10
+ src/niot_helfer.egg-info/SOURCES.txt
11
+ src/niot_helfer.egg-info/dependency_links.txt
12
+ src/niot_helfer.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ examples
2
+ niot_helfer
@@ -0,0 +1,474 @@
1
+ import network
2
+ import urequests
3
+ import time
4
+ import socket
5
+ import os
6
+ import machine
7
+ import json
8
+ import gc
9
+ import sys
10
+ import uasyncio as asyncio
11
+
12
+ # Helper to detect uasyncio v3
13
+ IS_UASYNCIO_V3 = hasattr(asyncio, "__version__") and asyncio.__version__ >= (3,)
14
+
15
+ '''
16
+ NIoT Helfer - Version 0.1
17
+ ==========================
18
+ # sendas main.py
19
+ # token ghsdufjsdtzxcvfgsHSRZTG23jFTDGSdjf
20
+ # ip 192.168.0.73
21
+ '''
22
+ # globals für den ESP8266
23
+ D4 = machine.Pin(2)
24
+ LED_BUILTIN = D4
25
+ LED_BUILDIN = D4
26
+
27
+ class DNSQuery:
28
+ def __init__(self, data):
29
+ self.data = data
30
+ self.domain = ''
31
+ tipo = (data[2] >> 3) & 15 # Opcode bits
32
+ if tipo == 0: # Standard query
33
+ ini = 12
34
+ lon = data[ini]
35
+ while lon != 0:
36
+ self.domain += data[ini + 1:ini + lon + 1].decode('utf-8') + '.'
37
+ ini += lon + 1
38
+ lon = data[ini]
39
+ print("DNSQuery domain:" + self.domain)
40
+
41
+ def response(self, ip):
42
+ print("DNSQuery response: {} ==> {}".format(self.domain, ip))
43
+ if self.domain:
44
+ packet = self.data[:2] + b'\x81\x80'
45
+ packet += self.data[4:6] + self.data[4:6] + b'\x00\x00\x00\x00'
46
+ packet += self.data[12:] # Original Domain Name Question
47
+ packet += b'\xC0\x0C' # Pointer to domain name
48
+ packet += b'\x00\x01\x00\x01\x00\x00\x00\x3C\x00\x04'
49
+ packet += bytes(map(int, ip.split('.')))
50
+ return packet
51
+
52
+ class NIoTHelper:
53
+ __URL_SERVER = "http://informatik.fsg-pfullingen.de"
54
+ __URL_SENSORAPI = "/iot/webapi.php"
55
+ _hostname = "LittleGreenPanda"
56
+
57
+ # Access point constants
58
+ __AP_SSID = 'NIoT' # max 32 characters
59
+ __AP_IP = '192.168.42.1'
60
+ __AP_SUBNET = '255.255.255.0'
61
+
62
+ def __init__(self, verbose=False):
63
+ self.client = None
64
+ self.server_socket = None
65
+ self.handlers = {}
66
+ self.register_handler("/", self.handle_root)
67
+ self.handler_default = self.handle_404
68
+ self.verbose = verbose
69
+ # get and store the event loop
70
+ self.tasks = []
71
+ self.webserver_infos = None
72
+ self.run_captive_portal = False
73
+ self.wifis = []
74
+
75
+ def register_handler(self, url, fun):
76
+ self.handlers[url] = fun
77
+ def register_handler_default(self, fun):
78
+ self.handler_default = fun
79
+
80
+ def logging(self, msg):
81
+ if self.verbose:
82
+ print(msg)
83
+
84
+ def getConfig(self):
85
+ return self.client.ifconfig()
86
+
87
+ def getCredentials(self):
88
+
89
+ try:
90
+
91
+ with open("wifi.json", "r") as f:
92
+ daten = json.load(f)
93
+
94
+ ssid = daten["ssid"]
95
+ pw = daten["pw"]
96
+
97
+ print("Geladene WLAN-Daten:")
98
+ print("SSID:", ssid)
99
+
100
+ return ssid, pw
101
+
102
+ except Exception as e:
103
+
104
+ print("Keine gespeicherten WLAN-Daten gefunden")
105
+ print(e)
106
+
107
+ return None, None
108
+
109
+ def getDeviceID(self):
110
+ return "".join(["%02x" % b for b in self.client.config("mac")])
111
+
112
+ def isConnected(self):
113
+ return self.client and self.client.isconnected()
114
+
115
+ def connect(self, ssid=None, pw=None):
116
+ self.client = network.WLAN(network.WLAN.IF_STA)
117
+ network.hostname(self._hostname)
118
+ if not self.isConnected():
119
+ if not ssid or not pw:
120
+ ssid, pw = self.getCredentials()
121
+ self.logging(f'connecting to: {ssid} with pw: {pw[:max(1,len(pw)//5)]}...')
122
+ self.client.active(False)
123
+ time.sleep(1)
124
+ self.client.active(True)
125
+ time.sleep(0.5)
126
+ try:
127
+ self.client.connect(ssid,pw)
128
+ for num in range(20):
129
+ if self.isConnected():
130
+ break
131
+ else:
132
+ if self.verbose:
133
+ print(".", end="")
134
+ if num == 19:
135
+ self.logging(f'\nERROR connecting: timed out')
136
+ break
137
+ time.sleep(0.5)
138
+ print()
139
+ except Exception as e:
140
+ self.logging(f'Error connecting:\n {e}')
141
+ # FIXME: what about wrong credentials / timeout?
142
+ else:
143
+ self.logging(f'Wifi already connected. Nothing to do.')
144
+ self.logging(f'IP: {self.client.ipconfig("addr4")} MAC: {self.getDeviceID()}')
145
+ return self.client.isconnected()
146
+
147
+ def disconnect(self):
148
+ if self.client:
149
+ self.client.disconnect()
150
+
151
+ '''
152
+ The socket does not assure to successfully write all the data.
153
+ This this function repeats the sending process until
154
+ everything is transfered.
155
+ '''
156
+ def send_verified(self, conn, data):
157
+ total_sent = 0
158
+ length = len(data)
159
+ while total_sent < length:
160
+ sent = conn.send(data[total_sent:])
161
+ if sent == 0:
162
+ raise OSError("Socket connection broken")
163
+ total_sent += sent
164
+
165
+ def get_file(self, filename):
166
+ headers = ""
167
+ content = ""
168
+ try:
169
+ headers = "HTTP/1.0 200 OK\r\n"
170
+ headers += "Content-Type: text/html\r\n"
171
+ headers += "Connection: close\r\n\r\n"
172
+
173
+ with open(filename, "r") as f:
174
+ while True:
175
+ chunk = f.read()
176
+ if not chunk:
177
+ break
178
+ content += chunk
179
+ except OSError:
180
+ headers = "HTTP/1.0 404 Not Found\r\n\r\n"
181
+ print("file not found")
182
+ except Exception as e:
183
+ headers = "HTTP/1.0 500 Internal Server Error\r\n\r\n" + str(e)
184
+ return content, headers
185
+
186
+ def handle_root(self, client):
187
+ return "no handler for /", None
188
+ # self.send_verified(client, b"no handler for \\")
189
+
190
+ def handle_404(self, client):
191
+ return "404 - URL not handled", None
192
+ # self.send_verified(client, b"404 - URL not handled")
193
+
194
+ def start_webserver(self, port=80):
195
+ self.webserver_infos = {"port":port}
196
+
197
+ def stop_webserver(self):
198
+ if self.server_socket:
199
+ self.server_socket.close()
200
+
201
+ async def handle_http_connection(self, reader, writer):
202
+ # Get HTTP request line
203
+ data = await reader.readline()
204
+ request_line = data.decode()
205
+ if not request_line:
206
+ return
207
+ addr = writer.get_extra_info('peername')
208
+ method, url, version = request_line.split()
209
+ print(f"Received {method} from {addr} for {url}")
210
+
211
+ # Header lesen
212
+ headers_in = {}
213
+ content_length = 0
214
+
215
+ while True:
216
+ gc.collect()
217
+ line = await reader.readline()
218
+
219
+ if line == b'\r\n':
220
+ break
221
+
222
+ line = line.decode().strip()
223
+
224
+ if ":" in line:
225
+ key, value = line.split(":", 1)
226
+ headers_in[key.lower()] = value.strip()
227
+
228
+ # Content-Length holen
229
+ if "content-length" in headers_in:
230
+ content_length = int(headers_in["content-length"])
231
+
232
+ # POST-Daten lesen
233
+ payload = ""
234
+
235
+ if content_length > 0:
236
+ payload = await reader.read(content_length)
237
+ payload = payload.decode()
238
+
239
+ print("POST DATA:", payload)
240
+
241
+ # Handle the request
242
+ if len(request_line) > 0:
243
+ if "GET" == method or "POST" == method:
244
+ if url in self.handlers:
245
+ try:
246
+ if not callable(self.handlers[url]):
247
+ print("WARNING: a registered handler is not callable: ", url)
248
+ content, headers = self.handlers[url](payload)
249
+ except Exception as e:
250
+ print(url, e)
251
+ content, headers = "", None
252
+ else:
253
+ content, headers = self.handler_default(None)
254
+ if not headers:
255
+ headers = 'HTTP/1.0 200 OK\r\n\r\n'
256
+ writer.write(headers+content)
257
+ try:
258
+ await writer.drain()
259
+ except:
260
+ None
261
+
262
+ # Close the socket
263
+ await writer.aclose()
264
+ # print("client socket closed")
265
+ gc.collect()
266
+
267
+ def add_task(self, func):
268
+ if not callable(func):
269
+ print("WARNING: function for add_task not callable - no brackets please")
270
+ self.tasks.append(func)
271
+
272
+ def run(self):
273
+ asyncio.run(self._run())
274
+
275
+ async def _loop(self):
276
+ while True:
277
+ for t in self.tasks:
278
+ t()
279
+ await asyncio.sleep(0.1)
280
+
281
+ def wifi_table_html(self,networks):
282
+ html = "<table>"
283
+ html += "<tr><th>SSID</th><th>Signal [dB]</th></tr>"
284
+
285
+ for net in networks:
286
+ ssid = "<a href='#' onclick='waehleSSID(this)'>"+net["ssid"]+"</a>"
287
+ rssi = net["rssi"]
288
+
289
+ html += "<tr>"
290
+ html += "<td>{}</td>".format(ssid)
291
+ html += "<td>{}</td>".format(rssi)
292
+ html += "</tr>"
293
+
294
+ html += "</table>"
295
+ return html
296
+
297
+ async def scan_wifi(self):
298
+ wlan = network.WLAN(network.STA_IF)
299
+ wlan.active(True)
300
+
301
+ await asyncio.sleep(0) # Scheduler freigeben
302
+
303
+ nets = wlan.scan() # ⚠️ blockiert kurz
304
+
305
+ result = []
306
+ for ssid, bssid, channel, rssi, authmode, hidden in nets:
307
+ ssid_str = ssid.decode('utf-8', 'ignore')
308
+ mac = ':'.join('{:02x}'.format(b) for b in bssid)
309
+
310
+ result.append({
311
+ "ssid": ssid_str,
312
+ "bssid": mac,
313
+ "rssi": rssi,
314
+ "channel": channel,
315
+ "authmode": authmode,
316
+ "hidden": hidden
317
+ })
318
+ print("wifi scan done")
319
+ self.wifis = result
320
+
321
+ async def _run(self):
322
+ # create loop task for user code
323
+ asyncio.create_task(self._loop())
324
+
325
+ # create a wifi scan task
326
+ asyncio.create_task(self.scan_wifi())
327
+
328
+ # Create the server and add task to event loop
329
+ if self.webserver_infos:
330
+ print("start webserver..")
331
+ await asyncio.start_server(self.handle_http_connection, "0.0.0.0", self.webserver_infos["port"])
332
+
333
+ if self.run_captive_portal:
334
+ asyncio.create_task(self.start_dns_server())
335
+ await asyncio.Event().wait()
336
+
337
+ def start_ap_off(self):
338
+ asyncio.run(self._start_ap())
339
+
340
+ def _handle_exception(self, loop, context):
341
+ """ uasyncio v3 only: global exception handler """
342
+ print('Global exception handler')
343
+ sys.print_exception(context["exception"])
344
+ sys.exit()
345
+
346
+ def start_ap(self):
347
+ # get and prepare event loop
348
+ # loop = asyncio.get_event_loop()
349
+ # TODO: Warum den exception handler überschreiben?
350
+ # loop.set_exception_handler(self._handle_exception)
351
+
352
+ # start the wifi AP
353
+ """ setup the access point """
354
+ self.ap = network.WLAN(network.AP_IF)
355
+ self.ap.active(True)
356
+ self.ap.ifconfig((self.__AP_IP, self.__AP_SUBNET, self.__AP_IP, self.__AP_IP))
357
+ self.ap.config(essid=self.__AP_SSID, authmode=network.AUTH_OPEN)
358
+ print('Network config:', self.ap.ifconfig())
359
+
360
+ self.run_captive_portal = True
361
+ # Create the server and add task to event loop
362
+ # server = await asyncio.start_server(self.handle_http_connection, "0.0.0.0", 80)
363
+ # loop.create_task(server)
364
+
365
+ # Start the DNS server task
366
+ # loop.create_task(self.run_dns_server())
367
+
368
+ # Start looping forever
369
+ # print('Looping forever...')
370
+ # loop.run_forever()
371
+ wifi.register_handler("/wlansearch", handle_wlansearch)
372
+
373
+ def start_captive_portal(self):
374
+ self.run_captive_portal = True
375
+ self.start_webserver()
376
+ self.register_handler("/wlansearch", handle_wlansearch)
377
+ self.register_handler("/connection_test", handle_connection_test)
378
+ self.register_handler("/speichern", handle_speichern)
379
+
380
+ async def start_dns_server(self):
381
+ """ function to handle incoming dns requests """
382
+ udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
383
+ udps.setblocking(False)
384
+ udps.bind(('0.0.0.0', 53))
385
+
386
+ while True:
387
+ try:
388
+ gc.collect()
389
+ if IS_UASYNCIO_V3:
390
+ yield asyncio.core._io_queue.queue_read(udps)
391
+ else:
392
+ yield asyncio.IORead(udps)
393
+ data, addr = udps.recvfrom(128)
394
+ print("Incoming DNS request...")
395
+
396
+ DNS = DNSQuery(data)
397
+ udps.sendto(DNS.response(self.__AP_IP), addr)
398
+
399
+ print("Replying: {:s} -> {:s}".format(DNS.domain, self.__AP_IP))
400
+ print(gc.mem_free())
401
+
402
+ except Exception as e:
403
+ print("DNS server error:", e)
404
+ await asyncio.sleep_ms(3000)
405
+ udps.close()
406
+
407
+ def handle_wlansearch(x):
408
+ print("wlan_search")
409
+ asyncio.create_task(wifi.scan_wifi())
410
+ return wifi.handle_404(None)
411
+
412
+ def handle_speichern(data):
413
+
414
+ print("speichern")
415
+ print(data)
416
+
417
+ try:
418
+ daten = json.loads(data)
419
+
420
+ print("SSID:", daten["ssid"])
421
+ print("PW:", daten["pw"])
422
+
423
+ with open("wifi.json", "w") as f:
424
+ json.dump(daten, f)
425
+
426
+ return "WLAN gespeichert!", \
427
+ "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n"
428
+
429
+ except Exception as e:
430
+
431
+ print(e)
432
+
433
+ return "Fehler beim Speichern", \
434
+ "HTTP/1.0 500 ERROR\r\nContent-Type: text/plain\r\n\r\n"
435
+ return wifi.handle_404(None)
436
+
437
+ def handle_connection_test(x):
438
+ print("connection test")
439
+ wifi.connect()
440
+ print(wifi.isConnected())
441
+ return wifi.handle_404(None)
442
+
443
+ def handle_captiveportal(client):
444
+ content, headers = wifi.get_file("index.html")
445
+ mac = wifi.ap.config('mac')
446
+ mac_str = ':'.join('{:02x}'.format(b) for b in mac)
447
+ content = content.replace("$MAC$", mac_str)
448
+ content = content.replace("<!--{wlans}-->", wifi.wifi_table_html(wifi.wifis))
449
+ return content, headers
450
+
451
+ led = machine.Pin(LED_BUILTIN, machine.Pin.OUT)
452
+
453
+ def loop():
454
+ led.value(not led.value())
455
+
456
+ if __name__ == '__main__':
457
+ use_case = "ap"
458
+ if use_case == "client":
459
+ wifi = NIoTHelper(verbose=True)
460
+ wifi.connect()
461
+ if use_case == "ap":
462
+ wifi = NIoTHelper(verbose=True)
463
+ wifi.start_ap()
464
+ wifi.start_captive_portal()
465
+ wifi.register_handler_default(handle_captiveportal)
466
+ wifi.register_handler("/", handle_captiveportal)
467
+ try:
468
+ wifi.add_task(loop)
469
+ wifi.run()
470
+ except Exception as e:
471
+ print(f"ERROR: Exception occured: {e}")
472
+ wifi.stop_webserver()
473
+ wifi.disconnect()
474
+