phue2 0.0.1.dev280__tar.gz → 0.0.3__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.
- phue2-0.0.3/.cursor/rules/docs.mdc +14 -0
- phue2-0.0.3/.cursor/rules/python-style.mdc +36 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/PKG-INFO +1 -1
- {phue2-0.0.1.dev280 → phue2-0.0.3}/pyproject.toml +2 -8
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/__main__.py +11 -1
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/bridge.py +10 -6
- {phue2-0.0.1.dev280 → phue2-0.0.3}/tests/test_bridge.py +27 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/.github/workflows/lint.yml +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/.github/workflows/publish.yml +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/.github/workflows/test.yml +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/.gitignore +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/.pre-commit-config.yaml +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/LICENSE +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/README.md +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/connect_bridge.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/flicker.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/hue-rainbow.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/random_colors.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/repros/smart_home.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/rgb_colors.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/sine_wave_lights.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/tk_gui_complex.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/tk_gui_hsb.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/examples/tk_gui_simple.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/justfile +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/__init__.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/_internal/__init__.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/_internal/console.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/exceptions.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/group.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/light.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/scene.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/src/phue2/sensor.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/tests/__init__.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/tests/samples.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/tests/test_basic_import.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/tests/test_cli.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/tests/test_request.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/tests/test_scene.py +0 -0
- {phue2-0.0.1.dev280 → phue2-0.0.3}/uv.lock +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: style and tone for documentation
|
|
3
|
+
globs: *.mdx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# tone
|
|
8
|
+
- casual, clear and concise
|
|
9
|
+
- use "you" to refer to the user (e.g. "Foo is great when you want Bar")
|
|
10
|
+
|
|
11
|
+
# content
|
|
12
|
+
- breathable
|
|
13
|
+
- makes good use of admonitions and mintlify's features like <CodeGroup> and <Tip>
|
|
14
|
+
- interspersed with practical and simple examples with output in <Accordian> where apt
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: how to write, test and run python
|
|
3
|
+
globs: *.py
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Python Style
|
|
8
|
+
|
|
9
|
+
## Design
|
|
10
|
+
- aggressively minimal and elegant
|
|
11
|
+
- prefer functional over OOP
|
|
12
|
+
- keep implementation details "private" (e.g. def _impl)
|
|
13
|
+
|
|
14
|
+
## Type Hints
|
|
15
|
+
- Marvin supports Python 3.10+
|
|
16
|
+
- Use modern syntax and full type annotations
|
|
17
|
+
- Use | instead of Union
|
|
18
|
+
- Use builtins like list, dict instead of typing.List and typing.Dict
|
|
19
|
+
- use | None instead of Optional
|
|
20
|
+
- Always type optional keyword arguments correctly e.g. def f(x: int | None = None)
|
|
21
|
+
|
|
22
|
+
## Running Commands
|
|
23
|
+
`uv` gives us superpowers to resolve deps fast and on the fly. no more pip or fussing with venvs
|
|
24
|
+
|
|
25
|
+
### Install dependencies
|
|
26
|
+
- install project deps: `uv sync`
|
|
27
|
+
- install project deps with an extra: `uv sync --extra foo`
|
|
28
|
+
|
|
29
|
+
### Running script
|
|
30
|
+
- run script with existing project deps: `uv run some/script.py`
|
|
31
|
+
- run script additional deps on the fly: `uv run --with pandas script.py`
|
|
32
|
+
|
|
33
|
+
### Testing
|
|
34
|
+
- test all: `uv run pytest`
|
|
35
|
+
- test all with 3 runners: `uv run pytest -n3`
|
|
36
|
+
- test certain folder, only matching cases: `uv run pytest tests/basic -k some_test_fn_subtr`
|
|
@@ -28,20 +28,14 @@ dev = [
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
[build-system]
|
|
31
|
-
requires = ["hatchling", "
|
|
31
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
32
32
|
build-backend = "hatchling.build"
|
|
33
33
|
|
|
34
34
|
[tool.hatch.build.targets.wheel]
|
|
35
35
|
packages = ["src/phue2"]
|
|
36
36
|
|
|
37
37
|
[tool.hatch.version]
|
|
38
|
-
source = "
|
|
39
|
-
|
|
40
|
-
[tool.uv-dynamic-versioning]
|
|
41
|
-
vcs = "git"
|
|
42
|
-
style = "pep440"
|
|
43
|
-
bump = true
|
|
44
|
-
metadata = false
|
|
38
|
+
source = "vcs"
|
|
45
39
|
|
|
46
40
|
|
|
47
41
|
[tool.pytest.ini_options]
|
|
@@ -60,6 +60,12 @@ def parse_args(
|
|
|
60
60
|
)
|
|
61
61
|
parser.add_argument("--config-file-path", help="Path to the config file")
|
|
62
62
|
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--no-save-config",
|
|
65
|
+
action="store_false",
|
|
66
|
+
dest="save_config",
|
|
67
|
+
help="Do not save connection details to the config file",
|
|
68
|
+
)
|
|
63
69
|
|
|
64
70
|
# Command subparsers
|
|
65
71
|
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
|
@@ -161,7 +167,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
161
167
|
console.info(f"Connecting to bridge at {args.host}...")
|
|
162
168
|
while True:
|
|
163
169
|
try:
|
|
164
|
-
bridge = Bridge(
|
|
170
|
+
bridge = Bridge(
|
|
171
|
+
args.host,
|
|
172
|
+
config_file_path=args.config_file_path,
|
|
173
|
+
save_config=args.save_config,
|
|
174
|
+
)
|
|
165
175
|
console.success("Successfully connected to the bridge!")
|
|
166
176
|
break
|
|
167
177
|
except PhueRegistrationException:
|
|
@@ -46,6 +46,7 @@ class Bridge:
|
|
|
46
46
|
username: str | None = None,
|
|
47
47
|
config_file_path: str | None = None,
|
|
48
48
|
timeout: int = 10,
|
|
49
|
+
save_config: bool = True,
|
|
49
50
|
):
|
|
50
51
|
"""Initialization function.
|
|
51
52
|
|
|
@@ -54,6 +55,7 @@ class Bridge:
|
|
|
54
55
|
username: Optional username for the bridge
|
|
55
56
|
config_file_path: Optional path to the configuration file
|
|
56
57
|
timeout: Request timeout in seconds (default: 10)
|
|
58
|
+
save_config: If False, don't save the config file (default: True)
|
|
57
59
|
"""
|
|
58
60
|
# Determine config file path
|
|
59
61
|
if config_file_path is not None:
|
|
@@ -89,6 +91,7 @@ class Bridge:
|
|
|
89
91
|
self.ip = ip
|
|
90
92
|
self._username = username
|
|
91
93
|
self.timeout = timeout
|
|
94
|
+
self.save_config = save_config
|
|
92
95
|
self.lights_by_id: dict[int, Light] = {}
|
|
93
96
|
self.lights_by_name: dict[str, Light] = {}
|
|
94
97
|
self.sensors_by_id: dict[int, Sensor] = {}
|
|
@@ -233,12 +236,13 @@ class Bridge:
|
|
|
233
236
|
for line in response:
|
|
234
237
|
for key in line:
|
|
235
238
|
if "success" in key:
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
if self.save_config:
|
|
240
|
+
with open(self.config_file_path, "w") as f:
|
|
241
|
+
logger.info(
|
|
242
|
+
"Writing configuration file to " + self.config_file_path
|
|
243
|
+
)
|
|
244
|
+
f.write(json.dumps({self.ip: line["success"]}))
|
|
245
|
+
logger.info("Reconnecting to the bridge")
|
|
242
246
|
self._username = line["success"]["username"]
|
|
243
247
|
return
|
|
244
248
|
if "error" in key:
|
|
@@ -90,3 +90,30 @@ def test_config_file(tmp_path: Path) -> None:
|
|
|
90
90
|
# Test loading from config
|
|
91
91
|
bridge2 = phue2.Bridge(config_file_path=str(config_file))
|
|
92
92
|
assert bridge2.username == "testuser"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_no_save_config(tmp_path: Path) -> None:
|
|
96
|
+
"""Test that the config file is not saved when save_config is False."""
|
|
97
|
+
config_file = tmp_path / ".python_hue"
|
|
98
|
+
|
|
99
|
+
with respx.mock(assert_all_called=True) as mock:
|
|
100
|
+
mock.post("http://192.168.1.100/api", name="create_user").mock(
|
|
101
|
+
return_value=httpx.Response(
|
|
102
|
+
200, json=[{"success": {"username": "nosavetest"}}]
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Create a bridge with save_config=False
|
|
107
|
+
bridge = phue2.Bridge(
|
|
108
|
+
ip="192.168.1.100",
|
|
109
|
+
config_file_path=str(config_file),
|
|
110
|
+
save_config=False,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Verify config file was NOT created and username is set
|
|
114
|
+
assert not config_file.exists()
|
|
115
|
+
assert bridge.username == "nosavetest"
|
|
116
|
+
|
|
117
|
+
# Verify that trying to connect again without IP fails (as config wasn't saved)
|
|
118
|
+
with pytest.raises(phue2.PhueException):
|
|
119
|
+
phue2.Bridge(config_file_path=str(config_file))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|