boneIO 1.5.0.dev7__py3-none-any.whl → 1.5.0.dev9__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/config/config_helper.py +57 -19
- boneio/core/discovery.py +2 -2
- boneio/core/manager/inputs.py +13 -4
- boneio/core/manager/irrigation.py +1 -1
- boneio/core/manager/manager.py +5 -5
- boneio/core/manager/update.py +1 -1
- boneio/core/messaging/mqtt.py +1 -1
- boneio/core/remote/wled.py +9 -1
- boneio/integration/homeassistant.py +9 -9
- boneio/integration/lox_template.py +2 -2
- boneio/modbus/devices/sensors/boneio-edge-temp.json +1 -1
- boneio/modbus/entities/base.py +1 -1
- boneio/runner.py +2 -1
- boneio/schema/schema.yaml +13 -0
- boneio/version.py +1 -1
- boneio/webui/frontend-dist/assets/BindingMatrix-CFdSjHx2.js +1 -0
- boneio/webui/frontend-dist/assets/ConfigEditor-7BXEIoby.js +1 -0
- boneio/webui/frontend-dist/assets/EditItemDialog-KRY9Nr7Q.js +66 -0
- boneio/webui/frontend-dist/assets/EntityCard-C_zsxPTw.js +1 -0
- boneio/webui/frontend-dist/assets/EntityGrid-uCBLaMe9.js +1 -0
- boneio/webui/frontend-dist/assets/HelpView--09VEVQt.js +1 -0
- boneio/webui/frontend-dist/assets/InputsView-C5SMvXaD.js +1 -0
- boneio/webui/frontend-dist/assets/LogViewer-Ejk-hBZk.js +2 -0
- boneio/webui/frontend-dist/assets/LongPressWrapper-B-jkbdNk.js +1 -0
- boneio/webui/frontend-dist/assets/ModbusView-DkjmrV7V.js +1 -0
- boneio/webui/frontend-dist/assets/NodeRedView-BCnN6hPl.js +1 -0
- boneio/webui/frontend-dist/assets/OutputsView-DqoaDXdb.js +1 -0
- boneio/webui/frontend-dist/assets/SearchableEntityPicker-AqpMXiW9.js +11 -0
- boneio/webui/frontend-dist/assets/SensorView-DrMXFCl_.js +1 -0
- boneio/webui/frontend-dist/assets/Sparkline-P3-zEZpz.js +10 -0
- boneio/webui/frontend-dist/assets/SudoPasswordDialog-D5dvXKgf.js +1 -0
- boneio/webui/frontend-dist/assets/SystemState-CvUUFRfw.js +1 -0
- boneio/webui/frontend-dist/assets/TemplatesView-CEJWenj2.js +1 -0
- boneio/webui/frontend-dist/assets/Tools-DMbCkTx_.js +14 -0
- boneio/webui/frontend-dist/assets/UISettings-KeMgW0rB.js +38 -0
- boneio/webui/frontend-dist/assets/aiConfig-IRYCBmLm.js +2 -0
- boneio/webui/frontend-dist/assets/aiWizardPrompt-D9OJ-cE7.js +2 -0
- boneio/webui/frontend-dist/assets/basePath-CxHj-SSK.js +1 -0
- boneio/webui/frontend-dist/assets/clipboard-CCtx29e-.js +1 -0
- boneio/webui/frontend-dist/assets/dialog-BTaKr79l.js +1 -0
- boneio/webui/frontend-dist/assets/formatters-b1QAvEOT.js +1 -0
- boneio/webui/frontend-dist/assets/gi-BamvZSOL.js +1 -0
- boneio/webui/frontend-dist/assets/index-4kW8M4Zc.css +2 -0
- boneio/webui/frontend-dist/assets/index-CQtJKcCO.js +2 -0
- boneio/webui/frontend-dist/assets/modbusDeviceCatalog-By-WNoAq.js +1 -0
- boneio/webui/frontend-dist/assets/select-2z0PCnkc.js +1 -0
- boneio/webui/frontend-dist/assets/tabs-box-DFRSG6Sv.js +1 -0
- boneio/webui/frontend-dist/assets/useTranslation-CMe3OXqd.js +13 -0
- boneio/webui/frontend-dist/assets/utils-CqpCEpSN.js +1 -0
- boneio/webui/frontend-dist/assets/vendor-DjFmndpd.js +11 -0
- boneio/webui/frontend-dist/index.html +5 -3
- boneio/webui/frontend-dist/schema/boneio.schema.json +8 -0
- boneio/webui/frontend-dist/schema/config.schema.json +8 -0
- boneio/webui/frontend-dist/sw.js +1 -1
- boneio/webui/middleware/auth.py +1 -0
- boneio/webui/routes/config.py +342 -7
- boneio/webui/routes/dev_fake_device.py +2 -2
- boneio/webui/routes/modbus.py +2 -2
- boneio/webui/routes/sensors.py +41 -0
- boneio/webui/routes/system.py +60 -3
- boneio/webui/schema/boneio.schema.json +8 -0
- boneio/webui/schema/config.schema.json +8 -0
- {boneio-1.5.0.dev7.dist-info → boneio-1.5.0.dev9.dist-info}/METADATA +1 -1
- {boneio-1.5.0.dev7.dist-info → boneio-1.5.0.dev9.dist-info}/RECORD +68 -38
- boneio/webui/frontend-dist/assets/ConfigEditor-DbZgmkmq.js +0 -1
- boneio/webui/frontend-dist/assets/aiWizardPrompt-BubjUKaK.js +0 -2
- boneio/webui/frontend-dist/assets/index-D6g-vdBv.css +0 -2
- boneio/webui/frontend-dist/assets/index-qx3gjAAI.js +0 -130
- boneio/webui/frontend-dist/assets/vendor-pmGLHP0_.js +0 -55
- {boneio-1.5.0.dev7.dist-info → boneio-1.5.0.dev9.dist-info}/WHEEL +0 -0
- {boneio-1.5.0.dev7.dist-info → boneio-1.5.0.dev9.dist-info}/entry_points.txt +0 -0
- {boneio-1.5.0.dev7.dist-info → boneio-1.5.0.dev9.dist-info}/licenses/LICENSE +0 -0
- {boneio-1.5.0.dev7.dist-info → boneio-1.5.0.dev9.dist-info}/top_level.txt +0 -0
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
import logging
|
|
7
7
|
from _collections_abc import dict_values
|
|
8
8
|
from typing import TYPE_CHECKING, Any
|
|
9
|
+
import time as _time
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
from boneio.integration.homeassistant import HomeAssistantDiscoveryMessage
|
|
@@ -28,7 +29,6 @@ from boneio.const import (
|
|
|
28
29
|
VALVE,
|
|
29
30
|
)
|
|
30
31
|
from boneio.core.system import get_serial_from_mac
|
|
31
|
-
from boneio.core.utils.util import sanitize_mqtt_topic
|
|
32
32
|
|
|
33
33
|
_LOGGER = logging.getLogger(__name__)
|
|
34
34
|
|
|
@@ -53,21 +53,29 @@ class ConfigHelper:
|
|
|
53
53
|
pwa_name: str | None = None,
|
|
54
54
|
ha_child_devices: bool = False,
|
|
55
55
|
ha_child_devices_naming: str = "default",
|
|
56
|
+
serial_override: str | None = None,
|
|
56
57
|
):
|
|
57
58
|
self._name = name
|
|
58
59
|
self._version = version
|
|
59
60
|
|
|
60
|
-
# Generate serial number from MAC - always required for
|
|
61
|
-
self.
|
|
61
|
+
# Generate real serial number from MAC - always required for physical identification
|
|
62
|
+
self._real_serial = get_serial_from_mac(network_info or {})
|
|
62
63
|
|
|
63
|
-
#
|
|
64
|
+
# If override is provided, use it as effective serial, otherwise use real MAC-based serial
|
|
65
|
+
if serial_override:
|
|
66
|
+
self._serial_no = serial_override
|
|
67
|
+
_LOGGER.warning("Serial override active: effective=%s, real=%s", serial_override, self._real_serial)
|
|
68
|
+
else:
|
|
69
|
+
self._serial_no = self._real_serial
|
|
70
|
+
|
|
71
|
+
# Build fixed topic prefix: boneio/blk_{serial} using effective serial
|
|
64
72
|
# This is no longer configurable - always uses this format
|
|
65
73
|
if self._serial_no:
|
|
66
74
|
self._topic_prefix = f"boneio/{self._serial_no}"
|
|
67
75
|
else:
|
|
68
76
|
# Fallback if MAC not available (should rarely happen)
|
|
69
77
|
self._topic_prefix = "boneio/blk_unknown"
|
|
70
|
-
_LOGGER.warning("Could not determine serial number
|
|
78
|
+
_LOGGER.warning("Could not determine serial number, using fallback topic prefix")
|
|
71
79
|
|
|
72
80
|
# PWA short name for Android home screen (max 12 chars)
|
|
73
81
|
if pwa_name:
|
|
@@ -165,7 +173,8 @@ class ConfigHelper:
|
|
|
165
173
|
|
|
166
174
|
@property
|
|
167
175
|
def serial_number(self) -> str:
|
|
168
|
-
|
|
176
|
+
"""Get effective device serial number (e.g., 'blk_abc123')."""
|
|
177
|
+
return self._serial_no or "blk_unknown"
|
|
169
178
|
|
|
170
179
|
@property
|
|
171
180
|
def proxy_port(self) -> int | None:
|
|
@@ -186,9 +195,16 @@ class ConfigHelper:
|
|
|
186
195
|
return self._topic_prefix
|
|
187
196
|
|
|
188
197
|
@property
|
|
189
|
-
def
|
|
190
|
-
"""
|
|
191
|
-
return self.
|
|
198
|
+
def real_serial(self) -> str:
|
|
199
|
+
"""Real serial from MAC — for HA device_info and UI display."""
|
|
200
|
+
return self._real_serial or "blk_unknown"
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def serial_override(self) -> str | None:
|
|
204
|
+
"""Return override value if active, None otherwise."""
|
|
205
|
+
if self._serial_no != self._real_serial:
|
|
206
|
+
return self._serial_no
|
|
207
|
+
return None
|
|
192
208
|
|
|
193
209
|
@property
|
|
194
210
|
def pwa_name(self) -> str:
|
|
@@ -390,16 +406,20 @@ class ConfigHelper:
|
|
|
390
406
|
return self._config_cache
|
|
391
407
|
|
|
392
408
|
def reload_config(self) -> dict[str, Any]:
|
|
393
|
-
"""Reload configuration from file
|
|
409
|
+
"""Reload configuration from YAML file (fast path for hot-reload).
|
|
394
410
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
(~
|
|
398
|
-
cache rebuild — it's independent.
|
|
411
|
+
Uses load_yaml_file() + merge_board_config() to read the config
|
|
412
|
+
directly from YAML without running Cerberus validation. This is
|
|
413
|
+
much faster (~0.5-1s) than the full validation path (~20s on BB).
|
|
399
414
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
415
|
+
Full Cerberus validation is only needed at:
|
|
416
|
+
- Application startup (via load_config_from_file)
|
|
417
|
+
- Background disk cache rebuild (debounced after config changes)
|
|
418
|
+
- Monaco YAML editor validation (load_config_from_string)
|
|
419
|
+
|
|
420
|
+
NOTE: Migrations are not applied here — they run at startup and
|
|
421
|
+
are idempotent (version-gated). Config saved via the UI always
|
|
422
|
+
has current schema version.
|
|
403
423
|
|
|
404
424
|
Returns:
|
|
405
425
|
dict: Reloaded configuration dictionary
|
|
@@ -407,8 +427,26 @@ class ConfigHelper:
|
|
|
407
427
|
Raises:
|
|
408
428
|
ValueError: If config_file_path is not set
|
|
409
429
|
"""
|
|
410
|
-
|
|
411
|
-
|
|
430
|
+
|
|
431
|
+
if self._config_file_path is None:
|
|
432
|
+
raise ValueError("config_file_path not set in ConfigHelper")
|
|
433
|
+
|
|
434
|
+
_t0 = _time.monotonic()
|
|
435
|
+
_LOGGER.info("Fast-reloading config from: %s", self._config_file_path)
|
|
436
|
+
|
|
437
|
+
from boneio.core.config.yaml_util import load_yaml_file, merge_board_config
|
|
438
|
+
|
|
439
|
+
config_yaml = load_yaml_file(self._config_file_path)
|
|
440
|
+
if config_yaml is None:
|
|
441
|
+
raise ValueError(f"Failed to load config from: {self._config_file_path}")
|
|
442
|
+
|
|
443
|
+
merged = merge_board_config(config_yaml)
|
|
444
|
+
self._config_cache = merged
|
|
445
|
+
|
|
446
|
+
elapsed = _time.monotonic() - _t0
|
|
447
|
+
_LOGGER.info("Fast config reload completed in %.2fs", elapsed)
|
|
448
|
+
|
|
449
|
+
return merged
|
|
412
450
|
|
|
413
451
|
def get_section(self, section_name: str, force_reload: bool = False) -> Any:
|
|
414
452
|
"""Get a specific configuration section.
|
boneio/core/discovery.py
CHANGED
|
@@ -62,9 +62,9 @@ class BlackDiscoveryPublisher:
|
|
|
62
62
|
|
|
63
63
|
config_helper = self._manager._config_helper
|
|
64
64
|
device_info = {
|
|
65
|
-
"id": config_helper.
|
|
65
|
+
"id": config_helper.serial_number,
|
|
66
66
|
"name": config_helper.name,
|
|
67
|
-
"serial_number": config_helper.
|
|
67
|
+
"serial_number": config_helper.serial_number,
|
|
68
68
|
"type": "black",
|
|
69
69
|
"firmware": __version__,
|
|
70
70
|
"topic_prefix": config_helper.topic_prefix,
|
boneio/core/manager/inputs.py
CHANGED
|
@@ -255,13 +255,17 @@ class InputManager:
|
|
|
255
255
|
|
|
256
256
|
# Reload: update existing input's actions and name
|
|
257
257
|
if existing_input:
|
|
258
|
-
# Check if HA-relevant fields changed (name, area, mqtt_sequences)
|
|
258
|
+
# Check if HA-relevant fields changed (name, area, device_class, mqtt_sequences)
|
|
259
259
|
old_name = existing_input._name if hasattr(existing_input, "_name") else None
|
|
260
260
|
old_area = getattr(existing_input, "area", None)
|
|
261
|
+
old_device_class = getattr(existing_input, "_device_class", None)
|
|
262
|
+
new_device_class = gpio.get(DEVICE_CLASS)
|
|
261
263
|
old_mqtt_sequences = existing_input.mqtt_sequences if hasattr(existing_input, "mqtt_sequences") else {}
|
|
262
264
|
new_mqtt_sequences = gpio.get("mqtt_sequences", {})
|
|
263
265
|
ha_fields_changed = (
|
|
264
|
-
(old_name != name) or (old_area != area)
|
|
266
|
+
(old_name != name) or (old_area != area)
|
|
267
|
+
or (old_device_class != new_device_class)
|
|
268
|
+
or (old_mqtt_sequences != new_mqtt_sequences)
|
|
265
269
|
)
|
|
266
270
|
|
|
267
271
|
# Update actions (always - this is internal to the controller)
|
|
@@ -383,10 +387,15 @@ class InputManager:
|
|
|
383
387
|
|
|
384
388
|
# Reload: update existing input's actions and name
|
|
385
389
|
if existing_input:
|
|
386
|
-
# Check if HA-relevant fields changed (name, area)
|
|
390
|
+
# Check if HA-relevant fields changed (name, area, device_class)
|
|
387
391
|
old_name = existing_input._name if hasattr(existing_input, "_name") else None
|
|
388
392
|
old_area = getattr(existing_input, "area", None)
|
|
389
|
-
|
|
393
|
+
old_device_class = getattr(existing_input, "_device_class", None)
|
|
394
|
+
new_device_class = gpio.get(DEVICE_CLASS)
|
|
395
|
+
ha_fields_changed = (
|
|
396
|
+
(old_name != name) or (old_area != area)
|
|
397
|
+
or (old_device_class != new_device_class)
|
|
398
|
+
)
|
|
390
399
|
|
|
391
400
|
# Update actions (always - this is internal to the controller)
|
|
392
401
|
existing_input.set_actions(actions=actions)
|
|
@@ -309,7 +309,7 @@ class IrrigationManager:
|
|
|
309
309
|
Sends empty payload to discovery topics to remove entities from HA.
|
|
310
310
|
"""
|
|
311
311
|
cfg = self._manager.config_helper
|
|
312
|
-
serial = cfg.
|
|
312
|
+
serial = cfg.serial_number
|
|
313
313
|
|
|
314
314
|
# Build list of discovery IDs to remove
|
|
315
315
|
discovery_ids: list[tuple[str, str]] = [
|
boneio/core/manager/manager.py
CHANGED
|
@@ -243,7 +243,7 @@ class Manager:
|
|
|
243
243
|
self.remote_devices = RemoteDeviceManager(
|
|
244
244
|
message_bus=message_bus,
|
|
245
245
|
remote_devices_config=remote_devices,
|
|
246
|
-
own_serial=config_helper.
|
|
246
|
+
own_serial=config_helper.serial_number,
|
|
247
247
|
name=self.config_helper.name,
|
|
248
248
|
)
|
|
249
249
|
|
|
@@ -625,7 +625,7 @@ class Manager:
|
|
|
625
625
|
ha_type: Home Assistant entity type (sensor, light, cover …).
|
|
626
626
|
payload: Ready-to-publish discovery payload dict.
|
|
627
627
|
"""
|
|
628
|
-
topic = f"{self._config_helper.ha_discovery_prefix}/{ha_type}/{self._config_helper.
|
|
628
|
+
topic = f"{self._config_helper.ha_discovery_prefix}/{ha_type}/{self._config_helper.serial_number}/{id}/config"
|
|
629
629
|
_LOGGER.debug("Sending HA discovery for %s entity %s.", ha_type, id)
|
|
630
630
|
self._config_helper.add_autodiscovery_msg(topic=topic, ha_type=ha_type, payload=payload)
|
|
631
631
|
self.send_message(topic=topic, payload=payload, retain=True)
|
|
@@ -1590,9 +1590,9 @@ class Manager:
|
|
|
1590
1590
|
_LOGGER.info("Starting config reload")
|
|
1591
1591
|
|
|
1592
1592
|
# Reload config cache in ConfigHelper
|
|
1593
|
-
#
|
|
1594
|
-
# full Cerberus validation (~20s)
|
|
1595
|
-
#
|
|
1593
|
+
# Uses fast path: load_yaml_file() + merge_board_config() (~0.5-1s)
|
|
1594
|
+
# instead of full Cerberus validation (~20s). Run in thread executor
|
|
1595
|
+
# because it still does file I/O (reading YAML files from disk).
|
|
1596
1596
|
try:
|
|
1597
1597
|
config = await asyncio.to_thread(self._config_helper.reload_config)
|
|
1598
1598
|
# Update areas mapping from reloaded config
|
boneio/core/manager/update.py
CHANGED
|
@@ -737,7 +737,7 @@ class UpdateManager(AsyncUpdater):
|
|
|
737
737
|
# --- Remove legacy event entity discovery (if any) ---
|
|
738
738
|
old_event_topic = (
|
|
739
739
|
f"{config.ha_discovery_prefix}/event/"
|
|
740
|
-
f"{config.
|
|
740
|
+
f"{config.serial_number}/migration_alert/config"
|
|
741
741
|
)
|
|
742
742
|
self._manager.send_message(topic=old_event_topic, payload="", retain=True)
|
|
743
743
|
_LOGGER.debug("Removed legacy migration_alert event entity discovery")
|
boneio/core/messaging/mqtt.py
CHANGED
|
@@ -59,7 +59,7 @@ class MQTTClient(MessageBus):
|
|
|
59
59
|
self._mqtt_energy_listeners: dict[str, Callable[[str, str], Awaitable[None]]] = {}
|
|
60
60
|
self._discovery_topics = (
|
|
61
61
|
[
|
|
62
|
-
f"{self._config_helper.ha_discovery_prefix}/{ha_type}/{self._config_helper.
|
|
62
|
+
f"{self._config_helper.ha_discovery_prefix}/{ha_type}/{self._config_helper.serial_number}/#"
|
|
63
63
|
for ha_type in self._config_helper.ha_types
|
|
64
64
|
]
|
|
65
65
|
if self._config_helper.ha_discovery
|
boneio/core/remote/wled.py
CHANGED
|
@@ -288,10 +288,18 @@ class WLEDRemoteDevice(RemoteDevice):
|
|
|
288
288
|
|
|
289
289
|
if action == "ON":
|
|
290
290
|
seg_state["on"] = True
|
|
291
|
+
# Ensure device is globally on so the segment is visible
|
|
292
|
+
state["on"] = True
|
|
291
293
|
elif action == "OFF":
|
|
292
294
|
seg_state["on"] = False
|
|
293
295
|
elif action == "TOGGLE":
|
|
294
|
-
|
|
296
|
+
# WLED ignores "on": "t" at segment level (tested on
|
|
297
|
+
# v0.15.0-b4) — resolve toggle using cached state.
|
|
298
|
+
current_on = self.segment_is_on(segment_id)
|
|
299
|
+
seg_state["on"] = not current_on
|
|
300
|
+
if not current_on:
|
|
301
|
+
# Turning on — ensure device is globally on
|
|
302
|
+
state["on"] = True
|
|
295
303
|
|
|
296
304
|
if brightness is not None:
|
|
297
305
|
seg_state["bri"] = max(0, min(255, brightness))
|
|
@@ -123,9 +123,9 @@ def ha_availabilty_message(
|
|
|
123
123
|
"identifiers": [child_identifier],
|
|
124
124
|
"manufacturer": manufacturer,
|
|
125
125
|
"model": model,
|
|
126
|
-
"model_id": config_helper.
|
|
126
|
+
"model_id": config_helper.real_serial,
|
|
127
127
|
"name": child_device_name,
|
|
128
|
-
"serial_number": config_helper.
|
|
128
|
+
"serial_number": config_helper.real_serial,
|
|
129
129
|
"sw_version": __version__,
|
|
130
130
|
"via_device": topic, # Link to main BoneIO device
|
|
131
131
|
**web_url_dict,
|
|
@@ -144,9 +144,9 @@ def ha_availabilty_message(
|
|
|
144
144
|
"identifiers": [f"{topic}_{area}"], # Use area ID for consistent grouping
|
|
145
145
|
"manufacturer": manufacturer,
|
|
146
146
|
"model": model,
|
|
147
|
-
"model_id": config_helper.
|
|
147
|
+
"model_id": config_helper.real_serial,
|
|
148
148
|
"name": sub_device_name,
|
|
149
|
-
"serial_number": config_helper.
|
|
149
|
+
"serial_number": config_helper.real_serial,
|
|
150
150
|
"sw_version": __version__,
|
|
151
151
|
"via_device": topic, # Link to main BoneIO device
|
|
152
152
|
"suggested_area": area_name, # Use area ID (lowercase) - HA converts area names to lowercase
|
|
@@ -158,9 +158,9 @@ def ha_availabilty_message(
|
|
|
158
158
|
"identifiers": [topic],
|
|
159
159
|
"manufacturer": manufacturer,
|
|
160
160
|
"model": model,
|
|
161
|
-
"model_id": config_helper.
|
|
161
|
+
"model_id": config_helper.real_serial,
|
|
162
162
|
"name": device_name,
|
|
163
|
-
"serial_number": config_helper.
|
|
163
|
+
"serial_number": config_helper.real_serial,
|
|
164
164
|
"sw_version": __version__,
|
|
165
165
|
**web_url_dict
|
|
166
166
|
}
|
|
@@ -297,9 +297,9 @@ def _ha_irrigation_device(
|
|
|
297
297
|
"identifiers": [f"{topic}_{IRRIGATION}_{ctrl_id}"],
|
|
298
298
|
"manufacturer": "boneIO",
|
|
299
299
|
"model": model,
|
|
300
|
-
"model_id": config_helper.
|
|
300
|
+
"model_id": config_helper.real_serial,
|
|
301
301
|
"name": ctrl_name,
|
|
302
|
-
"serial_number": config_helper.
|
|
302
|
+
"serial_number": config_helper.real_serial,
|
|
303
303
|
"sw_version": __version__,
|
|
304
304
|
"via_device": topic,
|
|
305
305
|
}
|
|
@@ -880,7 +880,7 @@ def modbus_polling_switch_message(
|
|
|
880
880
|
|
|
881
881
|
discovery_topic = (
|
|
882
882
|
f"{config_helper.ha_discovery_prefix}/switch/"
|
|
883
|
-
f"{config_helper.
|
|
883
|
+
f"{config_helper.serial_number}/{entity_id}/config"
|
|
884
884
|
)
|
|
885
885
|
|
|
886
886
|
area_name = config_helper.get_area_name(area) if area else None
|
|
@@ -52,7 +52,7 @@ def generate_lox_template(manager: Manager) -> str:
|
|
|
52
52
|
Returns:
|
|
53
53
|
XML string ready to be saved as a .xml template file.
|
|
54
54
|
"""
|
|
55
|
-
serial = manager.config_helper.
|
|
55
|
+
serial = manager.config_helper.serial_number or "boneio"
|
|
56
56
|
device_name = manager.config_helper.name or serial
|
|
57
57
|
|
|
58
58
|
# Get Lox UDP config (host/ports) from running config
|
|
@@ -275,7 +275,7 @@ def generate_lox_summary(manager: Manager) -> dict[str, Any]:
|
|
|
275
275
|
Returns:
|
|
276
276
|
Dictionary with 'commands' and 'status' sections.
|
|
277
277
|
"""
|
|
278
|
-
serial = manager.config_helper.
|
|
278
|
+
serial = manager.config_helper.serial_number or "boneio"
|
|
279
279
|
device_name = manager.config_helper.name or serial
|
|
280
280
|
|
|
281
281
|
commands: list[dict[str, str]] = []
|
boneio/modbus/entities/base.py
CHANGED
|
@@ -60,7 +60,7 @@ class BaseEntity(Filter):
|
|
|
60
60
|
self._timestamp = time.time()
|
|
61
61
|
self._id = f"{self._parent[ID]}_{self._decoded_name_low.replace('_', '')}"
|
|
62
62
|
self._topic = (
|
|
63
|
-
f"{self._config_helper.ha_discovery_prefix}/{self._entity_type}/{self._config_helper.
|
|
63
|
+
f"{self._config_helper.ha_discovery_prefix}/{self._entity_type}/{self._config_helper.serial_number}"
|
|
64
64
|
f"/{self._id}/config"
|
|
65
65
|
)
|
|
66
66
|
|
boneio/runner.py
CHANGED
|
@@ -170,6 +170,7 @@ async def async_run(
|
|
|
170
170
|
pwa_name=web_config.get("cloud", {}).get("pwa_name"),
|
|
171
171
|
ha_child_devices=main_config.get("ha_child_devices", False),
|
|
172
172
|
ha_child_devices_naming=main_config.get("ha_child_devices_naming", "default"),
|
|
173
|
+
serial_override=main_config.get("serial_override"),
|
|
173
174
|
)
|
|
174
175
|
|
|
175
176
|
# Load areas configuration
|
|
@@ -358,7 +359,7 @@ async def async_run(
|
|
|
358
359
|
cloud_reg = None
|
|
359
360
|
if _config_helper.cloud_registration:
|
|
360
361
|
local_ip = network_state.get("ip", "")
|
|
361
|
-
serial = _config_helper.
|
|
362
|
+
serial = _config_helper.serial_number
|
|
362
363
|
if local_ip and serial:
|
|
363
364
|
cloud_reg = CloudRegistration(
|
|
364
365
|
serial_number=serial,
|
boneio/schema/schema.yaml
CHANGED
|
@@ -2255,3 +2255,16 @@ boneio:
|
|
|
2255
2255
|
- default: Entity name only (e.g., "OUT 17"). May cause duplicates if multiple boneIO devices share output names.
|
|
2256
2256
|
- device_name: Adds boneIO device name prefix (e.g., "boneIO Black - OUT 17").
|
|
2257
2257
|
- device_name_area: Adds device name and area (e.g., "boneIO Black - Living Room - OUT 17"). Falls back to device_name if no area is assigned.
|
|
2258
|
+
serial_override:
|
|
2259
|
+
required: False
|
|
2260
|
+
type: string
|
|
2261
|
+
nullable: True
|
|
2262
|
+
regex: '^blk[a-f0-9]{6}$'
|
|
2263
|
+
meta:
|
|
2264
|
+
label: Serial number override
|
|
2265
|
+
description: |
|
|
2266
|
+
Override the auto-detected serial number (based on MAC address).
|
|
2267
|
+
Use this when restoring config from a different device (e.g., RMA replacement).
|
|
2268
|
+
Format: blk followed by 6 hex characters (e.g., blk8c7df0).
|
|
2269
|
+
WARNING: Two devices with the same serial on the same MQTT broker will conflict!
|
|
2270
|
+
|
boneio/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# flake8: noqa
|
|
2
|
-
__version__ = "1.5.
|
|
2
|
+
__version__ = "1.5.0dev9"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{c as t}from"./monaco-61yH0vfd.js";import{y as n}from"./vendor-DjFmndpd.js";import{t as r}from"./useTranslation-CMe3OXqd.js";import{$ as i,B as a,C as o,I as s,It as c,N as l,Pt as u,U as d,Ut as f,Wt as p,b as m,s as h,ut as g,y as _,z as v}from"./index-CQtJKcCO.js";import{t as y}from"./EditItemDialog-KRY9Nr7Q.js";var b=e(t(),1);function x(e){return p({tag:`svg`,attr:{viewBox:`0 0 512 512`},child:[{tag:`path`,attr:{d:`M224 0c17.7 0 32 14.3 32 32l0 12 96-12c17.7 0 32 14.3 32 32s-14.3 32-32 32L256 84l-31-3.9-1-.1-1 .1L192 84 96 96C78.3 96 64 81.7 64 64s14.3-32 32-32l96 12 0-12c0-17.7 14.3-32 32-32zM0 224c0-17.7 14.3-32 32-32l96 0 22.6-22.6c6-6 14.1-9.4 22.6-9.4l18.7 0 0-43.8 32-4 32 4 0 43.8 18.7 0c8.5 0 16.6 3.4 22.6 9.4L320 192l32 0c88.4 0 160 71.6 160 160c0 17.7-14.3 32-32 32l-64 0c-17.7 0-32-14.3-32-32s-14.3-32-32-32l-36.1 0c-20.2 29-53.9 48-91.9 48s-71.7-19-91.9-48L32 320c-17.7 0-32-14.3-32-32l0-64zM436.8 423.4c1.9-4.5 6.3-7.4 11.2-7.4s9.2 2.9 11.2 7.4l18.2 42.4c1.8 4.1 2.7 8.6 2.7 13.1l0 1.2c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-1.2c0-4.5 .9-8.9 2.7-13.1l18.2-42.4z`},child:[]}]})(e)}var S=n();function C({outputType:e,className:t}){switch(e){case`light`:return(0,S.jsx)(i,{className:h(`text-warning`,t)});case`switch`:return(0,S.jsx)(c,{className:h(`text-info`,t)});case`valve`:return(0,S.jsx)(x,{className:h(`text-accent`,t)});case`cover`:return(0,S.jsx)(l,{className:h(`text-secondary`,t)});case`shutter`:case`blind`:case`curtain`:return(0,S.jsx)(f,{className:h(`text-secondary`,t)});default:return null}}var w={single:`1×`,double:`2×`,triple:`3×`,long:`L`,pressed:`P`,released:`R`,double_then_long:`2×L`,single_then_long:`1×L`,double_then_single:`2×1`},T={TOGGLE:`short_toggle`,ON:`short_on`,OFF:`short_off`,OPEN:`short_open`,CLOSE:`short_close`,STOP:`short_stop`},E=[{key:`single`,labelKey:`binding_matrix.legend_single`,icon:`1×`},{key:`double`,labelKey:`binding_matrix.legend_double`,icon:`2×`},{key:`triple`,labelKey:`binding_matrix.legend_triple`,icon:`3×`},{key:`long`,labelKey:`binding_matrix.legend_long`,icon:`L`},{key:`pressed`,labelKey:`binding_matrix.legend_pressed`,icon:`P`},{key:`released`,labelKey:`binding_matrix.legend_released`,icon:`R`}],D=[{labelKey:`actions.toggle`,shortKey:`actions.short_toggle`},{labelKey:`actions.on`,shortKey:`actions.short_on`},{labelKey:`actions.off`,shortKey:`actions.short_off`},{labelKey:`actions.open`,shortKey:`actions.short_open`},{labelKey:`actions.close`,shortKey:`actions.short_close`},{labelKey:`actions.stop`,shortKey:`actions.short_stop`}],O={single:`single`,double:`double`,triple:`triple`,long:`long`,pressed:`basic`,released:`basic`,double_then_long:`sequences`,single_then_long:`sequences`,double_then_single:`sequences`};function k(e,t){let n=w[e.clickType]||e.clickType,r=T[e.actionValue];return`${n} ${r?t(`actions.${r}`):e.actionValue}`}function A(e,t){let n=`actions.${e.actionValue.toLowerCase()}`,r=t(n),i=r===n?e.actionValue:r;return`${e.clickType} → ${i}`}function j(e){let t=e.action||`output`,n=``,r=``,i;switch(t){case`output`:n=e.boneio_output||``,r=e.action_output||`TOGGLE`;break;case`cover`:n=e.boneio_cover||``,r=e.action_cover||`TOGGLE`;break;case`remote_output`:i=e.boneio_id||``,n=i?`${i}/${e.output_id||``}`:e.output_id||``,r=e.action_output||`TOGGLE`;break;case`remote_cover`:i=e.boneio_id||``,n=i?`${i}/${e.cover_id||``}`:e.cover_id||``,r=e.action_cover||`TOGGLE`;break;default:n=``,r=e.action_output||e.action_cover||``;break}return{targetId:n,actionType:t,actionValue:r,remoteDevice:i}}function M(e){let t=[],n=[`single`,`double`,`triple`,`long`,`pressed`,`released`,`double_then_long`,`single_then_long`,`double_then_single`],r=e.local_inputs||[];for(let e=0;e<r.length;e++){let i=r[e],a=i.id||i.pin||`local_${e}`,o=[];for(let e of n){let t=i.actions?.[e]||[];for(let n of t){let{targetId:t,actionType:r,actionValue:i,remoteDevice:a}=j(n);t&&o.push({clickType:e,actionType:r,targetId:t,actionValue:i,remoteDevice:a})}}t.push({id:a,name:i.name||i.id||i.pin||a,area:i.area||void 0,type:`local`,bindings:o})}let i=e.remote_inputs||[];for(let e=0;e<i.length;e++){let r=i[e],a=r.boneio_id||r.remote_device||``,o=r.id||r.entity_id||``,s=a?`${a}/${o}`:o||`remote_${e}`,c=[];for(let e of n){let t=r.actions?.[e]||[];for(let n of t){let{targetId:t,actionType:r,actionValue:i,remoteDevice:a}=j(n);t&&c.push({clickType:e,actionType:r,targetId:t,actionValue:i,remoteDevice:a})}}t.push({id:s,name:r.name||r.id||o,area:r.area||void 0,type:`remote`,remoteDevice:a,bindings:c})}return t}function N(e){let t=[],n=e.output||[];for(let e=0;e<n.length;e++){let r=n[e];if(r.output_type===`cover`||r.output_type===`none`)continue;let i=r.id||`output_${e}`;t.push({id:i,name:r.name||r.id||i,area:r.area||void 0,type:`output`,outputType:r.output_type})}let r=e.cover||[];for(let e=0;e<r.length;e++){let n=r[e],i=n.id||`cover_${e}`;t.push({id:i,name:n.name||n.id||i,area:n.area||void 0,type:`cover`,outputType:n.device_class||`cover`})}let i=e.remote_outputs||[];for(let e=0;e<i.length;e++){let n=i[e],r=n.boneio_id||n.remote_device||``,a=n.id||n.output_id||``,o=r?`${r}/${a}`:a||`remote_out_${e}`;t.push({id:o,name:n.name||n.id||a,area:n.area||void 0,type:`remote_output`,remoteDevice:r,outputType:n.output_type})}return t}function P({inputs:e,outputs:t,t:n}){let r=e.filter(e=>e.type===`local`),i=e.filter(e=>e.type===`remote`),a=t.filter(e=>e.type===`output`),o=t.filter(e=>e.type===`cover`),s=t.filter(e=>e.type===`remote_output`||e.type===`remote_cover`),c=r.filter(e=>e.bindings.length>0),l=i.filter(e=>e.bindings.length>0),u=new Set(e.flatMap(e=>e.bindings.map(e=>e.targetId))),d=a.filter(e=>u.has(e.id)),f=o.filter(e=>u.has(e.id)),p=s.filter(e=>u.has(e.id)),m=e.reduce((e,t)=>e+t.bindings.length,0);return(0,S.jsxs)(`div`,{className:`grid grid-cols-1 md:grid-cols-4 gap-3 mb-4`,children:[(0,S.jsxs)(`div`,{className:`bg-base-200/50 rounded-xl p-3 border border-base-300 space-y-1`,children:[(0,S.jsx)(`h4`,{className:`text-xs font-bold uppercase tracking-wider text-base-content/50`,children:n(`binding_matrix.local`)}),(0,S.jsxs)(`p`,{className:`text-xs text-base-content/70`,children:[`📥 `,n(`binding_matrix.inputs`),`: `,r.length,(0,S.jsxs)(`span`,{className:`text-success ml-1`,children:[`(`,c.length,` ✓)`]}),(0,S.jsxs)(`span`,{className:`text-warning ml-1`,children:[`(`,r.length-c.length,` ○)`]})]}),(0,S.jsxs)(`p`,{className:`text-xs text-base-content/70`,children:[`💡 `,n(`binding_matrix.outputs`),`: `,a.length,(0,S.jsxs)(`span`,{className:`text-success ml-1`,children:[`(`,d.length,` ✓)`]}),(0,S.jsxs)(`span`,{className:`text-warning ml-1`,children:[`(`,a.length-d.length,` ○)`]})]}),(0,S.jsxs)(`p`,{className:`text-xs text-base-content/70`,children:[`🚪 `,n(`binding_matrix.covers`),`: `,o.length,(0,S.jsxs)(`span`,{className:`text-success ml-1`,children:[`(`,f.length,` ✓)`]}),(0,S.jsxs)(`span`,{className:`text-warning ml-1`,children:[`(`,o.length-f.length,` ○)`]})]})]}),(i.length>0||s.length>0)&&(0,S.jsxs)(`div`,{className:`bg-info/5 rounded-xl p-3 border border-info/20 space-y-1`,children:[(0,S.jsx)(`h4`,{className:`text-xs font-bold uppercase tracking-wider text-info/70`,children:n(`binding_matrix.remote`)}),(0,S.jsxs)(`p`,{className:`text-xs text-base-content/70`,children:[`📥 `,n(`binding_matrix.inputs`),`: `,i.length,(0,S.jsxs)(`span`,{className:`text-success ml-1`,children:[`(`,l.length,` ✓)`]}),(0,S.jsxs)(`span`,{className:`text-warning ml-1`,children:[`(`,i.length-l.length,` ○)`]})]}),(0,S.jsxs)(`p`,{className:`text-xs text-base-content/70`,children:[`📡 `,n(`binding_matrix.outputs`),`: `,s.length,(0,S.jsxs)(`span`,{className:`text-success ml-1`,children:[`(`,p.length,` ✓)`]}),(0,S.jsxs)(`span`,{className:`text-warning ml-1`,children:[`(`,s.length-p.length,` ○)`]})]})]}),(0,S.jsxs)(`div`,{className:`bg-primary/5 rounded-xl p-3 border border-primary/20 flex flex-col justify-center`,children:[(0,S.jsx)(`p`,{className:`text-2xl font-extrabold text-primary`,children:m}),(0,S.jsx)(`p`,{className:`text-xs font-medium text-primary/70`,children:n(`binding_matrix.total_bindings`)})]}),(0,S.jsxs)(`div`,{className:`bg-base-200/50 rounded-xl p-3 border border-base-300`,children:[(0,S.jsx)(`h4`,{className:`text-xs font-bold uppercase tracking-wider text-base-content/50 mb-1.5`,children:n(`binding_matrix.legend`)}),(0,S.jsx)(`div`,{className:`grid grid-cols-2 gap-x-3 gap-y-0.5`,children:E.map(e=>(0,S.jsxs)(`span`,{className:`text-xxs text-base-content/60`,children:[(0,S.jsx)(`span`,{className:`font-mono font-bold text-base-content/80`,children:e.icon}),` `,n(e.labelKey)]},e.key))}),(0,S.jsx)(`div`,{className:`border-t border-base-300 mt-1.5 pt-1.5 grid grid-cols-3 gap-x-2 gap-y-0.5`,children:D.map(e=>(0,S.jsxs)(`span`,{className:`text-xxs text-base-content/60`,children:[(0,S.jsx)(`span`,{className:`font-mono font-bold text-base-content/80`,children:n(e.shortKey)}),` `,n(e.labelKey)]},e.shortKey))})]})]})}function F({inputs:e,outputs:t,t:n}){let[r,i]=(0,b.useState)(!0),[a,c]=(0,b.useState)(!0),l=new Set(e.flatMap(e=>e.bindings.map(e=>e.targetId))),u=e.filter(e=>e.type===`local`&&e.bindings.length===0),d=t.filter(e=>e.type===`output`&&!l.has(e.id)),f=t.filter(e=>e.type===`cover`&&!l.has(e.id)),p=e.filter(e=>e.type===`remote`&&e.bindings.length===0),h=t.filter(e=>(e.type===`remote_output`||e.type===`remote_cover`)&&!l.has(e.id)),g=u.length>0||d.length>0||f.length>0,_=p.length>0||h.length>0;return!g&&!_?null:(0,S.jsxs)(`div`,{className:`mt-4 space-y-3`,children:[g&&(0,S.jsxs)(`div`,{className:`border border-warning/20 rounded-xl bg-warning/5 overflow-hidden`,children:[(0,S.jsxs)(`button`,{className:`w-full p-3 flex items-center justify-between text-sm font-semibold text-warning hover:bg-warning/10 transition-colors`,onClick:()=>i(!r),children:[(0,S.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(s,{className:`w-3.5 h-3.5`}),n(`binding_matrix.unconfigured_local`)]}),r?(0,S.jsx)(o,{className:`w-3 h-3`}):(0,S.jsx)(m,{className:`w-3 h-3`})]}),r&&(0,S.jsxs)(`div`,{className:`p-3 pt-0 space-y-2`,children:[u.length>0&&(0,S.jsx)(I,{title:`📥 ${n(`binding_matrix.free_inputs`)} (${u.length})`,items:u.map(e=>({id:e.id,name:e.name,area:e.area}))}),d.length>0&&(0,S.jsx)(I,{title:`💡 ${n(`binding_matrix.free_outputs`)} (${d.length})`,items:d.map(e=>({id:e.id,name:e.name,area:e.area}))}),f.length>0&&(0,S.jsx)(I,{title:`🚪 ${n(`binding_matrix.free_covers`)} (${f.length})`,items:f.map(e=>({id:e.id,name:e.name,area:e.area}))})]})]}),_&&(0,S.jsxs)(`div`,{className:`border border-info/20 rounded-xl bg-info/5 overflow-hidden`,children:[(0,S.jsxs)(`button`,{className:`w-full p-3 flex items-center justify-between text-sm font-semibold text-info hover:bg-info/10 transition-colors`,onClick:()=>c(!a),children:[(0,S.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(s,{className:`w-3.5 h-3.5`}),n(`binding_matrix.unconfigured_remote`)]}),a?(0,S.jsx)(o,{className:`w-3 h-3`}):(0,S.jsx)(m,{className:`w-3 h-3`})]}),a&&(0,S.jsxs)(`div`,{className:`p-3 pt-0 space-y-2`,children:[p.length>0&&(0,S.jsx)(I,{title:`📥 ${n(`binding_matrix.free_remote_inputs`)} (${p.length})`,items:p.map(e=>({id:e.id,name:e.name,area:e.area,badge:e.remoteDevice}))}),h.length>0&&(0,S.jsx)(I,{title:`📡 ${n(`binding_matrix.free_remote_outputs`)} (${h.length})`,items:h.map(e=>({id:e.id,name:e.name,area:e.area,badge:e.remoteDevice}))})]})]})]})}function I({title:e,items:t}){return(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`p`,{className:`text-xs font-semibold text-base-content/60 mb-1`,children:e}),(0,S.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:t.map(e=>(0,S.jsxs)(`span`,{className:`badge badge-sm badge-ghost text-xxs font-mono gap-1`,title:e.id,children:[e.name,e.area&&(0,S.jsxs)(`span`,{className:`text-base-content/40`,children:[`(`,e.area,`)`]}),e.badge&&(0,S.jsxs)(`span`,{className:`text-info/60`,children:[`[`,e.badge,`]`]})]},e.id))})]})}function ee({inputs:e,outputs:t,areaFilter:n,hideEmpty:r,t:i,onEditInput:a,onEditOutput:o}){let[s,c]=(0,b.useState)(null),[l,u]=(0,b.useState)(null),d=(0,b.useRef)(null),f=(0,b.useRef)(!1),p=(0,b.useRef)(!1),m=(0,b.useRef)(0),_=(0,b.useRef)(0),[v,y]=(0,b.useState)(null),x=(0,b.useRef)(null),w=(0,b.useCallback)((e,t,n)=>{n||(e.preventDefault(),y({x:e.clientX,y:e.clientY,row:t}))},[]),T=(0,b.useCallback)((e,t,n)=>{if(n)return;let r=`touches`in e?{x:e.touches[0].clientX,y:e.touches[0].clientY}:{x:e.clientX,y:e.clientY};x.current=setTimeout(()=>{y({...r,row:t}),x.current=null},500)},[]),D=(0,b.useCallback)(()=>{x.current&&=(clearTimeout(x.current),null)},[]);(0,b.useEffect)(()=>{if(!v)return;let e=null,t=requestAnimationFrame(()=>{let t=()=>y(null);window.addEventListener(`click`,t,{capture:!0}),window.addEventListener(`contextmenu`,t,{capture:!0}),window.addEventListener(`scroll`,t,!0),e=()=>{window.removeEventListener(`click`,t,{capture:!0}),window.removeEventListener(`contextmenu`,t,{capture:!0}),window.removeEventListener(`scroll`,t,!0)}});return()=>{cancelAnimationFrame(t),e?.()}},[v]);let O=(0,b.useMemo)(()=>{let e=new Map;for(let n of t)e.set(n.id,n.area);return e},[t]),j=(0,b.useMemo)(()=>{let t=e;return n.size>0&&(t=t.filter(e=>{let t=e.area||`__none__`;return n.has(t)?!0:e.bindings.some(e=>{let t=O.get(e.targetId)||`__none__`;return n.has(t)})})),r&&(t=t.filter(e=>e.bindings.length>0)),t},[e,n,r,O]),M=(0,b.useMemo)(()=>new Set(e.flatMap(e=>e.bindings.map(e=>e.targetId))),[e]),N=(0,b.useMemo)(()=>{let t=new Map;for(let n of e){let e=n.area||`__none__`;for(let r of n.bindings)t.has(r.targetId)||t.set(r.targetId,new Set),t.get(r.targetId).add(e)}return t},[e]),P=(0,b.useMemo)(()=>{let e=t;return n.size>0&&(e=e.filter(e=>{let t=e.area||`__none__`;if(n.has(t))return!0;let r=N.get(e.id);if(r){for(let e of r)if(n.has(e))return!0}return!1})),r&&(e=e.filter(e=>M.has(e.id))),e},[t,n,r,M,N]),F=(0,b.useMemo)(()=>{let e=new Map;for(let t of j){let n=new Map;for(let e of t.bindings){let t=n.get(e.targetId)||[];t.push(e),n.set(e.targetId,t)}e.set(t.id,n)}return e},[j]);return j.length===0||P.length===0?(0,S.jsx)(`div`,{className:`text-center py-12 text-base-content/40`,children:(0,S.jsx)(`p`,{className:`text-sm`,children:i(`binding_matrix.no_data`)})}):(0,S.jsxs)(`div`,{ref:d,className:`overflow-x-auto rounded-xl border border-base-300 cursor-grab active:cursor-grabbing select-none`,onMouseDown:e=>{f.current=!0,p.current=!1,m.current=e.clientX,_.current=d.current?.scrollLeft||0},onMouseMove:e=>{if(!f.current||!d.current)return;let t=e.clientX-m.current;!p.current&&Math.abs(t)>5&&(p.current=!0),p.current&&(d.current.scrollLeft=_.current-t)},onMouseUp:()=>{f.current=!1,p.current=!1},onMouseLeave:()=>{f.current=!1,p.current=!1},onClickCapture:e=>{p.current&&(e.stopPropagation(),e.preventDefault())},children:[(0,S.jsxs)(`table`,{className:`table table-xs w-auto`,children:[(0,S.jsx)(`thead`,{children:(0,S.jsxs)(`tr`,{children:[(0,S.jsx)(`th`,{className:`sticky left-0 z-20 bg-base-200 border-r border-base-300 min-w-[120px]`}),P.map(e=>(0,S.jsxs)(`th`,{className:h(`text-center text-xxs font-bold px-2 py-2 min-w-[80px] border-r border-base-300 bg-base-200 cursor-pointer group/col whitespace-nowrap`,l===e.id&&`bg-primary/10`),onMouseEnter:()=>u(e.id),onMouseLeave:()=>u(null),onClick:()=>o(e),title:i(`binding_matrix.click_to_edit`),children:[(0,S.jsxs)(`div`,{className:`flex items-center justify-center gap-1`,children:[(0,S.jsx)(C,{outputType:e.outputType,className:`w-3 h-3 shrink-0`}),(0,S.jsx)(`span`,{className:`whitespace-nowrap`,children:e.name}),(0,S.jsx)(g,{className:`w-2 h-2 opacity-0 group-hover/col:opacity-40 transition-opacity shrink-0`})]}),e.area?(0,S.jsx)(`div`,{className:`text-xxs font-normal text-base-content/40 truncate`,children:e.area}):(0,S.jsx)(`div`,{className:`text-xxs font-normal text-base-content/20`,children:`—`}),e.remoteDevice&&(0,S.jsx)(`div`,{className:`text-xxs font-normal text-info/50 truncate`,children:e.remoteDevice})]},e.id))]})}),(0,S.jsx)(`tbody`,{children:j.map(e=>{let t=F.get(e.id);return(0,S.jsxs)(`tr`,{className:h(`hover:bg-base-200/50`,s===e.id&&`bg-base-200/40`),onMouseEnter:()=>c(e.id),onMouseLeave:()=>c(null),children:[(0,S.jsxs)(`td`,{className:h(`sticky left-0 z-10 bg-base-100 border-r border-base-300 font-medium text-xs cursor-pointer group/row`,s===e.id&&`bg-base-200/60`,e.bindings.length===0&&`text-base-content/30`),onClick:()=>a(e),title:i(`binding_matrix.click_to_edit`),children:[(0,S.jsxs)(`div`,{className:`font-bold truncate max-w-[120px] flex items-center gap-1`,title:e.id,children:[e.name,(0,S.jsx)(g,{className:`w-2.5 h-2.5 opacity-0 group-hover/row:opacity-40 transition-opacity shrink-0`})]}),e.area&&(0,S.jsx)(`div`,{className:`text-xxs text-base-content/40`,children:e.area}),e.remoteDevice&&(0,S.jsx)(`div`,{className:`text-xxs text-info/50`,children:e.remoteDevice})]}),P.map(n=>{let r=t?.get(n.id)||[],o=s===e.id||l===n.id;return(0,S.jsx)(`td`,{className:h(`text-center text-xs border-r border-base-300 px-1 py-1`,r.length>0?`bg-success/15 text-success font-medium`:`text-base-content/30`,o&&r.length>0&&`bg-success/25`,o&&r.length===0&&`bg-base-200/40`),onMouseEnter:()=>u(n.id),onMouseLeave:()=>{u(null),D()},onContextMenu:t=>w(t,e,r.length>0),onMouseDown:t=>{t.button===0&&T(t,e,r.length>0)},onMouseUp:D,onTouchStart:t=>T(t,e,r.length>0),onTouchEnd:D,onTouchMove:D,children:r.length>0&&(0,S.jsx)(`div`,{className:`flex flex-col gap-0.5`,children:r.map((t,n)=>(0,S.jsx)(`button`,{className:`whitespace-nowrap cursor-pointer hover:bg-success/30 rounded px-0.5 transition-colors text-left`,onClick:()=>a(e,t.clickType),title:`${A(t,i)}\n${i(`binding_matrix.click_to_edit`)}`,children:k(t,i)},n))})},n.id)})]},e.id)})})]}),v&&(0,S.jsxs)(`div`,{className:`fixed z-50 bg-base-100 border border-base-300 rounded-lg shadow-xl py-1 min-w-[180px] animate-in fade-in zoom-in-95 duration-100`,style:{left:v.x,top:v.y},onClick:e=>e.stopPropagation(),children:[(0,S.jsx)(`div`,{className:`px-3 py-1.5 text-xxs font-semibold text-base-content/40 uppercase tracking-wider`,children:i(`binding_matrix.add_action`)}),E.map(e=>(0,S.jsxs)(`button`,{className:`w-full text-left px-3 py-1.5 text-sm hover:bg-primary/10 transition-colors flex items-center gap-2`,onClick:()=>{a(v.row,e.key),y(null)},children:[(0,S.jsx)(`span`,{className:`font-mono text-base-content/70`,children:e.icon}),i(`binding_matrix.legend_${e.key}`)]},e.key))]})]})}function te({inputs:e,areaFilter:t,hideEmpty:n,t:r,onEditInput:i}){let[a,s]=(0,b.useState)(null),c=(0,b.useMemo)(()=>{let r=e;return t.size>0&&(r=r.filter(e=>{let n=e.area||`__none__`;return t.has(n)})),n&&(r=r.filter(e=>e.bindings.length>0)),r},[e,t,n]),l=(0,b.useCallback)(e=>{s(t=>t===e?null:e)},[]);return c.length===0?(0,S.jsx)(`div`,{className:`text-center py-12 text-base-content/40`,children:(0,S.jsx)(`p`,{className:`text-sm`,children:r(`binding_matrix.no_data`)})}):(0,S.jsx)(`div`,{className:`space-y-1.5`,children:c.map(e=>{let t=a===e.id,n=e.bindings.length>0,s=new Map;for(let t of e.bindings){let e=s.get(t.targetId)||[];e.push(t),s.set(t.targetId,e)}return(0,S.jsxs)(`div`,{className:h(`rounded-xl border overflow-hidden transition-colors`,n?`border-success/20 bg-success/5`:`border-base-300 bg-base-200/30`),children:[(0,S.jsxs)(`button`,{className:`w-full p-3 flex items-center justify-between text-sm hover:bg-base-200/30 transition-colors`,onClick:()=>n&&l(e.id),disabled:!n,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-2 min-w-0`,children:[n?(0,S.jsx)(_,{className:`w-3.5 h-3.5 text-success shrink-0`}):(0,S.jsx)(u,{className:`w-3.5 h-3.5 text-base-content/20 shrink-0`}),(0,S.jsxs)(`div`,{className:`min-w-0 text-left`,children:[(0,S.jsx)(`span`,{className:h(`font-semibold truncate block`,!n&&`text-base-content/30`),children:e.name}),e.area&&(0,S.jsx)(`span`,{className:`text-xxs text-base-content/40`,children:e.area})]})]}),(0,S.jsxs)(`div`,{className:`flex items-center gap-2`,children:[n&&(0,S.jsx)(`span`,{className:`badge badge-sm badge-success font-bold`,children:e.bindings.length}),e.type===`remote`&&(0,S.jsx)(`span`,{className:`badge badge-xs badge-info font-mono`,children:e.remoteDevice}),n&&(t?(0,S.jsx)(o,{className:`w-3 h-3 text-base-content/30`}):(0,S.jsx)(m,{className:`w-3 h-3 text-base-content/30`}))]})]}),t&&n&&(0,S.jsxs)(`div`,{className:`border-t border-base-300 p-3 space-y-2 bg-base-100/50`,children:[[...s.entries()].map(([e,t])=>(0,S.jsxs)(`div`,{className:`flex items-start gap-2 text-xs`,children:[(0,S.jsx)(`span`,{className:`font-mono font-bold text-base-content/70 shrink-0 mt-0.5`,children:`→`}),(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`span`,{className:`font-semibold`,children:e}),(0,S.jsx)(`div`,{className:`flex flex-wrap gap-1 mt-0.5`,children:t.map((e,t)=>(0,S.jsx)(`span`,{className:`badge badge-xs badge-success font-mono gap-0.5`,children:k(e,r)},t))})]})]},e)),(0,S.jsxs)(`button`,{className:`btn btn-xs btn-primary btn-outline gap-1 mt-1`,onClick:t=>{t.stopPropagation(),i(e)},children:[(0,S.jsx)(g,{className:`w-2.5 h-2.5`}),r(`binding_matrix.edit_input`)]})]})]},e.id)})})}var L=({formData:e,sections:t,onSaveSection:n,onUpdateFormData:i})=>{let{t:o}=r(),[s,c]=(0,b.useState)(new Set),[l,u]=(0,b.useState)(!1),[f,p]=(0,b.useState)(!1),[h,g]=(0,b.useState)(!1),[_,x]=(0,b.useState)(null),[C,w]=(0,b.useState)(``),[T,E]=(0,b.useState)(-1),[D,k]=(0,b.useState)(`basic`),[A,j]=(0,b.useState)(!1),[I,L]=(0,b.useState)(!1),R=(0,b.useRef)(null),[ne,z]=(0,b.useState)(!1),[B,V]=(0,b.useState)(null),[H,re]=(0,b.useState)(``),[U,ie]=(0,b.useState)(-1),[ae,W]=(0,b.useState)(!1),G=(0,b.useRef)(null),K=(0,b.useCallback)((t,n)=>{let r=t.type===`remote`?`remote_inputs`:`local_inputs`,i=e[r]||[],a=i.findIndex((e,n)=>{if(t.type===`remote`){let r=e.boneio_id||e.remote_device||``,i=e.id||e.entity_id||``;return(r?`${r}/${i}`:i||`remote_${n}`)===t.id}return(e.id||e.pin||`local_${n}`)===t.id});a<0||(w(r),E(a),x(JSON.parse(JSON.stringify(i[a]))),R.current=JSON.stringify(i[a]),k(n&&O[n]||`basic`),j(!1),g(!0))},[e]),oe=(0,b.useCallback)(async()=>{if(A||T<0||!_)return;let t=[...e[C]||[]];t[T]=_,L(!0);try{i(C,t),await n(C,t),g(!1),x(null)}catch(e){console.error(`Failed to save input:`,e)}finally{L(!1)}},[_,T,C,e,A,n,i]),se=(0,b.useCallback)(()=>{g(!1),x(null)},[]),q=(0,b.useMemo)(()=>M(e),[e]),J=(0,b.useMemo)(()=>N(e),[e]),ce=(0,b.useMemo)(()=>{let e=new Set;return q.forEach(t=>{t.area&&e.add(t.area)}),J.forEach(t=>{t.area&&e.add(t.area)}),[...e].sort()},[q,J]),le=(0,b.useMemo)(()=>q.some(e=>!e.area)||J.some(e=>!e.area),[q,J]),Y=(0,b.useMemo)(()=>e.output||[],[e.output]),X=(0,b.useMemo)(()=>e.cover||[],[e.cover]),Z=(0,b.useMemo)(()=>e.output_group||[],[e.output_group]),ue=(0,b.useMemo)(()=>e.remote_devices||[],[e.remote_devices]),de=(0,b.useMemo)(()=>(e.local_inputs||[]).filter(e=>e._inputType===`binary_sensor`),[e.local_inputs]),Q=(0,b.useMemo)(()=>(e.local_inputs||[]).filter(e=>e._inputType!==`binary_sensor`),[e.local_inputs]),$=(0,b.useMemo)(()=>e.areas||[],[e.areas]),fe=(0,b.useMemo)(()=>{let e=t.find(e=>e.name===`local_inputs`);return e?.schema||e?.normalizedSchema||{}},[t]),pe=(0,b.useMemo)(()=>{let e=t.find(e=>e.name===`output`);return e?.schema||e?.normalizedSchema||{}},[t]),me=(0,b.useMemo)(()=>t.find(e=>e.name===`output`)?.uiSchema||{},[t]),he=(0,b.useMemo)(()=>{let e=t.find(e=>e.name===`cover`);return e?.schema||e?.normalizedSchema||{}},[t]),ge=(0,b.useCallback)(t=>{let n={output:`output`,cover:`cover`,remote_output:`remote_outputs`,remote_cover:`remote_covers`}[t.type]||`output`,r=e[n]||[],i=r.findIndex((e,n)=>{if(t.type===`remote_output`||t.type===`remote_cover`){let r=e.boneio_id||e.remote_device||``,i=e.id||e.output_id||``;return(r?`${r}/${i}`:i||`remote_out_${n}`)===t.id}return(e.id||``)===t.id});i<0||(re(n),ie(i),V(JSON.parse(JSON.stringify(r[i]))),G.current=JSON.stringify(r[i]),z(!0))},[e]),_e=(0,b.useCallback)(async()=>{if(U<0||!B)return;let t=[...e[H]||[]];t[U]=B,W(!0);try{i(H,t),await n(H,t),z(!1),V(null)}catch(e){console.error(`Failed to save output:`,e)}finally{W(!1)}},[B,U,H,e,n,i]),ve=(0,b.useCallback)(()=>{z(!1),V(null)},[]),ye=_&&R.current&&JSON.stringify(_)!==R.current,be=B&&G.current&&JSON.stringify(B)!==G.current;return(0,S.jsxs)(`div`,{className:`p-4 md:p-6 max-w-full`,children:[(0,S.jsxs)(`div`,{className:`mb-4`,children:[(0,S.jsx)(`h2`,{className:`text-xl font-extrabold tracking-tight`,children:o(`binding_matrix.title`)}),(0,S.jsx)(`p`,{className:`text-sm text-base-content/50`,children:o(`binding_matrix.subtitle`)})]}),(0,S.jsx)(P,{inputs:q,outputs:J,t:o}),(0,S.jsxs)(`div`,{className:`flex flex-wrap gap-3 mb-4 items-center`,children:[(0,S.jsxs)(`div`,{className:`relative flex items-center gap-2`,children:[(0,S.jsx)(d,{className:`w-3 h-3 text-base-content/40`}),(0,S.jsxs)(`button`,{className:`btn btn-sm btn-outline gap-1 min-w-[160px] justify-between`,onClick:()=>u(e=>!e),children:[(0,S.jsx)(`span`,{className:`truncate text-xs`,children:s.size===0?o(`binding_matrix.all_areas`):s.size===1?s.has(`__none__`)?o(`binding_matrix.no_area`):[...s][0]:o(`binding_matrix.areas_selected`,{count:s.size})}),(0,S.jsx)(m,{className:`w-2.5 h-2.5 shrink-0`})]}),l&&(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`div`,{className:`fixed inset-0 z-40`,onClick:()=>u(!1)}),(0,S.jsxs)(`div`,{className:`absolute top-full left-0 mt-1 z-50 bg-base-100 border border-base-300 rounded-lg shadow-xl py-1 min-w-[200px] max-h-[300px] overflow-y-auto`,children:[(0,S.jsxs)(`label`,{className:`flex items-center gap-2 px-3 py-1.5 hover:bg-base-200/60 cursor-pointer transition-colors`,children:[(0,S.jsx)(`input`,{type:`checkbox`,className:`checkbox checkbox-xs checkbox-primary`,checked:s.size===0,onChange:()=>c(new Set)}),(0,S.jsx)(`span`,{className:`text-xs font-semibold`,children:o(`binding_matrix.all_areas`)})]}),(0,S.jsx)(`div`,{className:`border-t border-base-300 my-0.5`}),le&&(0,S.jsxs)(`label`,{className:`flex items-center gap-2 px-3 py-1.5 hover:bg-base-200/60 cursor-pointer transition-colors`,children:[(0,S.jsx)(`input`,{type:`checkbox`,className:`checkbox checkbox-xs checkbox-primary`,checked:s.has(`__none__`),onChange:()=>{let e=new Set(s);e.has(`__none__`)?e.delete(`__none__`):e.add(`__none__`),c(e)}}),(0,S.jsx)(`span`,{className:`text-xs italic text-base-content/50`,children:o(`binding_matrix.no_area`)})]}),ce.map(e=>(0,S.jsxs)(`label`,{className:`flex items-center gap-2 px-3 py-1.5 hover:bg-base-200/60 cursor-pointer transition-colors`,children:[(0,S.jsx)(`input`,{type:`checkbox`,className:`checkbox checkbox-xs checkbox-primary`,checked:s.has(e),onChange:()=>{let t=new Set(s);t.has(e)?t.delete(e):t.add(e),c(t)}}),(0,S.jsx)(`span`,{className:`text-xs`,children:e})]},e))]})]})]}),(0,S.jsxs)(`label`,{className:`flex items-center gap-2 cursor-pointer`,children:[(0,S.jsx)(`input`,{type:`checkbox`,className:`checkbox checkbox-sm checkbox-primary`,checked:f,onChange:e=>p(e.target.checked)}),(0,S.jsxs)(`span`,{className:`text-xs font-medium text-base-content/60 flex items-center gap-1`,children:[f?(0,S.jsx)(a,{className:`w-3 h-3`}):(0,S.jsx)(v,{className:`w-3 h-3`}),o(`binding_matrix.hide_empty`)]})]})]}),(0,S.jsx)(`div`,{className:`hidden md:block`,children:(0,S.jsx)(ee,{inputs:q,outputs:J,areaFilter:s,hideEmpty:f,t:o,onEditInput:K,onEditOutput:ge})}),(0,S.jsx)(`div`,{className:`md:hidden`,children:(0,S.jsx)(te,{inputs:q,areaFilter:s,hideEmpty:f,t:o,onEditInput:K})}),(0,S.jsx)(F,{inputs:q,outputs:J,t:o}),(0,S.jsx)(y,{open:h,onOpenChange:g,editingItem:_,editingIndex:T,sectionType:C,schema:C===`remote_inputs`?t.find(e=>e.name===`remote_inputs`)?.schema||t.find(e=>e.name===`remote_inputs`)?.normalizedSchema||{}:fe,allBinarySensors:de,allEvents:Q,allOutputs:Y,allOutputGroups:Z,allCovers:X,allAreas:$,allRemoteDevices:ue,savedOutputs:Y,savedOutputGroups:Z,savedCovers:X,onChange:x,onSave:oe,onCancel:se,onValidationChange:j,saveDisabled:A||!ye,isSaving:I,initialTab:D}),(0,S.jsx)(y,{open:ne,onOpenChange:z,editingItem:B,editingIndex:U,sectionType:H,schema:H===`cover`?he:pe,uiSchema:H===`cover`?void 0:me,allOutputs:Y,allCovers:X,allAreas:$,onChange:V,onSave:_e,onCancel:ve,saveDisabled:!be,isSaving:ae})]})};export{L as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{a as t,c as n,i as r,n as i,o as a,r as o,t as s}from"./monaco-61yH0vfd.js";import{y as c}from"./vendor-DjFmndpd.js";import{r as l,t as u}from"./useTranslation-CMe3OXqd.js";import{S as d,Wt as f,_t as p,b as m,gt as h,vt as g}from"./index-CQtJKcCO.js";var _=e(n(),1);function v(){let[e,t]=(0,_.useState)(`dark`);return(0,_.useEffect)(()=>{t(localStorage.getItem(`theme`)||`dark`);let e=new MutationObserver(e=>{e.forEach(e=>{e.attributeName===`data-theme`&&t(document.documentElement.getAttribute(`data-theme`)||`dark`)})});return e.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-theme`]}),()=>e.disconnect()},[]),e}function y(e){return f({tag:`svg`,attr:{viewBox:`0 0 24 24`},child:[{tag:`path`,attr:{d:`M11.28 9.53 8.81 12l2.47 2.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-3-3a.75.75 0 0 1 0-1.06l3-3a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734Z`},child:[]},{tag:`path`,attr:{d:`M3.75 2h16.5c.966 0 1.75.784 1.75 1.75v16.5A1.75 1.75 0 0 1 20.25 22H3.75A1.75 1.75 0 0 1 2 20.25V3.75C2 2.784 2.784 2 3.75 2ZM3.5 3.75v16.5c0 .138.112.25.25.25H15v-17H3.75a.25.25 0 0 0-.25.25Zm13 16.75h3.75a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25H16.5Z`},child:[]}]})(e)}var b=c();function x({isOpen:e,onClose:t}){let{t:n}=u(),[r,i]=(0,_.useState)(!1),[a,o]=(0,_.useState)(null),s=async()=>{i(!0);try{let e=await l.get(`/api/check_configuration`);o({status:e.data.status,message:e.data.message})}catch{o({status:`error`,message:n(`config_check.failed`)})}finally{i(!1)}};return e?(0,b.jsx)(`div`,{className:`fixed inset-0 bg-base-300/80 flex items-center justify-center z-50`,children:(0,b.jsxs)(`div`,{className:`bg-base-200 p-8 rounded-lg shadow-lg max-w-2xl w-full mx-4`,children:[(0,b.jsx)(`h2`,{className:`text-2xl font-bold mb-6`,children:n(`config_check.title`)}),r?(0,b.jsxs)(`div`,{className:`flex flex-col items-center gap-4 py-8`,children:[(0,b.jsx)(`div`,{className:`loading loading-spinner loading-lg`}),(0,b.jsx)(`p`,{className:`text-lg`,children:n(`config_check.checking`)})]}):a?(0,b.jsxs)(`div`,{className:`space-y-4`,children:[(0,b.jsx)(`div`,{className:`text-lg font-semibold ${a.status===`success`?`text-success`:`text-error`}`,children:a.status===`success`?n(`config_check.valid`):n(`config_check.error`)}),a.message&&(0,b.jsx)(`pre`,{className:`bg-base-300 p-4 rounded-lg whitespace-pre-wrap`,children:a.message}),(0,b.jsxs)(`div`,{className:`flex justify-end gap-4 mt-6`,children:[(0,b.jsx)(`button`,{className:`btn btn-ghost`,onClick:()=>{o(null),t()},children:n(`common.close`)}),(0,b.jsx)(`button`,{className:`btn btn-primary`,onClick:()=>{o(null),s()},children:n(`config_check.check_again`)})]})]}):(0,b.jsxs)(`div`,{className:`space-y-6`,children:[(0,b.jsx)(`p`,{className:`text-base-content/70`,children:n(`config_check.description`)}),(0,b.jsxs)(`div`,{className:`flex justify-end gap-4`,children:[(0,b.jsx)(`button`,{className:`btn btn-ghost`,onClick:t,children:n(`common.cancel`)}),(0,b.jsx)(`button`,{className:`btn btn-primary`,onClick:s,children:n(`config_check.check_button`)})]})]})]})}):null}function S(e){return new Worker(`/assets/yaml.worker-BKRoXlmz.js`,{name:e?.name})}self.MonacoEnvironment={getWorker(e,t){return t===`yaml`?new S:t===`json`?new s:new i}},a.config({monaco:r});var C;function w(){let[e,n]=(0,_.useState)([]),[r,i]=(0,_.useState)(null),[a,s]=(0,_.useState)(``),[c,u]=(0,_.useState)(!1),[f,S]=(0,_.useState)(`waiting`),[w,T]=(0,_.useState)(!0),[E,D]=(0,_.useState)(!1),O=v(),k=async()=>{try{n((await l.get(`/api/files`)).data.items)}catch(e){console.error(`Error loading directory:`,e)}},A=async e=>{try{let t=`/api/files/${encodeURIComponent(e)}`,n=await l.get(t);i(e),s(n.data.content),localStorage.setItem(`lastOpenedFile`,e)}catch(e){console.error(`Error loading file:`,e)}},j=async()=>{if(r)try{await l.put(`/api/files/${encodeURIComponent(r)}`,{content:a}),console.log(`File saved successfully`)}catch(e){console.error(`Error saving file:`,e)}},M=async e=>{if(!C){let t=(await l.get(`/schema/config.schema.json`)).data;C=await o(e,{enableSchemaRequest:!0,schemas:[{uri:window.location.origin+`/schema/config.schema.json`,fileMatch:[`config.yaml`,`config.yml`],schema:t},...Object.keys(t.properties).map(e=>({uri:window.location.origin+`/schema/${e}.schema.json`,fileMatch:[`${e}.yaml`,`${e}.yml`],schema:t.properties[e]}))],validate:!0})}},N=async(e,t)=>{e.addCommand(t.KeyMod.CtrlCmd|t.KeyCode.KeyS,()=>{j()})},P=e=>{n(t=>{let n=t=>t.map(t=>t.path===e?{...t,isOpen:!t.isOpen}:t.children?{...t,children:n(t.children)}:t);return n(t)})},F=(e,t=0)=>e.map(e=>{let n=t==0||e.isOpen;return(0,b.jsxs)(`div`,{children:[(0,b.jsxs)(`button`,{className:`flex items-center gap-2 w-full px-2 py-1 hover:bg-base-300 ${r===e.path?`bg-primary/10`:``}`,style:{paddingLeft:`${t*16+8}px`},onClick:()=>e.type===`file`?A(e.path):P(e.path),children:[e.type===`directory`&&(0,b.jsx)(`span`,{className:`text-xs`,children:n?(0,b.jsx)(m,{}):(0,b.jsx)(d,{})}),(0,b.jsx)(`span`,{className:`text-base-content/70`,children:e.type===`directory`?n?(0,b.jsx)(g,{}):(0,b.jsx)(p,{}):(0,b.jsx)(h,{})}),(0,b.jsx)(`span`,{className:`text-sm truncate`,children:e.name})]}),e.type===`directory`&&n&&e.children&&F(e.children,t+1)]},e.path)}),I=async()=>{try{return await l.get(`/api/files`),!0}catch{return!1}},L=async()=>{for(u(!0),S(`waiting`),await new Promise(e=>setTimeout(e,2e3)),S(`checking`);;){if(await I()){S(`ready`),await new Promise(e=>setTimeout(e,1e3)),u(!1),k();break}await new Promise(e=>setTimeout(e,1e3))}};return(0,_.useEffect)(()=>{(async()=>{await k();let e=localStorage.getItem(`lastOpenedFile`);e&&A(e)})()},[]),(0,b.jsxs)(`div`,{className:`flex h-full bg-base-300`,children:[c?(0,b.jsx)(`div`,{className:`fixed inset-0 bg-base-300/80 flex items-center justify-center z-50`,children:(0,b.jsxs)(`div`,{className:`bg-base-200 p-8 rounded-lg shadow-lg text-center`,children:[(0,b.jsx)(`div`,{className:`loading loading-spinner loading-lg mb-4`}),(0,b.jsxs)(`h2`,{className:`text-xl font-bold mb-4`,children:[f===`waiting`&&`Restarting Service...`,f===`checking`&&`Waiting for Service...`,f===`ready`&&`Service Restarted!`]}),(0,b.jsxs)(`p`,{className:`text-base-content/70`,children:[f===`waiting`&&`Initiating service restart`,f===`checking`&&`Checking if service is back online`,f===`ready`&&`Redirecting...`]})]})}):null,(0,b.jsxs)(`div`,{className:`${w?`w-64`:`w-0`} transition-all duration-300 bg-base-200 border-r border-base-content/10 overflow-hidden`,children:[(0,b.jsx)(`div`,{className:`p-2 text-sm font-semibold text-base-content/70 uppercase`,children:`Explorer`}),(0,b.jsx)(`div`,{className:`overflow-y-auto`,children:F(e)})]}),(0,b.jsxs)(`div`,{className:`flex-1 overflow-hidden flex flex-col h-full max-h-screen`,children:[(0,b.jsxs)(`div`,{className:`flex items-center p-2 border-b border-base-content/10`,children:[(0,b.jsx)(`button`,{onClick:()=>T(!w),className:`btn btn-ghost btn-sm p-1`,title:w?`Hide file tree`:`Show file tree`,children:(0,b.jsx)(y,{className:`h-5 w-5 opacity-70 transition-transform duration-200 ${w?``:`rotate-180`}`})}),r&&(0,b.jsx)(`span`,{className:`ml-2 text-sm opacity-70`,children:r})]}),(0,b.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,b.jsx)(t,{height:`100%`,width:`100%`,defaultLanguage:`yaml`,path:r||`config.yaml`,theme:O===`light`?`vs-light`:`vs-dark`,value:a,onChange:e=>s(e||``),onMount:N,beforeMount:M,options:{minimap:{enabled:!0},scrollBeyondLastLine:!1,fontSize:14,automaticLayout:!0,readOnly:!1,formatOnType:!0,formatOnPaste:!0,quickSuggestions:{other:!0,comments:!1,strings:!0}}})}),(0,b.jsxs)(`div`,{className:`h-12 bg-base-200 border-t border-base-content/10 flex items-center justify-end px-4 gap-4`,children:[(0,b.jsx)(`button`,{onClick:j,disabled:!r,className:`btn btn-primary btn-sm`,children:`Save`}),(0,b.jsx)(`button`,{onClick:()=>D(!0),className:`btn btn-primary btn-sm`,children:`Check Config`}),(0,b.jsx)(`button`,{onClick:async()=>{await j();try{if((await l.post(`/api/restart`)).data.status===`not available`){let e=document.getElementById(`toast`);e&&(e.classList.remove(`hidden`),setTimeout(()=>{e.classList.add(`hidden`)},5e3))}else console.log(`Service restarted successfully`),L()}catch(e){console.error(`Error restarting service:`,e)}},disabled:!r,className:`btn btn-secondary btn-sm`,children:`Save and Restart`})]})]}),(0,b.jsx)(`div`,{id:`toast`,className:`toast toast-end hidden`,children:(0,b.jsx)(`div`,{className:`alert alert-warning`,children:(0,b.jsx)(`span`,{children:`Restart not possible. Application is not running as systemd service. Restart manually.`})})}),(0,b.jsx)(x,{isOpen:E,onClose:()=>D(!1)})]})}export{w as default};
|