m5stick-wireless-viewer 3.0.0__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.
- m5stick_wireless_viewer-3.0.0/.gitattributes +1 -0
- m5stick_wireless_viewer-3.0.0/.github/workflows/ci.yml +37 -0
- m5stick_wireless_viewer-3.0.0/.github/workflows/release.yml +54 -0
- m5stick_wireless_viewer-3.0.0/.gitignore +15 -0
- m5stick_wireless_viewer-3.0.0/AUTHORS.md +13 -0
- m5stick_wireless_viewer-3.0.0/Dockerfile +19 -0
- m5stick_wireless_viewer-3.0.0/PKG-INFO +125 -0
- m5stick_wireless_viewer-3.0.0/README.md +98 -0
- m5stick_wireless_viewer-3.0.0/SEGUIMIENTO.md +304 -0
- m5stick_wireless_viewer-3.0.0/docker-compose.yml +32 -0
- m5stick_wireless_viewer-3.0.0/docs/legacy-wifi-marauder-viewer-README.md +14 -0
- m5stick_wireless_viewer-3.0.0/pyproject.toml +52 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/__init__.py +3 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/cli.py +342 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/exporter/__init__.py +7 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/exporter/splunk_hec.py +246 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/models.py +98 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/__init__.py +25 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/base.py +61 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/evil_m5project.py +115 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/hash_monster.py +33 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/marauder.py +57 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/packet_monitor.py +33 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/registry.py +62 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/parser/wifi_duck.py +33 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/source/__init__.py +9 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/source/base.py +27 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/source/file_source.py +49 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/source/serial_source.py +141 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/store/__init__.py +9 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/store/base.py +149 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/store/memory_store.py +172 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/store/sqlite_store.py +317 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/__init__.py +8 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/api.py +236 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/app.py +98 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/schemas.py +190 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/sse.py +102 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/static/.gitkeep +0 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/static/css/style.css +308 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/static/js/dashboard.js +356 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/web/templates/index.html +91 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/worker/__init__.py +7 -0
- m5stick_wireless_viewer-3.0.0/src/m5wireless/worker/collector.py +96 -0
- m5stick_wireless_viewer-3.0.0/tests/conftest.py +112 -0
- m5stick_wireless_viewer-3.0.0/tests/test_api.py +232 -0
- m5stick_wireless_viewer-3.0.0/tests/test_cli.py +173 -0
- m5stick_wireless_viewer-3.0.0/tests/test_integration.py +106 -0
- m5stick_wireless_viewer-3.0.0/tests/test_packaging.py +27 -0
- m5stick_wireless_viewer-3.0.0/tests/test_parsers.py +224 -0
- m5stick_wireless_viewer-3.0.0/tests/test_serial_source.py +109 -0
- m5stick_wireless_viewer-3.0.0/tests/test_splunk_hec.py +233 -0
- m5stick_wireless_viewer-3.0.0/tests/test_sse.py +290 -0
- m5stick_wireless_viewer-3.0.0/tests/test_store.py +175 -0
- m5stick_wireless_viewer-3.0.0/tests/test_web_ui.py +83 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* text=auto eol=lf
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
|
|
22
|
+
- name: Install (editable, dev extra)
|
|
23
|
+
run: pip install -e ".[dev]"
|
|
24
|
+
|
|
25
|
+
- name: ruff check
|
|
26
|
+
run: ruff check src tests
|
|
27
|
+
|
|
28
|
+
- name: ruff format --check
|
|
29
|
+
run: ruff format --check src tests
|
|
30
|
+
|
|
31
|
+
- name: mypy --strict
|
|
32
|
+
run: mypy --strict src/m5wireless
|
|
33
|
+
|
|
34
|
+
- name: pytest (coverage)
|
|
35
|
+
run: pytest --cov=m5wireless --cov-report=term-missing
|
|
36
|
+
env:
|
|
37
|
+
CI: "true"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Build sdist + wheel
|
|
20
|
+
run: |
|
|
21
|
+
pip install build
|
|
22
|
+
python -m build
|
|
23
|
+
|
|
24
|
+
- name: Create GitHub Release + artifacts
|
|
25
|
+
uses: softprops/action-gh-release@v2
|
|
26
|
+
env:
|
|
27
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
28
|
+
with:
|
|
29
|
+
files: dist/*
|
|
30
|
+
generate_release_notes: true
|
|
31
|
+
|
|
32
|
+
pypi:
|
|
33
|
+
needs: release
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
permissions:
|
|
36
|
+
id-token: write # obligatorio para trusted publishing (OIDC)
|
|
37
|
+
# Solo se ejecuta cuando el repo tenga definida la variable PUBLISH_TO_PYPI=true.
|
|
38
|
+
# Antes de activar: crear el proyecto en PyPI y configurar trusted publishing
|
|
39
|
+
# (PyPI: Settings -> Publishing -> Add new publisher, repo + tag pattern v*).
|
|
40
|
+
if: vars.PUBLISH_TO_PYPI == 'true'
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
|
|
44
|
+
- uses: actions/setup-python@v5
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.12"
|
|
47
|
+
|
|
48
|
+
- name: Build sdist + wheel
|
|
49
|
+
run: |
|
|
50
|
+
pip install build twine
|
|
51
|
+
python -m build
|
|
52
|
+
|
|
53
|
+
- name: Publish to PyPI (trusted publishing, sin token manual)
|
|
54
|
+
run: twine upload --non-interactive dist/*
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Creditos
|
|
2
|
+
|
|
3
|
+
Este proyecto nace de la fusion de dos repositorios previos:
|
|
4
|
+
|
|
5
|
+
- `PoisonXploIT/wifi-marauder-viewer` — dashboard para logs de WiFi Marauder
|
|
6
|
+
(M5StickC/ESP32). Sus regex y logica de parseo viven en
|
|
7
|
+
`src/m5wireless/parser/marauder.py`.
|
|
8
|
+
- `PoisonXploIT/Visualizacion_extendida_M5StickPlus2` — dashboard para
|
|
9
|
+
Evil-M5Project (M5Stick Plus 2) con captura serial. Su logica vive en
|
|
10
|
+
`src/m5wireless/parser/evil_m5project.py`.
|
|
11
|
+
|
|
12
|
+
Los repos originales se conservan como referencia historica; este proyecto no
|
|
13
|
+
reescribio su historial git, asi que el blame antiguo sigue disponible alli.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# m5wireless v3.0.0 — modo file por defecto (Docker = modo secundario; la captura
|
|
2
|
+
# serial en vivo se documenta como host nativo, decision del plan).
|
|
3
|
+
FROM python:3.11-slim
|
|
4
|
+
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
|
|
7
|
+
COPY pyproject.toml README.md ./
|
|
8
|
+
COPY src ./src
|
|
9
|
+
|
|
10
|
+
RUN pip install --no-cache-dir ".[serial,web,splunk]"
|
|
11
|
+
|
|
12
|
+
# El log lo aporta el volumen; se crea vacio si no existe para que `run` arranque.
|
|
13
|
+
VOLUME ["/data"]
|
|
14
|
+
EXPOSE 8000
|
|
15
|
+
|
|
16
|
+
ENV M5W_LOG_PATH=/data/scan.log \
|
|
17
|
+
PYTHONUNBUFFERED=1
|
|
18
|
+
|
|
19
|
+
CMD ["sh", "-c", "touch /data/scan.log && exec m5wireless run --source file --log-path /data/scan.log --host 0.0.0.0"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: m5stick-wireless-viewer
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Dashboard y pipeline de datos para firmwares de hardware hacking WiFi en ESP32 (Marauder, Evil-M5Project, ...)
|
|
5
|
+
Author: PoisonXploIT
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: AUTHORS.md
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: fastapi>=0.115; extra == 'dev'
|
|
11
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
12
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
13
|
+
Requires-Dist: pyserial>=3; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
17
|
+
Requires-Dist: types-pyserial; extra == 'dev'
|
|
18
|
+
Requires-Dist: uvicorn>=0.30; extra == 'dev'
|
|
19
|
+
Provides-Extra: serial
|
|
20
|
+
Requires-Dist: pyserial>=3; extra == 'serial'
|
|
21
|
+
Provides-Extra: splunk
|
|
22
|
+
Requires-Dist: httpx>=0.27; extra == 'splunk'
|
|
23
|
+
Provides-Extra: web
|
|
24
|
+
Requires-Dist: fastapi>=0.115; extra == 'web'
|
|
25
|
+
Requires-Dist: uvicorn>=0.30; extra == 'web'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# m5stick-wireless-viewer
|
|
29
|
+
|
|
30
|
+
Pipeline de datos y dashboard para firmwares de hardware hacking WiFi en ESP32.
|
|
31
|
+
|
|
32
|
+
Fusiona `wifi-marauder-viewer` y `Visualizacion_extendida_M5StickPlus2` sobre una
|
|
33
|
+
arquitectura modular: parsers por firmware, fuentes serial/file, store con
|
|
34
|
+
historico (SQLite), API web (FastAPI + SSE) y exporter a Splunk HEC.
|
|
35
|
+
|
|
36
|
+
## Estado
|
|
37
|
+
|
|
38
|
+
v3.0.0 funcional: modelos, parsers, stores, fuentes, colector, dashboard web en
|
|
39
|
+
vivo (SSE), export CSV/JSON, CLI unificada y Docker. Seguimiento de fases en
|
|
40
|
+
`SEGUIMIENTO.md`.
|
|
41
|
+
|
|
42
|
+
## Instalacion
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install . # core + CLI (export offline, sin dependencias web)
|
|
46
|
+
pip install .[serial,web,splunk] # completo: serial en vivo + dashboard + Splunk HEC
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
El paquete crea el comando `m5wireless` (`m5wireless --version`).
|
|
50
|
+
|
|
51
|
+
## Uso rapido
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Captura serial en vivo + dashboard web (http://localhost:8000)
|
|
55
|
+
m5wireless run
|
|
56
|
+
|
|
57
|
+
# Leer un log existente (modo offline/file), dashboard en :8000
|
|
58
|
+
m5wireless run --source file --log-path scan.log
|
|
59
|
+
|
|
60
|
+
# Export offline de un log a CSV/JSON
|
|
61
|
+
m5wireless export csv --input scan.log --firmware evil_m5project --output out.csv
|
|
62
|
+
m5wireless export json --input scan.log --firmware marauder --output out.json
|
|
63
|
+
|
|
64
|
+
# Guardar el dashboard HTML periodicamente (legacy auto_save_html)
|
|
65
|
+
m5wireless snapshot --url http://localhost:8000 --interval 60 --dir snapshots
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Configuracion: CLI > variables de entorno `M5W_*` > fichero `m5wireless.toml`
|
|
69
|
+
(./ o ~/.config/m5wireless/). Ejemplo de toml:
|
|
70
|
+
|
|
71
|
+
```toml
|
|
72
|
+
[run]
|
|
73
|
+
source = "file"
|
|
74
|
+
log_path = "scan.log"
|
|
75
|
+
firmware = "auto"
|
|
76
|
+
web_port = 8000
|
|
77
|
+
|
|
78
|
+
[splunk]
|
|
79
|
+
url = "https://splunk:8088/services/collector/event"
|
|
80
|
+
token = "..."
|
|
81
|
+
verify_ssl = true
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Splunk HEC
|
|
85
|
+
|
|
86
|
+
Se activa automaticamente cuando hay URL y token configurados (`M5W_SPLUNK_HEC_URL`
|
|
87
|
+
y `M5W_SPLUNK_HEC_TOKEN`, o la seccion `[splunk]` del toml); el colector reenvia
|
|
88
|
+
cada evento sin accion manual. Robustez: envio por lotes, cola en memoria con
|
|
89
|
+
spool a disco opcional (`max_queue_size`/`spool_path`) y circuit breaker tras fallos
|
|
90
|
+
consecutivos. `verify=True` por defecto; desactivar la verificacion TLS es solo
|
|
91
|
+
vía configuracion explicita (`verify_ssl = false`).
|
|
92
|
+
|
|
93
|
+
## Docker (modo file por defecto)
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
docker compose up -d m5wireless # dashboard en http://localhost:8000
|
|
97
|
+
docker compose --profile splunk up -d # con Splunk para pruebas HEC
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
La captura serial en vivo se recomienda como host nativo (USB); el log lo aporta
|
|
101
|
+
el volumen `./data` (`/data/scan.log`).
|
|
102
|
+
|
|
103
|
+
## Firmwares soportados
|
|
104
|
+
|
|
105
|
+
| Firmware | Estado |
|
|
106
|
+
|----------|--------|
|
|
107
|
+
| WiFi Marauder (M5StickC/ESP32) | Implementado |
|
|
108
|
+
| Evil-M5Project (M5Stick Plus 2) | Implementado |
|
|
109
|
+
| WiFi Duck, Hash Monster, PacketMonitor | Stubs documentados, pendiente de fixtures reales |
|
|
110
|
+
|
|
111
|
+
## Desarrollo
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
python -m venv .venv
|
|
115
|
+
.venv/Scripts/python -m pip install -e ".[dev]" # Windows
|
|
116
|
+
# o: source .venv/bin/activate && pip install -e ".[dev]" # Linux/macOS
|
|
117
|
+
|
|
118
|
+
.venv/Scripts/python -m pytest
|
|
119
|
+
.venv/Scripts/python -m ruff check src tests
|
|
120
|
+
.venv/Scripts/python -m mypy src/m5wireless --strict
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Licencia
|
|
124
|
+
|
|
125
|
+
MIT. Ver `AUTHORS.md` para creditos de los proyectos originales.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# m5stick-wireless-viewer
|
|
2
|
+
|
|
3
|
+
Pipeline de datos y dashboard para firmwares de hardware hacking WiFi en ESP32.
|
|
4
|
+
|
|
5
|
+
Fusiona `wifi-marauder-viewer` y `Visualizacion_extendida_M5StickPlus2` sobre una
|
|
6
|
+
arquitectura modular: parsers por firmware, fuentes serial/file, store con
|
|
7
|
+
historico (SQLite), API web (FastAPI + SSE) y exporter a Splunk HEC.
|
|
8
|
+
|
|
9
|
+
## Estado
|
|
10
|
+
|
|
11
|
+
v3.0.0 funcional: modelos, parsers, stores, fuentes, colector, dashboard web en
|
|
12
|
+
vivo (SSE), export CSV/JSON, CLI unificada y Docker. Seguimiento de fases en
|
|
13
|
+
`SEGUIMIENTO.md`.
|
|
14
|
+
|
|
15
|
+
## Instalacion
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install . # core + CLI (export offline, sin dependencias web)
|
|
19
|
+
pip install .[serial,web,splunk] # completo: serial en vivo + dashboard + Splunk HEC
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
El paquete crea el comando `m5wireless` (`m5wireless --version`).
|
|
23
|
+
|
|
24
|
+
## Uso rapido
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Captura serial en vivo + dashboard web (http://localhost:8000)
|
|
28
|
+
m5wireless run
|
|
29
|
+
|
|
30
|
+
# Leer un log existente (modo offline/file), dashboard en :8000
|
|
31
|
+
m5wireless run --source file --log-path scan.log
|
|
32
|
+
|
|
33
|
+
# Export offline de un log a CSV/JSON
|
|
34
|
+
m5wireless export csv --input scan.log --firmware evil_m5project --output out.csv
|
|
35
|
+
m5wireless export json --input scan.log --firmware marauder --output out.json
|
|
36
|
+
|
|
37
|
+
# Guardar el dashboard HTML periodicamente (legacy auto_save_html)
|
|
38
|
+
m5wireless snapshot --url http://localhost:8000 --interval 60 --dir snapshots
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Configuracion: CLI > variables de entorno `M5W_*` > fichero `m5wireless.toml`
|
|
42
|
+
(./ o ~/.config/m5wireless/). Ejemplo de toml:
|
|
43
|
+
|
|
44
|
+
```toml
|
|
45
|
+
[run]
|
|
46
|
+
source = "file"
|
|
47
|
+
log_path = "scan.log"
|
|
48
|
+
firmware = "auto"
|
|
49
|
+
web_port = 8000
|
|
50
|
+
|
|
51
|
+
[splunk]
|
|
52
|
+
url = "https://splunk:8088/services/collector/event"
|
|
53
|
+
token = "..."
|
|
54
|
+
verify_ssl = true
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Splunk HEC
|
|
58
|
+
|
|
59
|
+
Se activa automaticamente cuando hay URL y token configurados (`M5W_SPLUNK_HEC_URL`
|
|
60
|
+
y `M5W_SPLUNK_HEC_TOKEN`, o la seccion `[splunk]` del toml); el colector reenvia
|
|
61
|
+
cada evento sin accion manual. Robustez: envio por lotes, cola en memoria con
|
|
62
|
+
spool a disco opcional (`max_queue_size`/`spool_path`) y circuit breaker tras fallos
|
|
63
|
+
consecutivos. `verify=True` por defecto; desactivar la verificacion TLS es solo
|
|
64
|
+
vía configuracion explicita (`verify_ssl = false`).
|
|
65
|
+
|
|
66
|
+
## Docker (modo file por defecto)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
docker compose up -d m5wireless # dashboard en http://localhost:8000
|
|
70
|
+
docker compose --profile splunk up -d # con Splunk para pruebas HEC
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
La captura serial en vivo se recomienda como host nativo (USB); el log lo aporta
|
|
74
|
+
el volumen `./data` (`/data/scan.log`).
|
|
75
|
+
|
|
76
|
+
## Firmwares soportados
|
|
77
|
+
|
|
78
|
+
| Firmware | Estado |
|
|
79
|
+
|----------|--------|
|
|
80
|
+
| WiFi Marauder (M5StickC/ESP32) | Implementado |
|
|
81
|
+
| Evil-M5Project (M5Stick Plus 2) | Implementado |
|
|
82
|
+
| WiFi Duck, Hash Monster, PacketMonitor | Stubs documentados, pendiente de fixtures reales |
|
|
83
|
+
|
|
84
|
+
## Desarrollo
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
python -m venv .venv
|
|
88
|
+
.venv/Scripts/python -m pip install -e ".[dev]" # Windows
|
|
89
|
+
# o: source .venv/bin/activate && pip install -e ".[dev]" # Linux/macOS
|
|
90
|
+
|
|
91
|
+
.venv/Scripts/python -m pytest
|
|
92
|
+
.venv/Scripts/python -m ruff check src tests
|
|
93
|
+
.venv/Scripts/python -m mypy src/m5wireless --strict
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Licencia
|
|
97
|
+
|
|
98
|
+
MIT. Ver `AUTHORS.md` para creditos de los proyectos originales.
|