boneIO 0.7.0.dev2__tar.gz → 0.7.0.dev3__tar.gz
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-0.7.0.dev2 → boneIO-0.7.0.dev3}/PKG-INFO +1 -1
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/bonecli.py +4 -2
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/click_timer.py +2 -1
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/events.py +4 -7
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/gpio.py +2 -1
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/loader.py +19 -6
- boneIO-0.7.0.dev3/boneio/helper/pcf8575.py +13 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/input/gpio_beta.py +7 -7
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/manager.py +21 -17
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/mqtt_client.py +20 -22
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/relay/__init__.py +2 -1
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/relay/pcf.py +8 -6
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/runner.py +1 -5
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/schema.yaml +33 -1
- boneIO-0.7.0.dev3/boneio/version.py +2 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/pyproject.toml +1 -1
- boneIO-0.7.0.dev2/boneio/version.py +0 -2
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/LICENSE +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/README.md +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/const.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/cover.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/adc_v0_4.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/binary_sensor.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/config.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/cover.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/event.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/led32x4A.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/output24x16A.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/output24x16A_v0.3.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/example_config/output32x5A.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/fonts/danube__.ttf +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/async_updater.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/config.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/exceptions.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/filter.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/ha_discovery.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/logger.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/mqtt.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/oled.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/onewire/W1ThermSensor.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/onewire/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/onewire/ds2482.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/onewire/onewire.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/queue.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/state_manager.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/stats.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/timeperiod.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/util.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/helper/yaml_util.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/input/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/input/gpio.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/modbus.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/oled.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/relay/basic.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/relay/gpio.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/relay/mcp.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/relay/pca.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/actions.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/actions_sensor.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/actions_switch.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/filters.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/filters_adc.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/id.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/temp_unit.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/schema/update_interval.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/adc.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/gpio.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/gpio_beta.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/modbus/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/modbus/dts1964_3f.json +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/modbus/pt100.json +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/modbus/r4dcb08.json +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/modbus/sdm120.json +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/modbus/sdm630.json +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/modbus/sofar.json +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/temp/__init__.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/temp/dallas.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/temp/lm75.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/boneio/sensor/temp/mcp9808.py +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/tests/32_5_config.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/tests/bandit.yaml +0 -0
- {boneIO-0.7.0.dev2 → boneIO-0.7.0.dev3}/tests/relay_32_5.py +0 -0
|
@@ -15,6 +15,7 @@ from yaml import MarkedYAMLError
|
|
|
15
15
|
from boneio.const import ACTION
|
|
16
16
|
from boneio.helper import load_config_from_file
|
|
17
17
|
from boneio.helper.exceptions import ConfigurationException, RestartRequestException
|
|
18
|
+
from boneio.helper.events import GracefulExit
|
|
18
19
|
from boneio.helper.logger import configure_logger
|
|
19
20
|
from boneio.runner import async_run
|
|
20
21
|
from boneio.version import __version__
|
|
@@ -89,8 +90,9 @@ def run(config: str, debug: int, mqttusername: str = "", mqttpassword: str = "")
|
|
|
89
90
|
),
|
|
90
91
|
)
|
|
91
92
|
return 0
|
|
92
|
-
except RestartRequestException as err:
|
|
93
|
-
|
|
93
|
+
except (RestartRequestException, GracefulExit) as err:
|
|
94
|
+
if err is not None:
|
|
95
|
+
_LOGGER.info(err)
|
|
94
96
|
return 0
|
|
95
97
|
except (ConfigurationException, MarkedYAMLError) as err:
|
|
96
98
|
_LOGGER.error("Failed to load config. %s Exiting.", err)
|
|
@@ -24,9 +24,10 @@ class ClickTimer:
|
|
|
24
24
|
self._remove_listener = None
|
|
25
25
|
|
|
26
26
|
async def _start_async_timer(self) -> None:
|
|
27
|
+
start = time.time()
|
|
27
28
|
await asyncio.sleep(self._delay)
|
|
28
29
|
self._remove_listener = None
|
|
29
|
-
self._action(time.time())
|
|
30
|
+
self._action(round(time.time() - start, 2))
|
|
30
31
|
|
|
31
32
|
def start_timer(self) -> None:
|
|
32
33
|
"""Start timer."""
|
|
@@ -77,11 +77,9 @@ class ListenerJob:
|
|
|
77
77
|
class EventBus:
|
|
78
78
|
"""Simple event bus which ticks every second."""
|
|
79
79
|
|
|
80
|
-
def __init__(self,
|
|
80
|
+
def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
81
81
|
"""Initialize handler"""
|
|
82
|
-
self._loop = asyncio.get_event_loop()
|
|
83
|
-
self._last_will = last_will_mqtt
|
|
84
|
-
|
|
82
|
+
self._loop = loop or asyncio.get_event_loop()
|
|
85
83
|
self._listeners = {}
|
|
86
84
|
self._sigterm_listeners = []
|
|
87
85
|
self._haonline_listeners = []
|
|
@@ -89,7 +87,7 @@ class EventBus:
|
|
|
89
87
|
for signame in {"SIGINT", "SIGTERM"}:
|
|
90
88
|
self._loop.add_signal_handler(
|
|
91
89
|
getattr(signal, signame),
|
|
92
|
-
|
|
90
|
+
self.ask_exit,
|
|
93
91
|
)
|
|
94
92
|
|
|
95
93
|
def _run_second_event(self, time):
|
|
@@ -100,7 +98,7 @@ class EventBus:
|
|
|
100
98
|
self._loop.call_soon(listener.target, time)
|
|
101
99
|
)
|
|
102
100
|
|
|
103
|
-
|
|
101
|
+
def ask_exit(self):
|
|
104
102
|
"""Function to call on exit. Should invoke all sigterm listeners."""
|
|
105
103
|
_LOGGER.debug("Exiting process started.")
|
|
106
104
|
self._listeners = {}
|
|
@@ -108,7 +106,6 @@ class EventBus:
|
|
|
108
106
|
target()
|
|
109
107
|
self._timer_handle()
|
|
110
108
|
|
|
111
|
-
await self._last_will()
|
|
112
109
|
_LOGGER.info("Shutdown gracefully.")
|
|
113
110
|
raise GracefulExit(code=0)
|
|
114
111
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import asyncio
|
|
2
3
|
import logging
|
|
3
4
|
|
|
@@ -104,7 +105,7 @@ class GpioBaseClass:
|
|
|
104
105
|
"""Base class for initialize GPIO"""
|
|
105
106
|
|
|
106
107
|
def __init__(
|
|
107
|
-
self, pin: str, press_callback: Callable[[ClickTypes, str], None], **kwargs
|
|
108
|
+
self, pin: str, press_callback: Callable[[ClickTypes, str, bool | None], None], **kwargs
|
|
108
109
|
) -> None:
|
|
109
110
|
"""Setup GPIO Input Button"""
|
|
110
111
|
self._pin = pin
|
|
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Union
|
|
|
7
7
|
|
|
8
8
|
from adafruit_mcp230xx.mcp23017 import MCP23017
|
|
9
9
|
from adafruit_pca9685 import PCA9685
|
|
10
|
-
from adafruit_pcf8575 import PCF8575
|
|
11
10
|
|
|
12
11
|
from boneio.const import (
|
|
13
12
|
ACTIONS,
|
|
@@ -64,6 +63,7 @@ from boneio.helper.onewire import (
|
|
|
64
63
|
)
|
|
65
64
|
from boneio.helper.ha_discovery import ha_cover_availabilty_message
|
|
66
65
|
from boneio.helper.timeperiod import TimePeriod
|
|
66
|
+
from boneio.helper.pcf8575 import PCF8575
|
|
67
67
|
from boneio.input import GpioEventButton, GpioEventButtonBeta
|
|
68
68
|
from boneio.sensor import DallasSensorDS2482, GpioInputBinarySensor, GpioInputBinarySensorBeta
|
|
69
69
|
from boneio.sensor.temp.dallas import DallasSensorW1
|
|
@@ -74,7 +74,7 @@ if TYPE_CHECKING:
|
|
|
74
74
|
|
|
75
75
|
from busio import I2C
|
|
76
76
|
|
|
77
|
-
from boneio.relay import GpioRelay, MCPRelay, PWMPCA
|
|
77
|
+
from boneio.relay import GpioRelay, MCPRelay, PWMPCA, PCFRelay
|
|
78
78
|
from boneio.sensor import GpioADCSensor, initialize_adc
|
|
79
79
|
|
|
80
80
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -153,12 +153,14 @@ def create_temp_sensor(
|
|
|
153
153
|
_LOGGER.error("Can't configure Temp sensor. %s", err)
|
|
154
154
|
pass
|
|
155
155
|
|
|
156
|
+
|
|
156
157
|
expander_class = {
|
|
157
158
|
MCP: MCP23017,
|
|
158
159
|
PCA: PCA9685,
|
|
159
160
|
PCF: PCF8575
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
|
|
162
164
|
def create_expander(
|
|
163
165
|
expander_dict: dict,
|
|
164
166
|
expander_config: list,
|
|
@@ -187,7 +189,6 @@ def create_expander(
|
|
|
187
189
|
return grouped_outputs
|
|
188
190
|
|
|
189
191
|
|
|
190
|
-
|
|
191
192
|
def create_modbus_sensors(manager: Manager, sensors, **kwargs) -> None:
|
|
192
193
|
"""Create Modbus sensor for each device."""
|
|
193
194
|
from boneio.sensor.modbus import ModbusSensor
|
|
@@ -230,7 +231,7 @@ def output_chooser(output_kind: str, config):
|
|
|
230
231
|
return OutputEntry(PWMPCA, PCA, output_id)
|
|
231
232
|
elif output_kind == PCF:
|
|
232
233
|
output_id = config.pop(PCF_ID, None)
|
|
233
|
-
return OutputEntry(
|
|
234
|
+
return OutputEntry(PCFRelay, PCF, output_id)
|
|
234
235
|
else:
|
|
235
236
|
raise GPIOOutputException(f"""Output type {output_kind} dont exists""")
|
|
236
237
|
|
|
@@ -279,6 +280,17 @@ def configure_relay(
|
|
|
279
280
|
"pca_id": getattr(output, "output_id"),
|
|
280
281
|
"output_type": output_type,
|
|
281
282
|
}
|
|
283
|
+
elif getattr(output, "output_kind") == PCF:
|
|
284
|
+
expander = manager.pcf.get(getattr(output, "output_id"))
|
|
285
|
+
if not expander:
|
|
286
|
+
_LOGGER.error("No such PCF configured!")
|
|
287
|
+
return None
|
|
288
|
+
kwargs = {
|
|
289
|
+
"pin": int(config.pop(PIN)),
|
|
290
|
+
"expander": expander,
|
|
291
|
+
"expander_id": getattr(output, "output_id"),
|
|
292
|
+
"output_type": output_type,
|
|
293
|
+
}
|
|
282
294
|
elif getattr(output, "output_kind") == GPIO:
|
|
283
295
|
if GPIO not in manager.grouped_outputs:
|
|
284
296
|
manager.grouped_outputs[GPIO] = {}
|
|
@@ -337,12 +349,13 @@ def configure_event_sensor(
|
|
|
337
349
|
GpioEventButtonClass = GpioEventButton if gpio.get("detection_type", "stable") == "stable" else GpioEventButtonBeta
|
|
338
350
|
GpioEventButtonClass(
|
|
339
351
|
pin=pin,
|
|
340
|
-
press_callback=lambda x, i: press_callback(
|
|
352
|
+
press_callback=lambda x, i, z: press_callback(
|
|
341
353
|
x=x,
|
|
342
354
|
inpin=i,
|
|
343
355
|
actions=gpio.get(ACTIONS, {}).get(x, []),
|
|
344
356
|
input_type=INPUT,
|
|
345
|
-
empty_message_after=gpio.get("clear_message", False)
|
|
357
|
+
empty_message_after=gpio.get("clear_message", False),
|
|
358
|
+
duration=z
|
|
346
359
|
),
|
|
347
360
|
**gpio,
|
|
348
361
|
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from adafruit_pcf8575 import PCF8575 as AdafruitPCF8575
|
|
4
|
+
try:
|
|
5
|
+
from busio import I2C
|
|
6
|
+
except ImportError:
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PCF8575(AdafruitPCF8575):
|
|
11
|
+
|
|
12
|
+
def __init__(self, i2c: I2C, address: int, reset: bool) -> None:
|
|
13
|
+
super().__init__(i2c_bus=i2c, address=address)
|
|
@@ -19,6 +19,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
19
19
|
DOUBLE_CLICK_DURATION_MS = 300
|
|
20
20
|
LONG_PRESS_DURATION_MS = 600
|
|
21
21
|
|
|
22
|
+
|
|
22
23
|
class GpioEventButtonBeta(GpioBaseClass):
|
|
23
24
|
"""Represent Gpio input switch."""
|
|
24
25
|
|
|
@@ -38,7 +39,7 @@ class GpioEventButtonBeta(GpioBaseClass):
|
|
|
38
39
|
)
|
|
39
40
|
self._timer_long = ClickTimer(
|
|
40
41
|
delay=TimePeriod(milliseconds=LONG_PRESS_DURATION_MS),
|
|
41
|
-
action=lambda x: self.press_callback(click_type=LONG),
|
|
42
|
+
action=lambda x: self.press_callback(click_type=LONG, duration=x),
|
|
42
43
|
)
|
|
43
44
|
self._double_click_ran = False
|
|
44
45
|
self._is_waiting_for_second_click = False
|
|
@@ -51,7 +52,7 @@ class GpioEventButtonBeta(GpioBaseClass):
|
|
|
51
52
|
def double_click_press_callback(self):
|
|
52
53
|
self._is_waiting_for_second_click = False
|
|
53
54
|
if not self._state and not self._timer_long.is_waiting():
|
|
54
|
-
self.press_callback(click_type=SINGLE)
|
|
55
|
+
self.press_callback(click_type=SINGLE, duration=None)
|
|
55
56
|
|
|
56
57
|
def check_state(self, channel) -> None:
|
|
57
58
|
time_now = time.time()
|
|
@@ -64,7 +65,7 @@ class GpioEventButtonBeta(GpioBaseClass):
|
|
|
64
65
|
if self._timer_double.is_waiting():
|
|
65
66
|
self._timer_double.reset()
|
|
66
67
|
self._double_click_ran = True
|
|
67
|
-
self.press_callback(click_type=DOUBLE)
|
|
68
|
+
self.press_callback(click_type=DOUBLE, duration=None)
|
|
68
69
|
return
|
|
69
70
|
self._timer_double.start_timer()
|
|
70
71
|
self._is_waiting_for_second_click = True
|
|
@@ -72,13 +73,12 @@ class GpioEventButtonBeta(GpioBaseClass):
|
|
|
72
73
|
else:
|
|
73
74
|
if not self._is_waiting_for_second_click and not self._double_click_ran:
|
|
74
75
|
if self._timer_long.is_waiting():
|
|
75
|
-
self.press_callback(click_type=SINGLE)
|
|
76
|
+
self.press_callback(click_type=SINGLE, duration=None)
|
|
76
77
|
self._timer_long.reset()
|
|
77
78
|
self._double_click_ran = False
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
def press_callback(self, click_type: ClickTypes):
|
|
80
|
+
def press_callback(self, click_type: ClickTypes, duration: float | None = None):
|
|
81
81
|
self.click_count = 0
|
|
82
82
|
self._loop.call_soon_threadsafe(
|
|
83
|
-
partial(self._press_callback, click_type, self._pin)
|
|
83
|
+
partial(self._press_callback, click_type, self._pin, duration)
|
|
84
84
|
)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import asyncio
|
|
2
3
|
import logging
|
|
3
4
|
from collections import deque
|
|
@@ -86,7 +87,6 @@ class Manager:
|
|
|
86
87
|
stop_client: Callable[[], Awaitable[None]],
|
|
87
88
|
state_manager: StateManager,
|
|
88
89
|
config_helper: ConfigHelper,
|
|
89
|
-
event_bus: EventBus,
|
|
90
90
|
config_file_path: str,
|
|
91
91
|
relay_pins: List = [],
|
|
92
92
|
event_pins: List = [],
|
|
@@ -111,7 +111,7 @@ class Manager:
|
|
|
111
111
|
self._host_data = None
|
|
112
112
|
self._config_file_path = config_file_path
|
|
113
113
|
self._state_manager = state_manager
|
|
114
|
-
self._event_bus =
|
|
114
|
+
self._event_bus = EventBus(loop=self._loop)
|
|
115
115
|
|
|
116
116
|
self.send_message = send_message
|
|
117
117
|
self.stop_client = stop_client
|
|
@@ -145,21 +145,17 @@ class Manager:
|
|
|
145
145
|
)
|
|
146
146
|
self.grouped_outputs.update(
|
|
147
147
|
create_expander(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
153
|
-
)
|
|
148
|
+
expander_dict=self._pcf,
|
|
149
|
+
expander_config=pcf8575,
|
|
150
|
+
exp_type=PCF,
|
|
151
|
+
i2cbusio=self._i2cbusio))
|
|
154
152
|
self.grouped_outputs.update(
|
|
155
153
|
create_expander(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
)
|
|
162
|
-
|
|
154
|
+
expander_dict=self._pca,
|
|
155
|
+
expander_config=pca9685,
|
|
156
|
+
exp_type=PCA,
|
|
157
|
+
i2cbusio=self._i2cbusio))
|
|
158
|
+
|
|
163
159
|
self._configure_adc(adc_list=adc)
|
|
164
160
|
|
|
165
161
|
for _config in relay_pins:
|
|
@@ -484,12 +480,20 @@ class Manager:
|
|
|
484
480
|
return self._pcf
|
|
485
481
|
|
|
486
482
|
def press_callback(
|
|
487
|
-
self, x: ClickTypes, inpin: str, actions: List, input_type: InputTypes = INPUT, empty_message_after: bool = False
|
|
483
|
+
self, x: ClickTypes, inpin: str, actions: List, input_type: InputTypes = INPUT, empty_message_after: bool = False, duration: float | None = None
|
|
488
484
|
) -> None:
|
|
489
485
|
"""Press callback to use in input gpio.
|
|
490
486
|
If relay input map is provided also toggle action on relay or cover or mqtt."""
|
|
491
487
|
topic = f"{self._config_helper.topic_prefix}/{input_type}/{inpin}"
|
|
492
|
-
|
|
488
|
+
|
|
489
|
+
def generate_payload():
|
|
490
|
+
if input_type == INPUT:
|
|
491
|
+
if duration:
|
|
492
|
+
return {"event_type": x, "duration": duration}
|
|
493
|
+
return {"event_type": x}
|
|
494
|
+
return x
|
|
495
|
+
|
|
496
|
+
self.send_message(topic=topic, payload=generate_payload(), retain=False)
|
|
493
497
|
for action_definition in actions:
|
|
494
498
|
_LOGGER.debug("Executing action %s", action_definition)
|
|
495
499
|
if action_definition[ACTION] == OUTPUT:
|
|
@@ -15,9 +15,10 @@ from aiomqtt import Client as AsyncioClient, MqttError, Will
|
|
|
15
15
|
from paho.mqtt.properties import Properties
|
|
16
16
|
from paho.mqtt.subscribeoptions import SubscribeOptions
|
|
17
17
|
|
|
18
|
-
from boneio.const import
|
|
18
|
+
from boneio.const import OFFLINE, PAHO, STATE
|
|
19
19
|
from boneio.helper import UniqueQueue
|
|
20
20
|
from boneio.helper.config import ConfigHelper
|
|
21
|
+
from boneio.helper.events import GracefulExit
|
|
21
22
|
from boneio.manager import Manager
|
|
22
23
|
from boneio.helper.exceptions import RestartRequestException
|
|
23
24
|
|
|
@@ -58,7 +59,7 @@ class MQTTClient:
|
|
|
58
59
|
self.port,
|
|
59
60
|
will=Will(
|
|
60
61
|
topic=f"{self._config_helper.topic_prefix}/{STATE}",
|
|
61
|
-
payload=
|
|
62
|
+
payload=OFFLINE,
|
|
62
63
|
qos=0,
|
|
63
64
|
retain=False,
|
|
64
65
|
),
|
|
@@ -148,21 +149,23 @@ class MQTTClient:
|
|
|
148
149
|
async def start_client(self, manager: Manager) -> None:
|
|
149
150
|
"""Start the client with the manager."""
|
|
150
151
|
# Reconnect automatically until the client is stopped.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
152
|
+
try:
|
|
153
|
+
while True:
|
|
154
|
+
try:
|
|
155
|
+
await self._subscribe_manager(manager)
|
|
156
|
+
except MqttError as err:
|
|
157
|
+
self.reconnect_interval = min(self.reconnect_interval * 2, 900)
|
|
158
|
+
_LOGGER.error(
|
|
159
|
+
"MQTT error: %s. Reconnecting in %s seconds",
|
|
160
|
+
err,
|
|
161
|
+
self.reconnect_interval,
|
|
162
|
+
)
|
|
163
|
+
self._connection_established = False
|
|
164
|
+
await asyncio.sleep(self.reconnect_interval)
|
|
165
|
+
self.create_client() # reset connect/reconnect futures
|
|
166
|
+
except (asyncio.CancelledError, GracefulExit):
|
|
167
|
+
_LOGGER.info("MQTT client task canceled.")
|
|
168
|
+
pass
|
|
166
169
|
|
|
167
170
|
async def stop_client(self) -> None:
|
|
168
171
|
await self.unsubscribe(
|
|
@@ -170,11 +173,6 @@ class MQTTClient:
|
|
|
170
173
|
)
|
|
171
174
|
raise RestartRequestException("Restart requested.")
|
|
172
175
|
|
|
173
|
-
async def last_will_mqtt(self) -> None:
|
|
174
|
-
_LOGGER.info("Sending offline state.")
|
|
175
|
-
topic = f"{self._config_helper.topic_prefix}/{STATE}"
|
|
176
|
-
self.send_message(topic=topic, payload=OFFLINE, retain=True)
|
|
177
|
-
|
|
178
176
|
async def _subscribe_manager(self, manager: Manager) -> None:
|
|
179
177
|
"""Connect and subscribe to manager topics + host stats."""
|
|
180
178
|
async with AsyncExitStack() as stack:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Relay module."""
|
|
2
2
|
from boneio.relay.gpio import GpioRelay
|
|
3
3
|
from boneio.relay.mcp import MCPRelay
|
|
4
|
+
from boneio.relay.pcf import PCFRelay
|
|
4
5
|
from boneio.relay.pca import PWMPCA
|
|
5
6
|
|
|
6
|
-
__all__ = ["MCPRelay", "GpioRelay", "PWMPCA"]
|
|
7
|
+
__all__ = ["MCPRelay", "GpioRelay", "PWMPCA", "PCFRelay"]
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
-
from adafruit_pcf8575 import
|
|
5
|
+
from adafruit_pcf8575 import DigitalInOut
|
|
6
6
|
|
|
7
7
|
from boneio.const import NONE, SWITCH, PCF
|
|
8
8
|
from boneio.helper.events import async_track_point_in_time, utcnow
|
|
9
|
+
from boneio.helper.pcf8575 import PCF8575
|
|
9
10
|
from boneio.relay.basic import BasicRelay
|
|
10
11
|
|
|
11
12
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -17,14 +18,14 @@ class PCFRelay(BasicRelay):
|
|
|
17
18
|
def __init__(
|
|
18
19
|
self,
|
|
19
20
|
pin: int,
|
|
20
|
-
|
|
21
|
+
expander: PCF8575,
|
|
21
22
|
expander_id: str,
|
|
22
23
|
output_type: str = SWITCH,
|
|
23
24
|
restored_state: bool = False,
|
|
24
25
|
**kwargs,
|
|
25
26
|
) -> None:
|
|
26
27
|
"""Initialize MCP relay."""
|
|
27
|
-
self._pin: DigitalInOut =
|
|
28
|
+
self._pin: DigitalInOut = expander.get_pin(pin)
|
|
28
29
|
if output_type == NONE:
|
|
29
30
|
"""Just in case to not restore state of covers etc."""
|
|
30
31
|
restored_state = False
|
|
@@ -34,6 +35,7 @@ class PCFRelay(BasicRelay):
|
|
|
34
35
|
)
|
|
35
36
|
self._pin_id = pin
|
|
36
37
|
self._expander_id = expander_id
|
|
38
|
+
self._active_state = False
|
|
37
39
|
_LOGGER.debug("Setup PCF with pin %s", self._pin_id)
|
|
38
40
|
|
|
39
41
|
@property
|
|
@@ -54,7 +56,7 @@ class PCFRelay(BasicRelay):
|
|
|
54
56
|
@property
|
|
55
57
|
def is_active(self) -> bool:
|
|
56
58
|
"""Is relay active."""
|
|
57
|
-
return self.pin.value
|
|
59
|
+
return self.pin.value == self._active_state
|
|
58
60
|
|
|
59
61
|
@property
|
|
60
62
|
def pin(self) -> str:
|
|
@@ -63,7 +65,7 @@ class PCFRelay(BasicRelay):
|
|
|
63
65
|
|
|
64
66
|
def turn_on(self) -> None:
|
|
65
67
|
"""Call turn on action."""
|
|
66
|
-
self.pin.value =
|
|
68
|
+
self.pin.value = self._active_state
|
|
67
69
|
if self._momentary_turn_on:
|
|
68
70
|
async_track_point_in_time(
|
|
69
71
|
loop=self._loop,
|
|
@@ -74,7 +76,7 @@ class PCFRelay(BasicRelay):
|
|
|
74
76
|
|
|
75
77
|
def turn_off(self) -> None:
|
|
76
78
|
"""Call turn off action."""
|
|
77
|
-
self.pin.value =
|
|
79
|
+
self.pin.value = not self._active_state
|
|
78
80
|
if self._momentary_turn_off:
|
|
79
81
|
async_track_point_in_time(
|
|
80
82
|
loop=self._loop,
|
|
@@ -59,8 +59,6 @@ async def async_run(
|
|
|
59
59
|
mqttpassword: str = "",
|
|
60
60
|
) -> list[Any]:
|
|
61
61
|
"""Run BoneIO."""
|
|
62
|
-
_loop = asyncio.get_event_loop()
|
|
63
|
-
|
|
64
62
|
_config_helper = ConfigHelper(
|
|
65
63
|
topic_prefix=config[MQTT].pop(TOPIC_PREFIX),
|
|
66
64
|
ha_discovery=config[MQTT][HA_DISCOVERY].pop(ENABLED),
|
|
@@ -74,7 +72,6 @@ async def async_run(
|
|
|
74
72
|
port=config[MQTT].get(PORT, 1883),
|
|
75
73
|
config_helper=_config_helper,
|
|
76
74
|
)
|
|
77
|
-
event_bus = EventBus(last_will_mqtt=client.last_will_mqtt)
|
|
78
75
|
manager_kwargs = {
|
|
79
76
|
item["name"]: config.get(item["name"], item["default"])
|
|
80
77
|
for item in config_modules
|
|
@@ -83,7 +80,6 @@ async def async_run(
|
|
|
83
80
|
manager = Manager(
|
|
84
81
|
send_message=client.send_message,
|
|
85
82
|
stop_client=client.stop_client,
|
|
86
|
-
event_bus=event_bus,
|
|
87
83
|
relay_pins=config.get(OUTPUT, []),
|
|
88
84
|
event_pins=config.get(EVENT_ENTITY, []),
|
|
89
85
|
binary_pins=config.get(BINARY_SENSOR, []),
|
|
@@ -104,4 +100,4 @@ async def async_run(
|
|
|
104
100
|
tasks.update(manager.get_tasks())
|
|
105
101
|
_LOGGER.info("Connecting to MQTT.")
|
|
106
102
|
tasks.add(client.start_client(manager))
|
|
107
|
-
return await asyncio.gather(*tasks)
|
|
103
|
+
return await asyncio.gather(*tasks, return_exceptions=True)
|
|
@@ -218,6 +218,33 @@ mcp23017:
|
|
|
218
218
|
meta:
|
|
219
219
|
label: How long to sleep for MCP to initialize.
|
|
220
220
|
|
|
221
|
+
|
|
222
|
+
pcf8575:
|
|
223
|
+
type: list
|
|
224
|
+
required: False
|
|
225
|
+
meta:
|
|
226
|
+
label: pcf8575 Pin config
|
|
227
|
+
schema:
|
|
228
|
+
type: dict
|
|
229
|
+
schema:
|
|
230
|
+
id: !include id.yaml
|
|
231
|
+
address:
|
|
232
|
+
type: integer
|
|
233
|
+
required: True
|
|
234
|
+
meta:
|
|
235
|
+
label: GPIO of I2C SDA
|
|
236
|
+
init_sleep:
|
|
237
|
+
type:
|
|
238
|
+
- string
|
|
239
|
+
- timeperiod
|
|
240
|
+
coerce:
|
|
241
|
+
- str
|
|
242
|
+
- positive_time_period
|
|
243
|
+
required: True
|
|
244
|
+
default: 0s
|
|
245
|
+
meta:
|
|
246
|
+
label: How long to sleep for PCF to initialize.
|
|
247
|
+
|
|
221
248
|
pca9685:
|
|
222
249
|
type: list
|
|
223
250
|
required: False
|
|
@@ -260,7 +287,7 @@ output:
|
|
|
260
287
|
label: Id to send to use in HA and in GPIO Input. Default to `kind_pin`
|
|
261
288
|
kind:
|
|
262
289
|
type: string
|
|
263
|
-
allowed: ["gpio", "mcp", "pca"]
|
|
290
|
+
allowed: ["gpio", "mcp", "pca", "pcf"]
|
|
264
291
|
meta:
|
|
265
292
|
label: Either GPIO or i2c.
|
|
266
293
|
mcp_id:
|
|
@@ -273,6 +300,11 @@ output:
|
|
|
273
300
|
required: False
|
|
274
301
|
meta:
|
|
275
302
|
label: PCA ID to connect
|
|
303
|
+
pcf_id:
|
|
304
|
+
type: string
|
|
305
|
+
required: False
|
|
306
|
+
meta:
|
|
307
|
+
label: PCF ID to connect
|
|
276
308
|
pin:
|
|
277
309
|
type:
|
|
278
310
|
- string
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|