boneIO 1.3.0.dev18__py3-none-any.whl → 1.3.0.dev20__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 +3 -6
- boneio/core/config/yaml_util.py +13 -16
- boneio/core/manager/inputs.py +4 -3
- boneio/core/manager/manager.py +349 -228
- boneio/core/manager/modbus.py +1 -1
- boneio/core/manager/outputs.py +0 -2
- boneio/core/manager/sensors.py +0 -2
- boneio/core/manager/update.py +261 -181
- boneio/core/state/manager.py +3 -6
- boneio/hardware/display/oled.py +4 -6
- boneio/migrations/assets/MANIFEST.sha256 +1 -5
- boneio/migrations/assets/usr-sbin/oled_boot_splash.py +1 -1
- boneio/migrations/versions/v1_3_0_baseline.py +5 -0
- boneio/migrations/versions/{v1_3_0dev18_fix_oled_boot.py → v1_3_1_fix_oled_boot.py} +1 -1
- boneio/runner.py +0 -2
- boneio/version.py +1 -1
- boneio/webui/frontend-dist/assets/{ConfigEditor-vyhv5yQV.js → ConfigEditor-DKIsOog1.js} +1 -1
- boneio/webui/frontend-dist/assets/index-C_S5rhz7.css +2 -0
- boneio/webui/frontend-dist/assets/{index-D97SbEW0.js → index-ixNf6oOr.js} +31 -31
- boneio/webui/frontend-dist/assets/useTranslation-CS9arJOj.js +13 -0
- boneio/webui/frontend-dist/index.html +3 -3
- boneio/webui/frontend-dist/sw.js +1 -1
- boneio/webui/routes/config.py +27 -15
- boneio/webui/routes/modbus.py +2 -4
- {boneio-1.3.0.dev18.dist-info → boneio-1.3.0.dev20.dist-info}/METADATA +1 -1
- {boneio-1.3.0.dev18.dist-info → boneio-1.3.0.dev20.dist-info}/RECORD +30 -30
- boneio/webui/frontend-dist/assets/index-D7hfuu2N.css +0 -2
- boneio/webui/frontend-dist/assets/useTranslation-DxGR4SjZ.js +0 -13
- {boneio-1.3.0.dev18.dist-info → boneio-1.3.0.dev20.dist-info}/WHEEL +0 -0
- {boneio-1.3.0.dev18.dist-info → boneio-1.3.0.dev20.dist-info}/entry_points.txt +0 -0
- {boneio-1.3.0.dev18.dist-info → boneio-1.3.0.dev20.dist-info}/licenses/LICENSE +0 -0
- {boneio-1.3.0.dev18.dist-info → boneio-1.3.0.dev20.dist-info}/top_level.txt +0 -0
|
@@ -40,7 +40,7 @@ class ConfigHelper:
|
|
|
40
40
|
device_type: str = "boneIO Black",
|
|
41
41
|
ha_discovery: bool = True,
|
|
42
42
|
ha_discovery_prefix: str = HOMEASSISTANT,
|
|
43
|
-
network_info: dict =
|
|
43
|
+
network_info: dict | None = None,
|
|
44
44
|
is_web_active: bool = False,
|
|
45
45
|
web_port: int = 8090,
|
|
46
46
|
proxy_port: int | None = None,
|
|
@@ -56,7 +56,7 @@ class ConfigHelper:
|
|
|
56
56
|
self._name = name
|
|
57
57
|
|
|
58
58
|
# Generate serial number from MAC - always required for topic prefix
|
|
59
|
-
self._serial_no = get_serial_from_mac(network_info)
|
|
59
|
+
self._serial_no = get_serial_from_mac(network_info or {})
|
|
60
60
|
|
|
61
61
|
# Build fixed topic prefix: boneio/blk_{serial}
|
|
62
62
|
# This is no longer configurable - always uses this format
|
|
@@ -309,10 +309,7 @@ class ConfigHelper:
|
|
|
309
309
|
def is_topic_in_autodiscovery(self, topic: str) -> bool:
|
|
310
310
|
topic_parts_raw = topic[len(f"{self._ha_discovery_prefix}/") :].split("/")
|
|
311
311
|
ha_type = topic_parts_raw[0]
|
|
312
|
-
|
|
313
|
-
if topic in self._autodiscovery_messages[ha_type]:
|
|
314
|
-
return True
|
|
315
|
-
return False
|
|
312
|
+
return ha_type in self._autodiscovery_messages and topic in self._autodiscovery_messages[ha_type]
|
|
316
313
|
|
|
317
314
|
def clear_autodiscovery_type(self, ha_type: str):
|
|
318
315
|
self._autodiscovery_messages[ha_type] = {}
|
boneio/core/config/yaml_util.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import fnmatch
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
@@ -309,7 +310,7 @@ def merge_board_config(config: dict) -> dict:
|
|
|
309
310
|
if not board_config:
|
|
310
311
|
raise ConfigurationException(f"Bottom board configuration file {board_file} is empty")
|
|
311
312
|
except FileNotFoundError:
|
|
312
|
-
raise ConfigurationException(f"Board configuration for {board_name} version {version} not found")
|
|
313
|
+
raise ConfigurationException(f"Board configuration for {board_name} version {version} not found") from None
|
|
313
314
|
_LOGGER.debug(f"Loaded board configuration: {board_name}")
|
|
314
315
|
|
|
315
316
|
# Copy MCP configuration if not already defined
|
|
@@ -427,9 +428,8 @@ class CustomValidator(Validator):
|
|
|
427
428
|
doc_value = self.document[key] # type: ignore[attr-defined]
|
|
428
429
|
if isinstance(doc_value, str):
|
|
429
430
|
doc_value = doc_value.lower()
|
|
430
|
-
if doc_value in [v.lower() if isinstance(v, str) else v for v in values]:
|
|
431
|
-
|
|
432
|
-
self._error(field, f"required when {key} is {doc_value}") # type: ignore[attr-defined]
|
|
431
|
+
if doc_value in [v.lower() if isinstance(v, str) else v for v in values] and field not in self.document: # type: ignore[attr-defined]
|
|
432
|
+
self._error(field, f"required when {key} is {doc_value}") # type: ignore[attr-defined]
|
|
433
433
|
|
|
434
434
|
def _validate_forbidden_if(self, forbidden_if, field, value):
|
|
435
435
|
"""Validate that a field is forbidden if a condition is met.
|
|
@@ -448,9 +448,12 @@ class CustomValidator(Validator):
|
|
|
448
448
|
if isinstance(doc_value, str):
|
|
449
449
|
doc_value = doc_value.lower()
|
|
450
450
|
|
|
451
|
-
if
|
|
452
|
-
|
|
453
|
-
|
|
451
|
+
if (
|
|
452
|
+
doc_value in [v.lower() if isinstance(v, str) else v for v in values]
|
|
453
|
+
and field in self.document
|
|
454
|
+
and value != default_value
|
|
455
|
+
): # type: ignore[attr-defined]
|
|
456
|
+
self._error(field, f"forbidden when {key} is {doc_value}") # type: ignore[attr-defined]
|
|
454
457
|
|
|
455
458
|
def _normalize_coerce_action_field(self, value):
|
|
456
459
|
"""Handle conditional defaults for action fields."""
|
|
@@ -809,7 +812,6 @@ def _compute_config_dir_hash(config_file: str) -> str:
|
|
|
809
812
|
hashed — other files in the directory (e.g. ``state.json``) are ignored.
|
|
810
813
|
"""
|
|
811
814
|
import hashlib
|
|
812
|
-
import re
|
|
813
815
|
from pathlib import Path
|
|
814
816
|
|
|
815
817
|
config_path = Path(config_file)
|
|
@@ -1062,7 +1064,7 @@ def load_config_from_file(
|
|
|
1062
1064
|
try:
|
|
1063
1065
|
config_yaml = load_yaml_file(config_file)
|
|
1064
1066
|
except FileNotFoundError as err:
|
|
1065
|
-
raise ConfigurationException(err)
|
|
1067
|
+
raise ConfigurationException(err) from err
|
|
1066
1068
|
_LOGGER.debug("[STARTUP TIMING] load_yaml_file: %.2fs", _time.monotonic() - _t0)
|
|
1067
1069
|
if not config_yaml:
|
|
1068
1070
|
_LOGGER.warning("Missing yaml file. %s", config_file)
|
|
@@ -1205,7 +1207,6 @@ def update_config_section(config_file: str, section: str, data: dict | list) ->
|
|
|
1205
1207
|
Returns:
|
|
1206
1208
|
dict: Status response with success/error message
|
|
1207
1209
|
"""
|
|
1208
|
-
import os
|
|
1209
1210
|
from pathlib import Path
|
|
1210
1211
|
|
|
1211
1212
|
config_dir = Path(config_file).parent
|
|
@@ -1222,10 +1223,8 @@ def update_config_section(config_file: str, section: str, data: dict | list) ->
|
|
|
1222
1223
|
if addr.startswith("0x") or addr.startswith("0X"):
|
|
1223
1224
|
entry["address"] = int(addr, 16)
|
|
1224
1225
|
else:
|
|
1225
|
-
|
|
1226
|
+
with contextlib.suppress(ValueError):
|
|
1226
1227
|
entry["address"] = int(addr, 10)
|
|
1227
|
-
except ValueError:
|
|
1228
|
-
pass # Keep as string if conversion fails
|
|
1229
1228
|
|
|
1230
1229
|
# Strip default values to keep YAML clean
|
|
1231
1230
|
cleaned_data = strip_default_values(data, {}, section)
|
|
@@ -1366,8 +1365,6 @@ def update_yaml_field(config_file: str, section: str, field: str, value: str) ->
|
|
|
1366
1365
|
Returns:
|
|
1367
1366
|
dict: Status response with success/error message.
|
|
1368
1367
|
"""
|
|
1369
|
-
import re
|
|
1370
|
-
|
|
1371
1368
|
try:
|
|
1372
1369
|
with open(config_file, encoding="utf-8") as f:
|
|
1373
1370
|
lines = f.readlines()
|
|
@@ -1382,7 +1379,7 @@ def update_yaml_field(config_file: str, section: str, field: str, value: str) ->
|
|
|
1382
1379
|
insert_index = -1
|
|
1383
1380
|
updated_lines = []
|
|
1384
1381
|
|
|
1385
|
-
for
|
|
1382
|
+
for line in lines:
|
|
1386
1383
|
stripped = line.strip()
|
|
1387
1384
|
line_indent = len(line) - len(line.lstrip())
|
|
1388
1385
|
|
boneio/core/manager/inputs.py
CHANGED
|
@@ -177,7 +177,7 @@ class InputManager:
|
|
|
177
177
|
gpio: dict,
|
|
178
178
|
pin: str,
|
|
179
179
|
existing_input: GpioEventButton | None = None,
|
|
180
|
-
actions: dict =
|
|
180
|
+
actions: dict | None = None,
|
|
181
181
|
) -> GpioEventButton | None:
|
|
182
182
|
"""Configure event input sensor with multiclick detection.
|
|
183
183
|
|
|
@@ -190,6 +190,7 @@ class InputManager:
|
|
|
190
190
|
Returns:
|
|
191
191
|
Configured GpioEventButton instance or None on error
|
|
192
192
|
"""
|
|
193
|
+
actions = actions or {}
|
|
193
194
|
try:
|
|
194
195
|
# Determine display name (ensure it's always a string)
|
|
195
196
|
if "name" in gpio:
|
|
@@ -313,7 +314,7 @@ class InputManager:
|
|
|
313
314
|
gpio: dict,
|
|
314
315
|
pin: str,
|
|
315
316
|
existing_input: GpioInputBinarySensor | None = None,
|
|
316
|
-
actions: dict =
|
|
317
|
+
actions: dict | None = None,
|
|
317
318
|
) -> GpioInputBinarySensor | None:
|
|
318
319
|
"""Configure binary sensor input with state detection.
|
|
319
320
|
|
|
@@ -326,6 +327,7 @@ class InputManager:
|
|
|
326
327
|
Returns:
|
|
327
328
|
Configured GpioInputBinarySensor instance or None on error
|
|
328
329
|
"""
|
|
330
|
+
actions = actions or {}
|
|
329
331
|
try:
|
|
330
332
|
# Determine display name (ensure it's always a string)
|
|
331
333
|
if "name" in gpio:
|
|
@@ -564,7 +566,6 @@ class InputManager:
|
|
|
564
566
|
import time
|
|
565
567
|
|
|
566
568
|
from boneio.models import InputState
|
|
567
|
-
from boneio.models.events import InputEvent
|
|
568
569
|
|
|
569
570
|
timestamp = time.time()
|
|
570
571
|
|