boneIO 1.4.0.dev13__py3-none-any.whl → 1.4.0.dev14__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/components/irrigation/controller.py +7 -0
- boneio/core/manager/inputs.py +71 -21
- boneio/hardware/display/oled.py +1 -1
- boneio/version.py +1 -1
- boneio/webui/frontend-dist/assets/index-DarJzEhw.js +100 -0
- boneio/webui/frontend-dist/index.html +1 -1
- boneio/webui/frontend-dist/sw.js +1 -1
- boneio/webui/routes/irrigation.py +16 -5
- {boneio-1.4.0.dev13.dist-info → boneio-1.4.0.dev14.dist-info}/METADATA +1 -1
- {boneio-1.4.0.dev13.dist-info → boneio-1.4.0.dev14.dist-info}/RECORD +14 -14
- boneio/webui/frontend-dist/assets/index-CEComJE_.js +0 -100
- {boneio-1.4.0.dev13.dist-info → boneio-1.4.0.dev14.dist-info}/WHEEL +0 -0
- {boneio-1.4.0.dev13.dist-info → boneio-1.4.0.dev14.dist-info}/entry_points.txt +0 -0
- {boneio-1.4.0.dev13.dist-info → boneio-1.4.0.dev14.dist-info}/licenses/LICENSE +0 -0
- {boneio-1.4.0.dev13.dist-info → boneio-1.4.0.dev14.dist-info}/top_level.txt +0 -0
|
@@ -691,6 +691,13 @@ class IrrigationController:
|
|
|
691
691
|
self._active_zone_remaining_s = duration
|
|
692
692
|
self._state = ControllerState.RUNNING
|
|
693
693
|
|
|
694
|
+
# Publish state immediately so the UI (frontend + HA) shows
|
|
695
|
+
# RUNNING right away, before any pump/valve delay sleeps.
|
|
696
|
+
# The timer has not been armed yet so zone_end_time will be
|
|
697
|
+
# empty, but a second publish happens after hardware activation.
|
|
698
|
+
self._run_start_utc = utcnow()
|
|
699
|
+
await self.publish_all_states()
|
|
700
|
+
|
|
694
701
|
if src is not None:
|
|
695
702
|
if src.pump_start_valve_delay_s > 0:
|
|
696
703
|
# Source first (pump+valve), then zone valve after delay
|
boneio/core/manager/inputs.py
CHANGED
|
@@ -130,8 +130,16 @@ class InputManager:
|
|
|
130
130
|
return False
|
|
131
131
|
return False
|
|
132
132
|
|
|
133
|
-
def configure_single_input(
|
|
134
|
-
|
|
133
|
+
def configure_single_input(
|
|
134
|
+
configure_sensor_func: Callable, gpio: dict, expected_class: type
|
|
135
|
+
) -> None:
|
|
136
|
+
"""Configure a single input (event or binary sensor).
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
configure_sensor_func: The _configure_event_sensor or _configure_binary_sensor method.
|
|
140
|
+
gpio: GPIO configuration dictionary.
|
|
141
|
+
expected_class: The expected class type (GpioEventButton or GpioInputBinarySensor).
|
|
142
|
+
"""
|
|
135
143
|
# Work on a copy to avoid modifying the cached config
|
|
136
144
|
gpio_copy = gpio.copy()
|
|
137
145
|
|
|
@@ -149,6 +157,18 @@ class InputManager:
|
|
|
149
157
|
|
|
150
158
|
existing_input = self._inputs.get(input_id, None) if reload_config else None
|
|
151
159
|
|
|
160
|
+
# Guard: if the existing input is a different class (type changed,
|
|
161
|
+
# e.g. event → binary_sensor), do NOT pass it as existing_input.
|
|
162
|
+
# A new object with the correct detector must be created instead.
|
|
163
|
+
if existing_input is not None and not isinstance(existing_input, expected_class):
|
|
164
|
+
_LOGGER.info(
|
|
165
|
+
"Input %s type changed from %s to %s, creating new input object",
|
|
166
|
+
input_id,
|
|
167
|
+
type(existing_input).__name__,
|
|
168
|
+
expected_class.__name__,
|
|
169
|
+
)
|
|
170
|
+
existing_input = None
|
|
171
|
+
|
|
152
172
|
input_device = configure_sensor_func(
|
|
153
173
|
gpio=gpio_copy,
|
|
154
174
|
pin=pin,
|
|
@@ -172,14 +192,22 @@ class InputManager:
|
|
|
172
192
|
# Configure event buttons
|
|
173
193
|
for gpio in self._event_pins:
|
|
174
194
|
try:
|
|
175
|
-
configure_single_input(
|
|
195
|
+
configure_single_input(
|
|
196
|
+
configure_sensor_func=self._configure_event_sensor,
|
|
197
|
+
gpio=gpio,
|
|
198
|
+
expected_class=GpioEventButton,
|
|
199
|
+
)
|
|
176
200
|
except GPIOInputException as err:
|
|
177
201
|
_LOGGER.error("Failed to configure event input: %s", err)
|
|
178
202
|
|
|
179
203
|
# Configure binary sensors
|
|
180
204
|
for gpio in self._binary_pins:
|
|
181
205
|
try:
|
|
182
|
-
configure_single_input(
|
|
206
|
+
configure_single_input(
|
|
207
|
+
configure_sensor_func=self._configure_binary_sensor,
|
|
208
|
+
gpio=gpio,
|
|
209
|
+
expected_class=GpioInputBinarySensor,
|
|
210
|
+
)
|
|
183
211
|
except GPIOInputException as err:
|
|
184
212
|
_LOGGER.error("Failed to configure binary sensor: %s", err)
|
|
185
213
|
|
|
@@ -490,21 +518,25 @@ class InputManager:
|
|
|
490
518
|
# Get config from ConfigHelper (already reloaded by Manager)
|
|
491
519
|
config = self._manager._config_helper.get_config()
|
|
492
520
|
|
|
493
|
-
# Build map of new inputs from config (input_id -> {pin, area})
|
|
521
|
+
# Build map of new inputs from config (input_id -> {pin, area, ha_type})
|
|
494
522
|
# Note: boneio_input is normalized to lowercase to match yaml_util.py behavior
|
|
495
523
|
new_input_map: dict[str, dict] = {}
|
|
496
|
-
for
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
524
|
+
for ha_type, gpio_list in [
|
|
525
|
+
(EVENT_ENTITY, config.get(EVENT_ENTITY, [])),
|
|
526
|
+
(BINARY_SENSOR, config.get(BINARY_SENSOR, [])),
|
|
527
|
+
]:
|
|
528
|
+
for gpio in gpio_list:
|
|
529
|
+
pin = gpio.get("pin")
|
|
530
|
+
# Determine input ID (same logic as in _configure_event_sensor/_configure_binary_sensor)
|
|
531
|
+
if "id" in gpio:
|
|
532
|
+
input_id = gpio["id"]
|
|
533
|
+
elif "boneio_input" in gpio:
|
|
534
|
+
input_id = gpio["boneio_input"].lower()
|
|
535
|
+
elif pin:
|
|
536
|
+
input_id = pin
|
|
537
|
+
else:
|
|
538
|
+
continue
|
|
539
|
+
new_input_map[input_id] = {"pin": pin, "area": gpio.get("area"), "ha_type": ha_type}
|
|
508
540
|
|
|
509
541
|
# Find inputs to remove (in current config but not in new config)
|
|
510
542
|
current_input_ids = set(self._inputs.keys())
|
|
@@ -512,12 +544,29 @@ class InputManager:
|
|
|
512
544
|
|
|
513
545
|
inputs_to_remove = current_input_ids - new_input_ids
|
|
514
546
|
|
|
515
|
-
_LOGGER.info(
|
|
547
|
+
_LOGGER.info("Input reload: current=%s, new=%s, to_remove=%s", current_input_ids, new_input_ids, inputs_to_remove)
|
|
548
|
+
|
|
549
|
+
# Detect inputs whose type changed (event <-> binary_sensor).
|
|
550
|
+
# These must be removed and re-created with the correct detector class.
|
|
551
|
+
inputs_type_changed: set[str] = set()
|
|
552
|
+
for input_id, new_info in new_input_map.items():
|
|
553
|
+
existing = self._inputs.get(input_id)
|
|
554
|
+
if existing is None:
|
|
555
|
+
continue
|
|
556
|
+
new_ha_type = new_info["ha_type"]
|
|
557
|
+
# GpioEventButton has input_type=INPUT, GpioInputBinarySensor has input_type=INPUT_SENSOR
|
|
558
|
+
if new_ha_type == EVENT_ENTITY and not isinstance(existing, GpioEventButton):
|
|
559
|
+
inputs_type_changed.add(input_id)
|
|
560
|
+
elif new_ha_type == BINARY_SENSOR and not isinstance(existing, GpioInputBinarySensor):
|
|
561
|
+
inputs_type_changed.add(input_id)
|
|
562
|
+
|
|
563
|
+
if inputs_type_changed:
|
|
564
|
+
_LOGGER.info("Input type changed for: %s — will recreate with new detector", inputs_type_changed)
|
|
516
565
|
|
|
517
566
|
# Remove deleted inputs from internal state (GPIO pin stays registered - minimal overhead)
|
|
518
567
|
ha_discovery_changed = False
|
|
519
568
|
|
|
520
|
-
for input_id in inputs_to_remove:
|
|
569
|
+
for input_id in inputs_to_remove | inputs_type_changed:
|
|
521
570
|
input_device = self._inputs.get(input_id)
|
|
522
571
|
if input_device:
|
|
523
572
|
old_area = getattr(input_device, "area", None)
|
|
@@ -528,7 +577,7 @@ class InputManager:
|
|
|
528
577
|
|
|
529
578
|
# Remove from internal state (GPIO detector will be replaced by new input class)
|
|
530
579
|
del self._inputs[input_id]
|
|
531
|
-
_LOGGER.info(
|
|
580
|
+
_LOGGER.info("Removed input %s (type_changed=%s)", input_id, input_id in inputs_type_changed)
|
|
532
581
|
|
|
533
582
|
# Check for area changes on remaining inputs
|
|
534
583
|
for input_id, input_device in self._inputs.items():
|
|
@@ -537,7 +586,7 @@ class InputManager:
|
|
|
537
586
|
|
|
538
587
|
if old_area != new_area:
|
|
539
588
|
ha_discovery_changed = True
|
|
540
|
-
_LOGGER.info(
|
|
589
|
+
_LOGGER.info("Input %s area changed: %s -> %s, removing old HA Discovery", input_id, old_area, new_area)
|
|
541
590
|
self._remove_input_ha_discovery(input_id, old_area)
|
|
542
591
|
|
|
543
592
|
# Wait for HA to process the removal before sending new discovery
|
|
@@ -547,6 +596,7 @@ class InputManager:
|
|
|
547
596
|
|
|
548
597
|
# _configure_inputs with reload_config=True will:
|
|
549
598
|
# - Update existing inputs (actions, area, name)
|
|
599
|
+
# - Create new inputs for type-changed entries (old objects removed above)
|
|
550
600
|
# - Add new inputs if their GPIO pin is already registered
|
|
551
601
|
self._configure_inputs(reload_config=True)
|
|
552
602
|
|
boneio/hardware/display/oled.py
CHANGED
boneio/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# flake8: noqa
|
|
2
|
-
__version__ = "1.4.
|
|
2
|
+
__version__ = "1.4.0dev14"
|