lifx-emulator 2.3.1__tar.gz → 3.0.1__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.
- lifx_emulator-3.0.1/CHANGELOG.md +27 -0
- lifx_emulator-3.0.1/PKG-INFO +102 -0
- lifx_emulator-3.0.1/README.md +74 -0
- {lifx_emulator-2.3.1 → lifx_emulator-3.0.1}/pyproject.toml +22 -82
- lifx_emulator-3.0.1/src/lifx_emulator_app/__init__.py +10 -0
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/__main__.py +13 -5
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/__init__.py +1 -1
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/app.py +3 -3
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/mappers/__init__.py +1 -1
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/mappers/device_mapper.py +1 -1
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/models.py +1 -2
- lifx_emulator-3.0.1/src/lifx_emulator_app/api/routers/__init__.py +11 -0
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/routers/devices.py +2 -2
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/routers/monitoring.py +1 -1
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/routers/scenarios.py +1 -1
- lifx_emulator-3.0.1/src/lifx_emulator_app/api/services/__init__.py +8 -0
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/services/device_service.py +3 -2
- lifx_emulator-3.0.1/tests/conftest.py +9 -0
- {lifx_emulator-2.3.1 → lifx_emulator-3.0.1}/tests/test_api.py +3 -4
- {lifx_emulator-2.3.1 → lifx_emulator-3.0.1}/tests/test_api_validation.py +1 -2
- {lifx_emulator-2.3.1 → lifx_emulator-3.0.1}/tests/test_cli.py +45 -46
- {lifx_emulator-2.3.1 → lifx_emulator-3.0.1}/tests/test_cli_validation.py +1 -2
- lifx_emulator-2.3.1/.github/workflows/ci.yml +0 -188
- lifx_emulator-2.3.1/.github/workflows/docs.yml +0 -111
- lifx_emulator-2.3.1/.pre-commit-config.yaml +0 -101
- lifx_emulator-2.3.1/CLAUDE.md +0 -940
- lifx_emulator-2.3.1/LICENSE +0 -35
- lifx_emulator-2.3.1/PKG-INFO +0 -107
- lifx_emulator-2.3.1/README.md +0 -79
- lifx_emulator-2.3.1/docs/advanced/device-management-api.md +0 -658
- lifx_emulator-2.3.1/docs/advanced/index.md +0 -145
- lifx_emulator-2.3.1/docs/advanced/scenario-api.md +0 -809
- lifx_emulator-2.3.1/docs/advanced/scenarios.md +0 -406
- lifx_emulator-2.3.1/docs/advanced/storage.md +0 -291
- lifx_emulator-2.3.1/docs/api/device.md +0 -489
- lifx_emulator-2.3.1/docs/api/factories.md +0 -340
- lifx_emulator-2.3.1/docs/api/index.md +0 -265
- lifx_emulator-2.3.1/docs/api/products.md +0 -539
- lifx_emulator-2.3.1/docs/api/protocol.md +0 -718
- lifx_emulator-2.3.1/docs/api/server.md +0 -437
- lifx_emulator-2.3.1/docs/api/storage.md +0 -680
- lifx_emulator-2.3.1/docs/architecture/device-state.md +0 -3
- lifx_emulator-2.3.1/docs/architecture/index.md +0 -76
- lifx_emulator-2.3.1/docs/architecture/overview.md +0 -373
- lifx_emulator-2.3.1/docs/architecture/packet-flow.md +0 -3
- lifx_emulator-2.3.1/docs/architecture/protocol.md +0 -3
- lifx_emulator-2.3.1/docs/assets/favicon.png +0 -1
- lifx_emulator-2.3.1/docs/changelog.md +0 -91
- lifx_emulator-2.3.1/docs/faq.md +0 -574
- lifx_emulator-2.3.1/docs/getting-started/cli.md +0 -473
- lifx_emulator-2.3.1/docs/getting-started/index.md +0 -83
- lifx_emulator-2.3.1/docs/getting-started/installation.md +0 -165
- lifx_emulator-2.3.1/docs/getting-started/quickstart.md +0 -182
- lifx_emulator-2.3.1/docs/guide/best-practices.md +0 -696
- lifx_emulator-2.3.1/docs/guide/device-types.md +0 -366
- lifx_emulator-2.3.1/docs/guide/framebuffers.md +0 -209
- lifx_emulator-2.3.1/docs/guide/index.md +0 -90
- lifx_emulator-2.3.1/docs/guide/integration-testing.md +0 -788
- lifx_emulator-2.3.1/docs/guide/products-and-specs.md +0 -229
- lifx_emulator-2.3.1/docs/guide/testing-scenarios.md +0 -673
- lifx_emulator-2.3.1/docs/guide/web-interface.md +0 -490
- lifx_emulator-2.3.1/docs/index.md +0 -169
- lifx_emulator-2.3.1/docs/reference/glossary.md +0 -352
- lifx_emulator-2.3.1/docs/reference/troubleshooting.md +0 -845
- lifx_emulator-2.3.1/docs/stylesheets/extra.css +0 -37
- lifx_emulator-2.3.1/docs/tutorials/01-first-device.md +0 -261
- lifx_emulator-2.3.1/docs/tutorials/02-basic.md +0 -345
- lifx_emulator-2.3.1/docs/tutorials/03-integration.md +0 -642
- lifx_emulator-2.3.1/docs/tutorials/04-advanced-scenarios.md +0 -583
- lifx_emulator-2.3.1/docs/tutorials/05-cicd.md +0 -696
- lifx_emulator-2.3.1/docs/tutorials/index.md +0 -108
- lifx_emulator-2.3.1/mkdocs.yml +0 -266
- lifx_emulator-2.3.1/renovate.json +0 -161
- lifx_emulator-2.3.1/src/lifx_emulator/__init__.py +0 -31
- lifx_emulator-2.3.1/src/lifx_emulator/api/routers/__init__.py +0 -11
- lifx_emulator-2.3.1/src/lifx_emulator/api/services/__init__.py +0 -8
- lifx_emulator-2.3.1/src/lifx_emulator/constants.py +0 -33
- lifx_emulator-2.3.1/src/lifx_emulator/devices/__init__.py +0 -37
- lifx_emulator-2.3.1/src/lifx_emulator/devices/device.py +0 -339
- lifx_emulator-2.3.1/src/lifx_emulator/devices/manager.py +0 -256
- lifx_emulator-2.3.1/src/lifx_emulator/devices/observers.py +0 -139
- lifx_emulator-2.3.1/src/lifx_emulator/devices/persistence.py +0 -308
- lifx_emulator-2.3.1/src/lifx_emulator/devices/state_restorer.py +0 -259
- lifx_emulator-2.3.1/src/lifx_emulator/devices/state_serializer.py +0 -157
- lifx_emulator-2.3.1/src/lifx_emulator/devices/states.py +0 -377
- lifx_emulator-2.3.1/src/lifx_emulator/factories/__init__.py +0 -37
- lifx_emulator-2.3.1/src/lifx_emulator/factories/builder.py +0 -373
- lifx_emulator-2.3.1/src/lifx_emulator/factories/default_config.py +0 -158
- lifx_emulator-2.3.1/src/lifx_emulator/factories/factory.py +0 -221
- lifx_emulator-2.3.1/src/lifx_emulator/factories/firmware_config.py +0 -77
- lifx_emulator-2.3.1/src/lifx_emulator/factories/serial_generator.py +0 -82
- lifx_emulator-2.3.1/src/lifx_emulator/handlers/__init__.py +0 -39
- lifx_emulator-2.3.1/src/lifx_emulator/handlers/base.py +0 -49
- lifx_emulator-2.3.1/src/lifx_emulator/handlers/device_handlers.py +0 -322
- lifx_emulator-2.3.1/src/lifx_emulator/handlers/light_handlers.py +0 -503
- lifx_emulator-2.3.1/src/lifx_emulator/handlers/multizone_handlers.py +0 -249
- lifx_emulator-2.3.1/src/lifx_emulator/handlers/registry.py +0 -110
- lifx_emulator-2.3.1/src/lifx_emulator/handlers/tile_handlers.py +0 -488
- lifx_emulator-2.3.1/src/lifx_emulator/products/__init__.py +0 -28
- lifx_emulator-2.3.1/src/lifx_emulator/products/generator.py +0 -1037
- lifx_emulator-2.3.1/src/lifx_emulator/products/registry.py +0 -1496
- lifx_emulator-2.3.1/src/lifx_emulator/products/specs.py +0 -284
- lifx_emulator-2.3.1/src/lifx_emulator/products/specs.yml +0 -352
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/__init__.py +0 -1
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/base.py +0 -446
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/const.py +0 -8
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/generator.py +0 -1384
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/header.py +0 -159
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/packets.py +0 -1351
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/protocol_types.py +0 -817
- lifx_emulator-2.3.1/src/lifx_emulator/protocol/serializer.py +0 -379
- lifx_emulator-2.3.1/src/lifx_emulator/repositories/__init__.py +0 -22
- lifx_emulator-2.3.1/src/lifx_emulator/repositories/device_repository.py +0 -155
- lifx_emulator-2.3.1/src/lifx_emulator/repositories/storage_backend.py +0 -107
- lifx_emulator-2.3.1/src/lifx_emulator/scenarios/__init__.py +0 -22
- lifx_emulator-2.3.1/src/lifx_emulator/scenarios/manager.py +0 -322
- lifx_emulator-2.3.1/src/lifx_emulator/scenarios/models.py +0 -112
- lifx_emulator-2.3.1/src/lifx_emulator/scenarios/persistence.py +0 -241
- lifx_emulator-2.3.1/src/lifx_emulator/server.py +0 -464
- lifx_emulator-2.3.1/tests/conftest.py +0 -239
- lifx_emulator-2.3.1/tests/test_async_storage.py +0 -246
- lifx_emulator-2.3.1/tests/test_backwards_compatibility.py +0 -943
- lifx_emulator-2.3.1/tests/test_device.py +0 -541
- lifx_emulator-2.3.1/tests/test_device_edge_cases.py +0 -231
- lifx_emulator-2.3.1/tests/test_device_handlers_extended.py +0 -723
- lifx_emulator-2.3.1/tests/test_device_manager.py +0 -288
- lifx_emulator-2.3.1/tests/test_handler_registry.py +0 -103
- lifx_emulator-2.3.1/tests/test_integration.py +0 -463
- lifx_emulator-2.3.1/tests/test_light_handlers_extended.py +0 -788
- lifx_emulator-2.3.1/tests/test_multizone_handlers_extended.py +0 -560
- lifx_emulator-2.3.1/tests/test_observers.py +0 -164
- lifx_emulator-2.3.1/tests/test_products_generator.py +0 -1121
- lifx_emulator-2.3.1/tests/test_products_specs.py +0 -366
- lifx_emulator-2.3.1/tests/test_protocol_generator.py +0 -815
- lifx_emulator-2.3.1/tests/test_protocol_types_coverage.py +0 -118
- lifx_emulator-2.3.1/tests/test_repositories.py +0 -131
- lifx_emulator-2.3.1/tests/test_scenario_manager.py +0 -431
- lifx_emulator-2.3.1/tests/test_scenario_persistence.py +0 -271
- lifx_emulator-2.3.1/tests/test_serializer.py +0 -460
- lifx_emulator-2.3.1/tests/test_server.py +0 -545
- lifx_emulator-2.3.1/tests/test_state_restorer.py +0 -270
- lifx_emulator-2.3.1/tests/test_tile_handlers_extended.py +0 -1265
- lifx_emulator-2.3.1/uv.lock +0 -1411
- {lifx_emulator-2.3.1 → lifx_emulator-3.0.1}/.gitignore +0 -0
- {lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/templates/dashboard.html +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
<!-- version list -->
|
|
4
|
+
|
|
5
|
+
## v3.0.1 (2025-11-26)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Adjust uv build for new monorepo layout
|
|
10
|
+
([`a0d5b7c`](https://github.com/Djelibeybi/lifx-emulator/commit/a0d5b7c1c1ab5659acc8554931f6c441654add05))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v3.0.0 (2025-11-26)
|
|
14
|
+
|
|
15
|
+
### Refactoring
|
|
16
|
+
|
|
17
|
+
- Split into monorepo with separate library and CLI packages
|
|
18
|
+
([`402fe6e`](https://github.com/Djelibeybi/lifx-emulator/commit/402fe6e6c42e4fb730d076cd4dd0bfe7743b2c57))
|
|
19
|
+
|
|
20
|
+
### Breaking Changes
|
|
21
|
+
|
|
22
|
+
- The project is now split into two packages:
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## v2.4.0 (2025-11-26)
|
|
26
|
+
|
|
27
|
+
- Initial Release
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lifx-emulator
|
|
3
|
+
Version: 3.0.1
|
|
4
|
+
Summary: Standalone LIFX Emulator with CLI and HTTP management API
|
|
5
|
+
Author-email: Avi Miller <me@dje.li>
|
|
6
|
+
Maintainer-email: Avi Miller <me@dje.li>
|
|
7
|
+
License-Expression: UPL-1.0
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Framework :: AsyncIO
|
|
10
|
+
Classifier: Framework :: FastAPI
|
|
11
|
+
Classifier: Framework :: Pytest
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: cyclopts>=4.2.0
|
|
23
|
+
Requires-Dist: fastapi>=0.115.0
|
|
24
|
+
Requires-Dist: lifx-emulator-core>=2.4.0
|
|
25
|
+
Requires-Dist: rich>=14.2.0
|
|
26
|
+
Requires-Dist: uvicorn>=0.34.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# lifx-emulator
|
|
30
|
+
|
|
31
|
+
Standalone LIFX device emulator with CLI and HTTP management API.
|
|
32
|
+
|
|
33
|
+
This package provides a ready-to-run emulator for testing LIFX LAN protocol libraries. It includes a command-line interface and an optional HTTP API for runtime device management.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install lifx-emulator
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Start with default configuration (1 color light)
|
|
45
|
+
lifx-emulator
|
|
46
|
+
|
|
47
|
+
# Create multiple device types
|
|
48
|
+
lifx-emulator --color 2 --multizone 1 --tile 1
|
|
49
|
+
|
|
50
|
+
# Enable HTTP management API
|
|
51
|
+
lifx-emulator --api
|
|
52
|
+
|
|
53
|
+
# List available LIFX products
|
|
54
|
+
lifx-emulator list-products
|
|
55
|
+
|
|
56
|
+
# Create devices by product ID
|
|
57
|
+
lifx-emulator --product 27 --product 38 --product 55
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- Command-line interface for quick emulator setup
|
|
63
|
+
- HTTP management API with web dashboard
|
|
64
|
+
- Real-time device monitoring and management
|
|
65
|
+
- Support for all LIFX device types (color, multizone, tile, infrared, HEV, switch)
|
|
66
|
+
- Persistent device state across restarts
|
|
67
|
+
- Testing scenarios for protocol edge cases
|
|
68
|
+
|
|
69
|
+
## HTTP Management API
|
|
70
|
+
|
|
71
|
+
Enable with `--api` to get:
|
|
72
|
+
|
|
73
|
+
- **Web Dashboard**: `http://localhost:8080` - Real-time monitoring UI
|
|
74
|
+
- **REST API**: Device management, statistics, and scenario control
|
|
75
|
+
- **OpenAPI Docs**: `http://localhost:8080/docs` - Interactive API documentation
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Start with API server
|
|
79
|
+
lifx-emulator --color 2 --api --api-port 9090
|
|
80
|
+
|
|
81
|
+
# Add a device via API
|
|
82
|
+
curl -X POST http://localhost:9090/api/devices \
|
|
83
|
+
-H "Content-Type: application/json" \
|
|
84
|
+
-d '{"product_id": 27}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Documentation
|
|
88
|
+
|
|
89
|
+
Full documentation is available at: **https://djelibeybi.github.io/lifx-emulator**
|
|
90
|
+
|
|
91
|
+
- [Installation Guide](https://djelibeybi.github.io/lifx-emulator/getting-started/installation/)
|
|
92
|
+
- [CLI Reference](https://djelibeybi.github.io/lifx-emulator/cli/cli-reference/)
|
|
93
|
+
- [Web Interface](https://djelibeybi.github.io/lifx-emulator/cli/web-interface/)
|
|
94
|
+
- [REST API](https://djelibeybi.github.io/lifx-emulator/cli/device-management-api/)
|
|
95
|
+
|
|
96
|
+
## Related Packages
|
|
97
|
+
|
|
98
|
+
- **[lifx-emulator-core](https://pypi.org/project/lifx-emulator-core/)**: Core library for embedding the emulator in your own projects
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
[UPL-1.0](https://opensource.org/licenses/UPL)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# lifx-emulator
|
|
2
|
+
|
|
3
|
+
Standalone LIFX device emulator with CLI and HTTP management API.
|
|
4
|
+
|
|
5
|
+
This package provides a ready-to-run emulator for testing LIFX LAN protocol libraries. It includes a command-line interface and an optional HTTP API for runtime device management.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install lifx-emulator
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Start with default configuration (1 color light)
|
|
17
|
+
lifx-emulator
|
|
18
|
+
|
|
19
|
+
# Create multiple device types
|
|
20
|
+
lifx-emulator --color 2 --multizone 1 --tile 1
|
|
21
|
+
|
|
22
|
+
# Enable HTTP management API
|
|
23
|
+
lifx-emulator --api
|
|
24
|
+
|
|
25
|
+
# List available LIFX products
|
|
26
|
+
lifx-emulator list-products
|
|
27
|
+
|
|
28
|
+
# Create devices by product ID
|
|
29
|
+
lifx-emulator --product 27 --product 38 --product 55
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- Command-line interface for quick emulator setup
|
|
35
|
+
- HTTP management API with web dashboard
|
|
36
|
+
- Real-time device monitoring and management
|
|
37
|
+
- Support for all LIFX device types (color, multizone, tile, infrared, HEV, switch)
|
|
38
|
+
- Persistent device state across restarts
|
|
39
|
+
- Testing scenarios for protocol edge cases
|
|
40
|
+
|
|
41
|
+
## HTTP Management API
|
|
42
|
+
|
|
43
|
+
Enable with `--api` to get:
|
|
44
|
+
|
|
45
|
+
- **Web Dashboard**: `http://localhost:8080` - Real-time monitoring UI
|
|
46
|
+
- **REST API**: Device management, statistics, and scenario control
|
|
47
|
+
- **OpenAPI Docs**: `http://localhost:8080/docs` - Interactive API documentation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Start with API server
|
|
51
|
+
lifx-emulator --color 2 --api --api-port 9090
|
|
52
|
+
|
|
53
|
+
# Add a device via API
|
|
54
|
+
curl -X POST http://localhost:9090/api/devices \
|
|
55
|
+
-H "Content-Type: application/json" \
|
|
56
|
+
-d '{"product_id": 27}'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
Full documentation is available at: **https://djelibeybi.github.io/lifx-emulator**
|
|
62
|
+
|
|
63
|
+
- [Installation Guide](https://djelibeybi.github.io/lifx-emulator/getting-started/installation/)
|
|
64
|
+
- [CLI Reference](https://djelibeybi.github.io/lifx-emulator/cli/cli-reference/)
|
|
65
|
+
- [Web Interface](https://djelibeybi.github.io/lifx-emulator/cli/web-interface/)
|
|
66
|
+
- [REST API](https://djelibeybi.github.io/lifx-emulator/cli/device-management-api/)
|
|
67
|
+
|
|
68
|
+
## Related Packages
|
|
69
|
+
|
|
70
|
+
- **[lifx-emulator-core](https://pypi.org/project/lifx-emulator-core/)**: Core library for embedding the emulator in your own projects
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
[UPL-1.0](https://opensource.org/licenses/UPL)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lifx-emulator"
|
|
3
|
-
version = "
|
|
4
|
-
description = "LIFX Emulator
|
|
3
|
+
version = "3.0.1"
|
|
4
|
+
description = "Standalone LIFX Emulator with CLI and HTTP management API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
|
+
"lifx-emulator-core>=2.4.0",
|
|
8
9
|
"cyclopts>=4.2.0",
|
|
9
10
|
"fastapi>=0.115.0",
|
|
10
|
-
"pyyaml>=6.0.3",
|
|
11
11
|
"rich>=14.2.0",
|
|
12
12
|
"uvicorn>=0.34.0",
|
|
13
13
|
]
|
|
@@ -20,7 +20,9 @@ maintainers = [
|
|
|
20
20
|
{name = "Avi Miller", email = "me@dje.li"}
|
|
21
21
|
]
|
|
22
22
|
classifiers = [
|
|
23
|
+
"Environment :: Console",
|
|
23
24
|
"Framework :: AsyncIO",
|
|
25
|
+
"Framework :: FastAPI",
|
|
24
26
|
"Framework :: Pytest",
|
|
25
27
|
"Intended Audience :: Developers",
|
|
26
28
|
"Natural Language :: English",
|
|
@@ -29,66 +31,19 @@ classifiers = [
|
|
|
29
31
|
"Programming Language :: Python :: 3.12",
|
|
30
32
|
"Programming Language :: Python :: 3.13",
|
|
31
33
|
"Programming Language :: Python :: 3.14",
|
|
32
|
-
"Topic :: Software Development ::
|
|
33
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
34
|
+
"Topic :: Software Development :: Testing",
|
|
34
35
|
"Typing :: Typed"
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
[project.scripts]
|
|
38
|
-
lifx-emulator = "
|
|
39
|
-
|
|
40
|
-
[dependency-groups]
|
|
41
|
-
dev = [
|
|
42
|
-
"hatchling>=1.27.0",
|
|
43
|
-
"httpx>=0.28.0",
|
|
44
|
-
"pyright>=1.1.407",
|
|
45
|
-
"pytest>=8.4.2",
|
|
46
|
-
"pytest-asyncio>=0.24.0",
|
|
47
|
-
"pytest-cov>=7.0.0",
|
|
48
|
-
"pytest-sugar>=1.1.1",
|
|
49
|
-
"ruff>=0.14.2",
|
|
50
|
-
"mkdocs>=1.6.0",
|
|
51
|
-
"mkdocs-material>=9.6.0",
|
|
52
|
-
"mkdocstrings[python]>=0.27.0",
|
|
53
|
-
"mkdocs-git-revision-date-localized-plugin>=1.4.0",
|
|
54
|
-
"mkdocs-llmstxt>=0.3.0",
|
|
55
|
-
]
|
|
39
|
+
lifx-emulator = "lifx_emulator_app.__main__:main"
|
|
56
40
|
|
|
57
41
|
[build-system]
|
|
58
42
|
requires = ["hatchling"]
|
|
59
43
|
build-backend = "hatchling.build"
|
|
60
44
|
|
|
61
45
|
[tool.hatch.build.targets.wheel]
|
|
62
|
-
packages = ["src/
|
|
63
|
-
|
|
64
|
-
[tool.ruff]
|
|
65
|
-
line-length = 88
|
|
66
|
-
indent-width = 4
|
|
67
|
-
target-version = "py311"
|
|
68
|
-
|
|
69
|
-
[tool.ruff.format]
|
|
70
|
-
quote-style = "double"
|
|
71
|
-
indent-style = "space"
|
|
72
|
-
docstring-code-format = true
|
|
73
|
-
docstring-code-line-length = "dynamic"
|
|
74
|
-
|
|
75
|
-
[tool.ruff.lint]
|
|
76
|
-
select = ["E", "F", "I", "N", "W", "UP"]
|
|
77
|
-
ignore = []
|
|
78
|
-
|
|
79
|
-
# Complexity and code quality limits
|
|
80
|
-
[tool.ruff.lint.mccabe]
|
|
81
|
-
max-complexity = 10
|
|
82
|
-
|
|
83
|
-
[tool.ruff.lint.pylint]
|
|
84
|
-
max-args = 5
|
|
85
|
-
max-branches = 12
|
|
86
|
-
max-statements = 50
|
|
87
|
-
|
|
88
|
-
[tool.ruff.lint.per-file-ignores]
|
|
89
|
-
"src/lifx_emulator/protocol/generator.py" = ["E501"]
|
|
90
|
-
"src/lifx_emulator/protocol/packets.py" = ["E501"]
|
|
91
|
-
|
|
46
|
+
packages = ["src/lifx_emulator_app"]
|
|
92
47
|
|
|
93
48
|
[tool.pyright]
|
|
94
49
|
typeCheckingMode = "standard"
|
|
@@ -102,43 +57,28 @@ pythonpath = ["src"]
|
|
|
102
57
|
python_files = ["test_*.py"]
|
|
103
58
|
python_classes = ["Test*"]
|
|
104
59
|
python_functions = ["test_*"]
|
|
105
|
-
addopts = """\
|
|
106
|
-
-v
|
|
107
|
-
-Wdefault
|
|
108
|
-
--cov=lifx_emulator
|
|
109
|
-
--cov-branch
|
|
110
|
-
--cov-report=xml
|
|
111
|
-
--cov-report=term-missing:skip-covered
|
|
112
|
-
--junitxml=junit.xml -o junit_family=legacy
|
|
113
|
-
"""
|
|
114
60
|
asyncio_mode = "auto"
|
|
115
61
|
asyncio_default_fixture_loop_scope = "function"
|
|
116
62
|
|
|
117
|
-
[tool.coverage.run]
|
|
118
|
-
omit = [
|
|
119
|
-
"src/lifx_emulator/protocol/generator.py",
|
|
120
|
-
"src/lifx_emulator/protocol/protocol_types.py",
|
|
121
|
-
"src/lifx_emulator/products/generator.py",
|
|
122
|
-
"src/lifx_emulator/products/registry.py",
|
|
123
|
-
]
|
|
124
|
-
|
|
125
|
-
[tool.coverage.report]
|
|
126
|
-
exclude_lines = [
|
|
127
|
-
"pragma: no cover",
|
|
128
|
-
"@overload",
|
|
129
|
-
"if TYPE_CHECKING",
|
|
130
|
-
"raise NotImplementedError",
|
|
131
|
-
'if __name__ == "__main__":',
|
|
132
|
-
]
|
|
133
|
-
|
|
134
63
|
[tool.semantic_release]
|
|
64
|
+
commit_parser = "conventional-monorepo"
|
|
65
|
+
commit_message = """\
|
|
66
|
+
chore(release): lifx-emulator@{version}`
|
|
67
|
+
|
|
68
|
+
Automatically generated by python-semantic-release
|
|
69
|
+
"""
|
|
70
|
+
tag_format = "app-v{version}"
|
|
135
71
|
version_toml = ["pyproject.toml:project.version"]
|
|
136
72
|
build_command = """
|
|
137
73
|
uv lock --upgrade-package "$PACKAGE_NAME"
|
|
138
|
-
git add uv.lock
|
|
139
|
-
uv build
|
|
74
|
+
git add ../../uv.lock
|
|
75
|
+
uv build --package lifx-emulator --out-dir dist/
|
|
140
76
|
"""
|
|
141
77
|
|
|
78
|
+
[tool.semantic_release.commit_parser_options]
|
|
79
|
+
path_filters = ["."]
|
|
80
|
+
scope_prefix = "app-"
|
|
81
|
+
|
|
142
82
|
[tool.semantic_release.branches.main]
|
|
143
83
|
match = "main"
|
|
144
84
|
prerelease = false
|
|
@@ -161,7 +101,7 @@ exclude_commit_patterns = [
|
|
|
161
101
|
]
|
|
162
102
|
|
|
163
103
|
[tool.semantic_release.changelog.default_templates]
|
|
164
|
-
changelog_file = "
|
|
104
|
+
changelog_file = "CHANGELOG.md"
|
|
165
105
|
output_format = "md"
|
|
166
106
|
|
|
167
107
|
[tool.semantic_release.remote]
|
{lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/__main__.py
RENAMED
|
@@ -6,8 +6,6 @@ import signal
|
|
|
6
6
|
from typing import Annotated
|
|
7
7
|
|
|
8
8
|
import cyclopts
|
|
9
|
-
from rich.logging import RichHandler
|
|
10
|
-
|
|
11
9
|
from lifx_emulator.constants import LIFX_UDP_PORT
|
|
12
10
|
from lifx_emulator.devices import (
|
|
13
11
|
DEFAULT_STORAGE_DIR,
|
|
@@ -21,12 +19,14 @@ from lifx_emulator.factories import (
|
|
|
21
19
|
create_hev_light,
|
|
22
20
|
create_infrared_light,
|
|
23
21
|
create_multizone_light,
|
|
22
|
+
create_switch,
|
|
24
23
|
create_tile_device,
|
|
25
24
|
)
|
|
26
25
|
from lifx_emulator.products.registry import get_registry
|
|
27
26
|
from lifx_emulator.repositories import DeviceRepository
|
|
28
27
|
from lifx_emulator.scenarios import ScenarioPersistenceAsyncFile
|
|
29
28
|
from lifx_emulator.server import EmulatedLifxServer
|
|
29
|
+
from rich.logging import RichHandler
|
|
30
30
|
|
|
31
31
|
app = cyclopts.App(
|
|
32
32
|
name="lifx-emulator",
|
|
@@ -239,6 +239,7 @@ async def run(
|
|
|
239
239
|
hev: Annotated[int, cyclopts.Parameter(group=device_group)] = 0,
|
|
240
240
|
multizone: Annotated[int, cyclopts.Parameter(group=device_group)] = 0,
|
|
241
241
|
tile: Annotated[int, cyclopts.Parameter(group=device_group)] = 0,
|
|
242
|
+
switch: Annotated[int, cyclopts.Parameter(group=device_group)] = 0,
|
|
242
243
|
# Multizone Options
|
|
243
244
|
multizone_zones: Annotated[
|
|
244
245
|
int | None, cyclopts.Parameter(group=multizone_group)
|
|
@@ -284,6 +285,7 @@ async def run(
|
|
|
284
285
|
multizone_extended: Enable extended multizone support (Beam).
|
|
285
286
|
Set --no-multizone-extended for basic multizone (Z) devices.
|
|
286
287
|
tile: Number of tile/matrix chain devices.
|
|
288
|
+
switch: Number of LIFX Switch devices (relays, no lighting).
|
|
287
289
|
tile_count: Number of tiles per device. Uses product defaults if not
|
|
288
290
|
specified (5 for Tile, 1 for Candle/Ceiling).
|
|
289
291
|
tile_width: Width of each tile in zones. Uses product defaults if not
|
|
@@ -310,7 +312,7 @@ async def run(
|
|
|
310
312
|
lifx-emulator --color 2 --multizone 1 --tile 1 --api --verbose
|
|
311
313
|
|
|
312
314
|
Create only specific device types:
|
|
313
|
-
lifx-emulator --color 0 --infrared 3 --hev 2
|
|
315
|
+
lifx-emulator --color 0 --infrared 3 --hev 2 --switch 2
|
|
314
316
|
|
|
315
317
|
Custom serial prefix:
|
|
316
318
|
lifx-emulator --serial-prefix cafe00 --color 5
|
|
@@ -410,6 +412,7 @@ async def run(
|
|
|
410
412
|
and infrared == 0
|
|
411
413
|
and hev == 0
|
|
412
414
|
and multizone == 0
|
|
415
|
+
and switch == 0
|
|
413
416
|
):
|
|
414
417
|
color = 0
|
|
415
418
|
|
|
@@ -423,6 +426,7 @@ async def run(
|
|
|
423
426
|
and hev == 0
|
|
424
427
|
and multizone == 0
|
|
425
428
|
and tile == 0
|
|
429
|
+
and switch == 0
|
|
426
430
|
):
|
|
427
431
|
color = 0
|
|
428
432
|
|
|
@@ -467,13 +471,17 @@ async def run(
|
|
|
467
471
|
)
|
|
468
472
|
)
|
|
469
473
|
|
|
474
|
+
# Create switch devices
|
|
475
|
+
for _ in range(switch):
|
|
476
|
+
devices.append(create_switch(get_serial(), storage=storage))
|
|
477
|
+
|
|
470
478
|
if not devices:
|
|
471
479
|
if persistent:
|
|
472
480
|
logger.warning("No devices configured. Server will run with no devices.")
|
|
473
481
|
logger.info("Use API (--api) or restart with device flags to add devices.")
|
|
474
482
|
else:
|
|
475
483
|
logger.error(
|
|
476
|
-
"No devices configured. Use --color, --multizone, --tile, "
|
|
484
|
+
"No devices configured. Use --color, --multizone, --tile, --switch, "
|
|
477
485
|
"etc. to add devices."
|
|
478
486
|
)
|
|
479
487
|
return
|
|
@@ -520,7 +528,7 @@ async def run(
|
|
|
520
528
|
# Start API server if enabled
|
|
521
529
|
api_task = None
|
|
522
530
|
if api:
|
|
523
|
-
from
|
|
531
|
+
from lifx_emulator_app.api import run_api_server
|
|
524
532
|
|
|
525
533
|
logger.info("Starting HTTP API server on http://%s:%s", api_host, api_port)
|
|
526
534
|
api_task = asyncio.create_task(run_api_server(server, api_host, api_port))
|
{lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/__init__.py
RENAMED
|
@@ -10,7 +10,7 @@ of concerns.
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
# Import from new refactored structure
|
|
13
|
-
from
|
|
13
|
+
from lifx_emulator_app.api.app import create_api_app, run_api_server
|
|
14
14
|
|
|
15
15
|
# Note: HTML_UI remains in the old lifx_emulator/api.py file temporarily
|
|
16
16
|
# TODO: Phase 1.1d - extract HTML template to separate file
|
{lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/app.py
RENAMED
|
@@ -19,9 +19,9 @@ from fastapi.templating import Jinja2Templates
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
20
|
from lifx_emulator.server import EmulatedLifxServer
|
|
21
21
|
|
|
22
|
-
from
|
|
23
|
-
from
|
|
24
|
-
from
|
|
22
|
+
from lifx_emulator_app.api.routers.devices import create_devices_router
|
|
23
|
+
from lifx_emulator_app.api.routers.monitoring import create_monitoring_router
|
|
24
|
+
from lifx_emulator_app.api.routers.scenarios import create_scenarios_router
|
|
25
25
|
|
|
26
26
|
logger = logging.getLogger(__name__)
|
|
27
27
|
|
|
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from lifx_emulator.devices import EmulatedLifxDevice
|
|
9
9
|
|
|
10
|
-
from
|
|
10
|
+
from lifx_emulator_app.api.models import ColorHsbk, DeviceInfo
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class DeviceMapper:
|
{lifx_emulator-2.3.1/src/lifx_emulator → lifx_emulator-3.0.1/src/lifx_emulator_app}/api/models.py
RENAMED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""Pydantic models for API requests and responses."""
|
|
2
2
|
|
|
3
|
-
from pydantic import BaseModel, Field, field_validator
|
|
4
|
-
|
|
5
3
|
# Import shared domain models
|
|
6
4
|
from lifx_emulator.scenarios import ScenarioConfig
|
|
5
|
+
from pydantic import BaseModel, Field, field_validator
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class DeviceCreateRequest(BaseModel):
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""API routers for LIFX emulator endpoints."""
|
|
2
|
+
|
|
3
|
+
from lifx_emulator_app.api.routers.devices import create_devices_router
|
|
4
|
+
from lifx_emulator_app.api.routers.monitoring import create_monitoring_router
|
|
5
|
+
from lifx_emulator_app.api.routers.scenarios import create_scenarios_router
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"create_monitoring_router",
|
|
9
|
+
"create_devices_router",
|
|
10
|
+
"create_scenarios_router",
|
|
11
|
+
]
|
|
@@ -9,8 +9,8 @@ from fastapi import APIRouter, HTTPException
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from lifx_emulator.server import EmulatedLifxServer
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
12
|
+
from lifx_emulator_app.api.models import DeviceCreateRequest, DeviceInfo
|
|
13
|
+
from lifx_emulator_app.api.services.device_service import (
|
|
14
14
|
DeviceAlreadyExistsError,
|
|
15
15
|
DeviceCreationError,
|
|
16
16
|
DeviceNotFoundError,
|
|
@@ -9,7 +9,7 @@ from fastapi import APIRouter
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from lifx_emulator.server import EmulatedLifxServer
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from lifx_emulator_app.api.models import ActivityEvent, ServerStats
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def create_monitoring_router(server: EmulatedLifxServer) -> APIRouter:
|
|
@@ -9,7 +9,7 @@ from fastapi import APIRouter, HTTPException
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from lifx_emulator.server import EmulatedLifxServer
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from lifx_emulator_app.api.models import ScenarioConfig, ScenarioResponse
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def _validate_device_serial(serial: str) -> bool:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Business logic services for API endpoints."""
|
|
2
|
+
|
|
3
|
+
from lifx_emulator_app.api.services.device_service import DeviceService
|
|
4
|
+
|
|
5
|
+
# TODO: Create ScenarioService (Phase 1.1b completion)
|
|
6
|
+
# from lifx_emulator_app.api.services.scenario_service import ScenarioService
|
|
7
|
+
|
|
8
|
+
__all__ = ["DeviceService"]
|
|
@@ -13,10 +13,11 @@ from typing import TYPE_CHECKING
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from lifx_emulator.server import EmulatedLifxServer
|
|
15
15
|
|
|
16
|
-
from lifx_emulator.api.mappers import DeviceMapper
|
|
17
|
-
from lifx_emulator.api.models import DeviceCreateRequest, DeviceInfo
|
|
18
16
|
from lifx_emulator.factories import create_device
|
|
19
17
|
|
|
18
|
+
from lifx_emulator_app.api.mappers import DeviceMapper
|
|
19
|
+
from lifx_emulator_app.api.models import DeviceCreateRequest, DeviceInfo
|
|
20
|
+
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
22
23
|
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
from fastapi.testclient import TestClient
|
|
5
|
-
|
|
6
|
-
from lifx_emulator.api import create_api_app
|
|
7
5
|
from lifx_emulator.devices.manager import DeviceManager
|
|
8
6
|
from lifx_emulator.factories import create_color_light, create_multizone_light
|
|
9
7
|
from lifx_emulator.repositories import DeviceRepository
|
|
10
8
|
from lifx_emulator.server import EmulatedLifxServer
|
|
9
|
+
from lifx_emulator_app.api import create_api_app
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
@pytest.fixture
|
|
@@ -725,7 +724,7 @@ class TestRunAPIServer:
|
|
|
725
724
|
"""Test that run_api_server creates uvicorn server with correct config."""
|
|
726
725
|
from unittest.mock import AsyncMock, Mock
|
|
727
726
|
|
|
728
|
-
from
|
|
727
|
+
from lifx_emulator_app.api.app import run_api_server
|
|
729
728
|
|
|
730
729
|
# Mock uvicorn components
|
|
731
730
|
mock_server_instance = Mock()
|
|
@@ -768,7 +767,7 @@ class TestRunAPIServer:
|
|
|
768
767
|
"""Test that run_api_server uses default host and port."""
|
|
769
768
|
from unittest.mock import AsyncMock, Mock
|
|
770
769
|
|
|
771
|
-
from
|
|
770
|
+
from lifx_emulator_app.api.app import run_api_server
|
|
772
771
|
|
|
773
772
|
# Mock uvicorn components
|
|
774
773
|
mock_server_instance = Mock()
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"""Tests for API input validation using Pydantic models."""
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from lifx_emulator_app.api.models import ColorHsbk, DeviceCreateRequest
|
|
4
5
|
from pydantic import ValidationError
|
|
5
6
|
|
|
6
|
-
from lifx_emulator.api.models import ColorHsbk, DeviceCreateRequest
|
|
7
|
-
|
|
8
7
|
|
|
9
8
|
class TestDeviceCreateRequestValidation:
|
|
10
9
|
"""Test DeviceCreateRequest validation."""
|