boneIO 1.1.0.dev2__py3-none-any.whl → 1.1.0.dev3__py3-none-any.whl
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.
- boneio/core/manager/manager.py +12 -3
- boneio/core/manager/remote.py +113 -4
- boneio/core/remote/__init__.py +9 -0
- boneio/core/remote/base.py +2 -0
- boneio/core/remote/wled.py +572 -0
- boneio/integration/homeassistant.py +1 -2
- boneio/runner.py +2 -2
- boneio/schema/actions.yaml +18 -0
- boneio/schema/remote_devices.yaml +106 -3
- boneio/version.py +1 -1
- boneio/webui/frontend-dist/assets/{cssMode-Ca1DAf9-.js → cssMode-D_Y1ZXCG.js} +1 -1
- boneio/webui/frontend-dist/assets/{freemarker2-C5IgNKwL.js → freemarker2-CzSRjwLP.js} +1 -1
- boneio/webui/frontend-dist/assets/{handlebars-coN2SbcA.js → handlebars-CZgWREM_.js} +1 -1
- boneio/webui/frontend-dist/assets/{html-DauOMz0u.js → html-l_-Dx9v2.js} +1 -1
- boneio/webui/frontend-dist/assets/{htmlMode-CmodurAE.js → htmlMode-C94ufgZC.js} +1 -1
- boneio/webui/frontend-dist/assets/{index-CeFOvpPM.js → index-BtWe9nS_.js} +201 -201
- boneio/webui/frontend-dist/assets/index-D0XGmf2O.css +1 -0
- boneio/webui/frontend-dist/assets/{javascript-N6k9oxRy.js → javascript-CTWNFYCB.js} +1 -1
- boneio/webui/frontend-dist/assets/{jsonMode-DCkYHItT.js → jsonMode-6lY0okwf.js} +1 -1
- boneio/webui/frontend-dist/assets/{liquid-CuYEfgul.js → liquid-BpvwlD2T.js} +1 -1
- boneio/webui/frontend-dist/assets/{mdx-DEiUIP8w.js → mdx-CLQNFljY.js} +1 -1
- boneio/webui/frontend-dist/assets/{python-DBpf3_HZ.js → python-Cq5ZYn3v.js} +1 -1
- boneio/webui/frontend-dist/assets/{razor-S1P72cEP.js → razor-kCTdL12M.js} +1 -1
- boneio/webui/frontend-dist/assets/{tsMode-A2L2Jxa9.js → tsMode-C1oHUIMn.js} +1 -1
- boneio/webui/frontend-dist/assets/{typescript-CupJHRfY.js → typescript-Bg5eEWu8.js} +1 -1
- boneio/webui/frontend-dist/assets/{xml-DDPTy55c.js → xml-CMwCv6rv.js} +1 -1
- boneio/webui/frontend-dist/assets/{yaml-CuImUjrc.js → yaml-CVxeQdw0.js} +1 -1
- boneio/webui/frontend-dist/index.html +2 -2
- boneio/webui/frontend-dist/schem/binary_sensor.schema.json +32 -0
- boneio/webui/frontend-dist/schem/config.schema.json +327 -5
- boneio/webui/frontend-dist/schem/event.schema.json +112 -0
- boneio/webui/frontend-dist/schem/remote_devices.schema.json +179 -1
- boneio/webui/frontend-dist/schem/web.schema.json +4 -4
- boneio/webui/routes/config.py +3 -0
- boneio/webui/routes/remote_devices.py +84 -0
- boneio/webui/schema/binary_sensor.schema.json +32 -0
- boneio/webui/schema/config.schema.json +327 -5
- boneio/webui/schema/event.schema.json +112 -0
- boneio/webui/schema/remote_devices.schema.json +179 -1
- boneio/webui/schema/web.schema.json +4 -4
- boneio/webui/web_server.py +34 -29
- {boneio-1.1.0.dev2.dist-info → boneio-1.1.0.dev3.dist-info}/METADATA +2 -1
- {boneio-1.1.0.dev2.dist-info → boneio-1.1.0.dev3.dist-info}/RECORD +47 -46
- boneio/webui/frontend-dist/assets/index-BGHXdp7U.css +0 -1
- {boneio-1.1.0.dev2.dist-info → boneio-1.1.0.dev3.dist-info}/WHEEL +0 -0
- {boneio-1.1.0.dev2.dist-info → boneio-1.1.0.dev3.dist-info}/entry_points.txt +0 -0
- {boneio-1.1.0.dev2.dist-info → boneio-1.1.0.dev3.dist-info}/licenses/LICENSE +0 -0
- {boneio-1.1.0.dev2.dist-info → boneio-1.1.0.dev3.dist-info}/top_level.txt +0 -0
boneio/core/manager/manager.py
CHANGED
|
@@ -698,7 +698,7 @@ class Manager:
|
|
|
698
698
|
)
|
|
699
699
|
|
|
700
700
|
elif action == REMOTE_OUTPUT:
|
|
701
|
-
# Control output on remote device
|
|
701
|
+
# Control output on remote device (supports ESPHome lights with brightness/color and WLED effects)
|
|
702
702
|
remote_device_id = action_definition.get("remote_device")
|
|
703
703
|
output_id = action_definition.get("output_id")
|
|
704
704
|
action_output = action_definition.get("action_output", "TOGGLE")
|
|
@@ -706,6 +706,14 @@ class Manager:
|
|
|
706
706
|
device_id=remote_device_id,
|
|
707
707
|
output_id=output_id,
|
|
708
708
|
action=action_output,
|
|
709
|
+
brightness=action_definition.get("brightness"),
|
|
710
|
+
color_temp=action_definition.get("color_temp"),
|
|
711
|
+
rgb=action_definition.get("rgb"),
|
|
712
|
+
transition=action_definition.get("transition"),
|
|
713
|
+
effect=action_definition.get("effect"),
|
|
714
|
+
palette=action_definition.get("palette"),
|
|
715
|
+
effect_speed=action_definition.get("effect_speed"),
|
|
716
|
+
effect_intensity=action_definition.get("effect_intensity"),
|
|
709
717
|
)
|
|
710
718
|
|
|
711
719
|
elif action == REMOTE_COVER:
|
|
@@ -740,16 +748,17 @@ class Manager:
|
|
|
740
748
|
configure_logger(log_config, debug=0)
|
|
741
749
|
_LOGGER.info("Logger configuration reloaded successfully")
|
|
742
750
|
|
|
743
|
-
def _reload_remote_devices(self) -> None:
|
|
751
|
+
async def _reload_remote_devices(self) -> None:
|
|
744
752
|
"""Reload remote devices configuration from config file.
|
|
745
753
|
|
|
746
754
|
This allows hot-reloading of remote devices without restarting the application.
|
|
755
|
+
Handles ESPHome connections properly (stops old, starts new).
|
|
747
756
|
"""
|
|
748
757
|
config = self._config_helper.get_config()
|
|
749
758
|
remote_devices_config = config.get("remote_devices", [])
|
|
750
759
|
|
|
751
760
|
_LOGGER.info("Reloading remote devices configuration")
|
|
752
|
-
self.remote_devices.reload(remote_devices_config)
|
|
761
|
+
await self.remote_devices.reload(remote_devices_config)
|
|
753
762
|
_LOGGER.info("Remote devices configuration reloaded successfully")
|
|
754
763
|
|
|
755
764
|
async def publish_discovery(self) -> None:
|
boneio/core/manager/remote.py
CHANGED
|
@@ -18,6 +18,7 @@ from boneio.core.remote.base import (
|
|
|
18
18
|
)
|
|
19
19
|
from boneio.core.remote.mqtt import MQTTRemoteDevice
|
|
20
20
|
from boneio.core.remote.esphome import ESPHomeRemoteDevice, ESPHOME_API_AVAILABLE
|
|
21
|
+
from boneio.core.remote.wled import WLEDRemoteDevice
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
23
24
|
from boneio.core.messaging import MessageBus
|
|
@@ -176,6 +177,8 @@ class RemoteDeviceManager:
|
|
|
176
177
|
return None
|
|
177
178
|
elif protocol == RemoteDeviceProtocol.ESPHOME_API:
|
|
178
179
|
return self._create_esphome_device(device_id, name, config)
|
|
180
|
+
elif protocol == RemoteDeviceProtocol.WLED:
|
|
181
|
+
return self._create_wled_device(device_id, name, config)
|
|
179
182
|
else:
|
|
180
183
|
_LOGGER.error("Unsupported protocol '%s' for device '%s'", protocol, device_id)
|
|
181
184
|
return None
|
|
@@ -259,6 +262,40 @@ class RemoteDeviceManager:
|
|
|
259
262
|
covers=covers,
|
|
260
263
|
)
|
|
261
264
|
|
|
265
|
+
def _create_wled_device(
|
|
266
|
+
self,
|
|
267
|
+
device_id: str,
|
|
268
|
+
name: str,
|
|
269
|
+
config: dict[str, Any],
|
|
270
|
+
) -> WLEDRemoteDevice | None:
|
|
271
|
+
"""Create WLED remote device.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
device_id: Device ID
|
|
275
|
+
name: Device name
|
|
276
|
+
config: Full device configuration
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
WLEDRemoteDevice instance or None if creation failed
|
|
280
|
+
"""
|
|
281
|
+
wled_config = config.get("wled", {})
|
|
282
|
+
host = wled_config.get("host")
|
|
283
|
+
|
|
284
|
+
if not host:
|
|
285
|
+
_LOGGER.error("WLED device '%s' missing 'host' in wled config", device_id)
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
port = wled_config.get("port", 80)
|
|
289
|
+
segments = wled_config.get("segments", [])
|
|
290
|
+
|
|
291
|
+
return WLEDRemoteDevice(
|
|
292
|
+
id=device_id,
|
|
293
|
+
name=name,
|
|
294
|
+
host=host,
|
|
295
|
+
port=port,
|
|
296
|
+
segments=segments,
|
|
297
|
+
)
|
|
298
|
+
|
|
262
299
|
async def start_all_connections(self, delay_seconds: float = 10.0) -> None:
|
|
263
300
|
"""Start persistent connections for all ESPHome devices.
|
|
264
301
|
|
|
@@ -332,19 +369,32 @@ class RemoteDeviceManager:
|
|
|
332
369
|
output_id: str,
|
|
333
370
|
action: str,
|
|
334
371
|
brightness: int | None = None,
|
|
372
|
+
color_temp: int | None = None,
|
|
373
|
+
rgb: list[int] | tuple[int, int, int] | None = None,
|
|
335
374
|
transition: float | None = None,
|
|
375
|
+
effect: int | None = None,
|
|
376
|
+
palette: int | None = None,
|
|
377
|
+
effect_speed: int | None = None,
|
|
378
|
+
effect_intensity: int | None = None,
|
|
336
379
|
) -> bool:
|
|
337
|
-
"""Control output on remote device (BoneIO MQTT
|
|
380
|
+
"""Control output on remote device (BoneIO MQTT, ESPHome API, or WLED).
|
|
338
381
|
|
|
339
382
|
Automatically detects device protocol and routes to appropriate method.
|
|
340
383
|
For ESPHome devices, output_id can be a switch or light entity.
|
|
384
|
+
For WLED devices, output_id can be "main" or segment ID.
|
|
341
385
|
|
|
342
386
|
Args:
|
|
343
387
|
device_id: ID of the remote device
|
|
344
388
|
output_id: ID of the output/switch/light to control
|
|
345
389
|
action: Action to perform (ON, OFF, TOGGLE, BRIGHTNESS_UP, BRIGHTNESS_DOWN, SET_BRIGHTNESS)
|
|
346
|
-
brightness: Brightness level (0-255) -
|
|
347
|
-
|
|
390
|
+
brightness: Brightness level (0-255) - for ESPHome/WLED lights
|
|
391
|
+
color_temp: Color temperature in mireds - only for ESPHome lights
|
|
392
|
+
rgb: RGB color as [R, G, B] list or tuple (0-255 each) - for ESPHome/WLED lights
|
|
393
|
+
transition: Transition time in seconds - for ESPHome/WLED lights
|
|
394
|
+
effect: WLED effect ID
|
|
395
|
+
palette: WLED color palette ID
|
|
396
|
+
effect_speed: WLED effect speed (0-255)
|
|
397
|
+
effect_intensity: WLED effect intensity (0-255)
|
|
348
398
|
|
|
349
399
|
Returns:
|
|
350
400
|
True if command was sent successfully
|
|
@@ -359,10 +409,16 @@ class RemoteDeviceManager:
|
|
|
359
409
|
# Check if output_id is a light
|
|
360
410
|
if device.has_light(output_id):
|
|
361
411
|
_LOGGER.debug("Controlling ESPHome light '%s' on device '%s'", output_id, device_id)
|
|
412
|
+
# Convert rgb list to tuple[int, int, int] if needed
|
|
413
|
+
rgb_tuple: tuple[int, int, int] | None = None
|
|
414
|
+
if rgb and len(rgb) >= 3:
|
|
415
|
+
rgb_tuple = (int(rgb[0]), int(rgb[1]), int(rgb[2]))
|
|
362
416
|
return await device.control_light(
|
|
363
417
|
light_id=output_id,
|
|
364
418
|
action=action,
|
|
365
419
|
brightness=brightness,
|
|
420
|
+
color_temp=color_temp,
|
|
421
|
+
rgb=rgb_tuple,
|
|
366
422
|
transition=transition if transition is not None else 0.0,
|
|
367
423
|
)
|
|
368
424
|
# Otherwise treat as switch
|
|
@@ -372,6 +428,27 @@ class RemoteDeviceManager:
|
|
|
372
428
|
action=action,
|
|
373
429
|
)
|
|
374
430
|
|
|
431
|
+
# For WLED devices, use HTTP JSON API
|
|
432
|
+
if isinstance(device, WLEDRemoteDevice):
|
|
433
|
+
_LOGGER.debug("Controlling WLED '%s' segment '%s' on device '%s'", output_id, action, device_id)
|
|
434
|
+
# Parse segment_id - "main" means whole device, otherwise it's segment ID
|
|
435
|
+
segment_id = None if output_id == "main" else int(output_id)
|
|
436
|
+
# Convert rgb list to tuple if needed
|
|
437
|
+
wled_rgb: tuple[int, int, int] | None = None
|
|
438
|
+
if rgb and len(rgb) >= 3:
|
|
439
|
+
wled_rgb = (int(rgb[0]), int(rgb[1]), int(rgb[2]))
|
|
440
|
+
return await device.control_light(
|
|
441
|
+
segment_id=segment_id,
|
|
442
|
+
action=action,
|
|
443
|
+
brightness=brightness,
|
|
444
|
+
rgb=wled_rgb,
|
|
445
|
+
transition=transition if transition is not None else 0.0,
|
|
446
|
+
effect=effect,
|
|
447
|
+
palette=palette,
|
|
448
|
+
effect_speed=effect_speed,
|
|
449
|
+
effect_intensity=effect_intensity,
|
|
450
|
+
)
|
|
451
|
+
|
|
375
452
|
# For MQTT devices, use standard control_output
|
|
376
453
|
return await device.control_output(
|
|
377
454
|
output_id=output_id,
|
|
@@ -420,20 +497,52 @@ class RemoteDeviceManager:
|
|
|
420
497
|
**kwargs,
|
|
421
498
|
)
|
|
422
499
|
|
|
423
|
-
def reload(self, remote_devices_config: list[dict[str, Any]] | None = None) -> None:
|
|
500
|
+
async def reload(self, remote_devices_config: list[dict[str, Any]] | None = None) -> None:
|
|
424
501
|
"""Reload remote devices from config.
|
|
425
502
|
|
|
503
|
+
Stops existing ESPHome connections, reconfigures devices,
|
|
504
|
+
and starts new ESPHome connections.
|
|
505
|
+
|
|
426
506
|
Args:
|
|
427
507
|
remote_devices_config: New list of remote device configurations
|
|
428
508
|
"""
|
|
429
509
|
_LOGGER.info("Reloading remote devices configuration")
|
|
510
|
+
|
|
511
|
+
# Stop existing ESPHome connections first
|
|
512
|
+
await self.stop_all_connections()
|
|
513
|
+
|
|
430
514
|
self._devices.clear()
|
|
431
515
|
|
|
432
516
|
if remote_devices_config:
|
|
433
517
|
self._configure_devices(remote_devices_config)
|
|
434
518
|
|
|
519
|
+
# Start ESPHome connections immediately (no delay for reload)
|
|
520
|
+
await self._start_esphome_connections_immediate()
|
|
521
|
+
|
|
435
522
|
_LOGGER.info("Reloaded %d remote devices", len(self._devices))
|
|
436
523
|
|
|
524
|
+
async def _start_esphome_connections_immediate(self) -> None:
|
|
525
|
+
"""Start ESPHome connections immediately without delay.
|
|
526
|
+
|
|
527
|
+
Used during reload when we want connections to start right away.
|
|
528
|
+
"""
|
|
529
|
+
esphome_devices = [
|
|
530
|
+
(device_id, device)
|
|
531
|
+
for device_id, device in self._devices.items()
|
|
532
|
+
if isinstance(device, ESPHomeRemoteDevice)
|
|
533
|
+
]
|
|
534
|
+
|
|
535
|
+
if not esphome_devices:
|
|
536
|
+
return
|
|
537
|
+
|
|
538
|
+
_LOGGER.info("Starting %d ESPHome connection(s)...", len(esphome_devices))
|
|
539
|
+
for device_id, device in esphome_devices:
|
|
540
|
+
try:
|
|
541
|
+
await device.start_connection()
|
|
542
|
+
_LOGGER.info("Started connection for ESPHome device '%s'", device_id)
|
|
543
|
+
except Exception as e:
|
|
544
|
+
_LOGGER.error("Failed to start connection for ESPHome device '%s': %s", device_id, e)
|
|
545
|
+
|
|
437
546
|
def to_dict(self) -> dict[str, Any]:
|
|
438
547
|
"""Convert to dictionary representation.
|
|
439
548
|
|
boneio/core/remote/__init__.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Supports multiple protocols:
|
|
4
4
|
- MQTT: Standard MQTT communication (boneIO Black, ESPHome, etc.)
|
|
5
5
|
- ESPHome API: Native ESPHome API via TCP/IP
|
|
6
|
+
- WLED: WLED HTTP JSON API for LED strips
|
|
6
7
|
- CAN: CAN bus communication (future)
|
|
7
8
|
- Loxone: Loxone Miniserver integration (future)
|
|
8
9
|
"""
|
|
@@ -16,6 +17,11 @@ from boneio.core.remote.esphome import (
|
|
|
16
17
|
ESPHOME_API_AVAILABLE,
|
|
17
18
|
ZEROCONF_AVAILABLE,
|
|
18
19
|
)
|
|
20
|
+
from boneio.core.remote.wled import (
|
|
21
|
+
WLEDRemoteDevice,
|
|
22
|
+
scan_wled_devices,
|
|
23
|
+
discover_wled_info,
|
|
24
|
+
)
|
|
19
25
|
|
|
20
26
|
__all__ = [
|
|
21
27
|
"RemoteDevice",
|
|
@@ -26,4 +32,7 @@ __all__ = [
|
|
|
26
32
|
"scan_esphome_devices",
|
|
27
33
|
"ESPHOME_API_AVAILABLE",
|
|
28
34
|
"ZEROCONF_AVAILABLE",
|
|
35
|
+
"WLEDRemoteDevice",
|
|
36
|
+
"scan_wled_devices",
|
|
37
|
+
"discover_wled_info",
|
|
29
38
|
]
|
boneio/core/remote/base.py
CHANGED
|
@@ -25,6 +25,7 @@ class RemoteDeviceProtocol(str, Enum):
|
|
|
25
25
|
LOXONE = "loxone"
|
|
26
26
|
ESPHOME_UDP = "esphome_udp"
|
|
27
27
|
ESPHOME_API = "esphome_api"
|
|
28
|
+
WLED = "wled"
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class RemoteDeviceType(str, Enum):
|
|
@@ -33,6 +34,7 @@ class RemoteDeviceType(str, Enum):
|
|
|
33
34
|
BONEIO_BLACK = "boneio_black"
|
|
34
35
|
ESPHOME = "esphome"
|
|
35
36
|
LOXONE_MINISERVER = "loxone_miniserver"
|
|
37
|
+
WLED = "wled"
|
|
36
38
|
GENERIC = "generic"
|
|
37
39
|
|
|
38
40
|
|