feat(api): tcp driver registry, identify, preset push delivery
- Track Wi-Fi TCP clients, liveness pings, disconnect broadcast, bind errors via gather\n- Device list/get include connected; POST identify with __identify preset\n- Presets push/send delivery helpers; bump led-driver hello type Made-with: Cursor
This commit is contained in:
@@ -102,6 +102,7 @@ def test_upsert_wifi_tcp_client():
|
||||
assert i1 == m1
|
||||
d = devices.read(i1)
|
||||
assert d["name"] == "kitchen"
|
||||
assert d["type"] == "led"
|
||||
assert d["transport"] == "wifi"
|
||||
assert d["address"] == "192.168.1.20"
|
||||
|
||||
@@ -115,6 +116,14 @@ def test_upsert_wifi_tcp_client():
|
||||
assert again == m1
|
||||
assert devices.read(m1)["address"] == "192.168.1.99"
|
||||
|
||||
assert (
|
||||
devices.upsert_wifi_tcp_client(
|
||||
"kitchen", "192.168.1.100", m1, device_type="bogus"
|
||||
)
|
||||
== m1
|
||||
)
|
||||
assert devices.read(m1)["type"] == "led"
|
||||
|
||||
i3 = devices.upsert_wifi_tcp_client("hall", "10.0.0.5", "deadbeefcafe")
|
||||
assert i3 == "deadbeefcafe"
|
||||
assert len(devices.list()) == 3
|
||||
|
||||
@@ -6,8 +6,8 @@ Listens on the same TCP port used by led-driver WiFi transport and
|
||||
every 5 seconds sends a newline-delimited JSON message with v="1".
|
||||
|
||||
Clients talking to the real Pi registry should send a first line JSON object
|
||||
that includes device_name and mac (12 hex) so the controller can register
|
||||
the device by MAC.
|
||||
that includes device_name, mac (12 hex), and type (e.g. led) so the controller
|
||||
can register the device by MAC.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
@@ -349,9 +349,7 @@ def test_tabs_ui(browser: BrowserTest) -> bool:
|
||||
# Fill in tab name
|
||||
if browser.fill_input(By.ID, 'new-tab-name', 'Browser Test Tab'):
|
||||
print(" ✓ Filled tab name")
|
||||
# Fill in device IDs
|
||||
if browser.fill_input(By.ID, 'new-tab-ids', '1,2,3'):
|
||||
print(" ✓ Filled device IDs")
|
||||
# Devices default from registry or placeholder name "1"
|
||||
# Click create button
|
||||
if browser.click_element(By.ID, 'create-tab-btn'):
|
||||
print(" ✓ Clicked create button")
|
||||
@@ -790,7 +788,6 @@ def test_preset_drag_and_drop(browser: BrowserTest) -> bool:
|
||||
if tabs_list and 'No tabs found' in tabs_list.text:
|
||||
# Create a tab
|
||||
browser.fill_input(By.ID, 'new-tab-name', 'Drag Test Tab')
|
||||
browser.fill_input(By.ID, 'new-tab-ids', '1')
|
||||
browser.click_element(By.ID, 'create-tab-btn')
|
||||
time.sleep(1)
|
||||
|
||||
@@ -848,7 +845,7 @@ def test_preset_drag_and_drop(browser: BrowserTest) -> bool:
|
||||
print("✓ Created 3 presets for drag test")
|
||||
passed += 1
|
||||
|
||||
# Test 4: Add presets to the tab (via Edit Tab modal – Select buttons in list)
|
||||
# Test 4: Add presets to the tab (via Edit Tab modal – Add buttons in list)
|
||||
total += 1
|
||||
try:
|
||||
tab_id = browser.driver.execute_script(
|
||||
@@ -864,12 +861,12 @@ def test_preset_drag_and_drop(browser: BrowserTest) -> bool:
|
||||
time.sleep(1)
|
||||
list_el = browser.wait_for_element(By.ID, 'edit-tab-presets-list', timeout=5)
|
||||
if list_el:
|
||||
select_buttons = browser.driver.find_elements(By.XPATH, "//div[@id='edit-tab-presets-list']//button[text()='Select']")
|
||||
select_buttons = browser.driver.find_elements(By.XPATH, "//div[@id='edit-tab-presets-list']//button[text()='Add']")
|
||||
if len(select_buttons) >= 2:
|
||||
browser.driver.execute_script("arguments[0].click();", select_buttons[0])
|
||||
time.sleep(1.5)
|
||||
browser.handle_alert(accept=True, timeout=1)
|
||||
select_buttons = browser.driver.find_elements(By.XPATH, "//div[@id='edit-tab-presets-list']//button[text()='Select']")
|
||||
select_buttons = browser.driver.find_elements(By.XPATH, "//div[@id='edit-tab-presets-list']//button[text()='Add']")
|
||||
if len(select_buttons) >= 1:
|
||||
browser.driver.execute_script("arguments[0].click();", select_buttons[0])
|
||||
time.sleep(1.5)
|
||||
@@ -949,7 +946,7 @@ def test_preset_drag_and_drop(browser: BrowserTest) -> bool:
|
||||
tab_id
|
||||
)
|
||||
time.sleep(1)
|
||||
select_buttons = browser.driver.find_elements(By.XPATH, "//div[@id='edit-tab-presets-list']//button[text()='Select']")
|
||||
select_buttons = browser.driver.find_elements(By.XPATH, "//div[@id='edit-tab-presets-list']//button[text()='Add']")
|
||||
if select_buttons:
|
||||
print(" Attempting to add another preset...")
|
||||
browser.driver.execute_script("arguments[0].click();", select_buttons[0])
|
||||
@@ -973,7 +970,7 @@ def test_preset_drag_and_drop(browser: BrowserTest) -> bool:
|
||||
else:
|
||||
print(f" ✗ Still only {len(draggable_presets)} preset(s) after adding")
|
||||
else:
|
||||
print(" ✗ No Select buttons found in Edit Tab modal")
|
||||
print(" ✗ No Add buttons found in Edit Tab modal")
|
||||
else:
|
||||
print(f"✗ No presets found in tab (found {len(draggable_presets)})")
|
||||
else:
|
||||
|
||||
@@ -348,11 +348,15 @@ def test_settings_controller(server):
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_profiles_presets_tabs_endpoints(server):
|
||||
def test_profiles_presets_tabs_endpoints(server, monkeypatch):
|
||||
c: requests.Session = server["client"]
|
||||
base_url: str = server["base_url"]
|
||||
sender: DummySender = server["sender"]
|
||||
|
||||
import controllers.device as device_ctl
|
||||
|
||||
monkeypatch.setattr(device_ctl, "IDENTIFY_OFF_DELAY_S", 0.05)
|
||||
|
||||
unique_profile_name = f"pytest-profile-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
resp = c.post(f"{base_url}/profiles", json={"name": unique_profile_name})
|
||||
@@ -593,6 +597,28 @@ def test_groups_sequences_scenes_palettes_patterns_endpoints(server):
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["name"] == "pytest-dev"
|
||||
assert resp.json()["type"] == "led"
|
||||
assert resp.json().get("connected") is None
|
||||
|
||||
resp = c.get(f"{base_url}/devices")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()[dev_id].get("connected") is None
|
||||
|
||||
sender.sent.clear()
|
||||
resp = c.post(f"{base_url}/devices/{dev_id}/identify")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json().get("message")
|
||||
assert len(sender.sent) >= 1
|
||||
first = json.loads(sender.sent[0][0])
|
||||
assert "presets" in first and "select" in first
|
||||
assert first["presets"]["__identify"]["p"] == "blink"
|
||||
assert first["presets"]["__identify"]["d"] == 50
|
||||
assert first["select"]["pytest-dev"] == ["__identify"]
|
||||
deadline = time.monotonic() + 2.0
|
||||
while len(sender.sent) < 2 and time.monotonic() < deadline:
|
||||
time.sleep(0.02)
|
||||
assert len(sender.sent) >= 2
|
||||
second = json.loads(sender.sent[1][0])
|
||||
assert second.get("select") == {"pytest-dev": ["off"]}
|
||||
|
||||
resp = c.post(
|
||||
f"{base_url}/devices",
|
||||
@@ -610,6 +636,10 @@ def test_groups_sequences_scenes_palettes_patterns_endpoints(server):
|
||||
assert resp.json()[wid]["transport"] == "wifi"
|
||||
assert resp.json()[wid]["address"] == "192.168.50.10"
|
||||
|
||||
resp = c.get(f"{base_url}/devices/{wid}")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json().get("connected") is False
|
||||
|
||||
resp = c.post(
|
||||
f"{base_url}/devices",
|
||||
json={
|
||||
|
||||
Reference in New Issue
Block a user