boneIO 0.8.0.dev8__tar.gz → 0.8.0.dev10__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.8.0.dev8 → boneIO-0.8.0.dev10}/PKG-INFO +1 -1
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/bonecli.py +57 -7
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/filter.py +3 -2
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/gpio.py +29 -8
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/loader.py +1 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/stats.py +8 -6
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/client.py +9 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/cwt.json +1 -1
- boneIO-0.8.0.dev10/boneio/modbus/liquid-sensor.json +37 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/modbuscli.py +91 -18
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/orno-or-we-517.json +8 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/r4dcb08.json +7 -1
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/sensor.py +16 -2
- boneIO-0.8.0.dev10/boneio/modbus/sht20.json +37 -0
- boneIO-0.8.0.dev8/boneio/modbus/socomec_e23.json → boneIO-0.8.0.dev10/boneio/modbus/socomec_e03.json +1 -1
- boneIO-0.8.0.dev10/boneio/modbus/socomec_e23.json +164 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/schema.yaml +14 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/gpio_new.py +18 -4
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/ina219.py +9 -13
- boneIO-0.8.0.dev10/boneio/version.py +2 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/pyproject.toml +1 -1
- boneIO-0.8.0.dev8/boneio/version.py +0 -2
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/LICENSE +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/README.md +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/const.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/cover.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/adc.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/binary_sensor.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/binary_sensor_v_0_7.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/config.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/cover.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/event.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/event_all.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/event_v_0_7.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/led32x4A.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/output24x16A.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/output24x16A_v0.3.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/output32x10A.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/example_config/output32x5A.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/fonts/danube__.ttf +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/group/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/group/output.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/async_updater.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/click_timer.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/config.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/events.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/exceptions.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/ha_discovery.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/logger.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/mqtt.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/oled.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/onewire/W1ThermSensor.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/onewire/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/onewire/ds2482.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/onewire/onewire.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/pcf8575.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/queue.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/sensor/ina_219_smbus.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/state_manager.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/timeperiod.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/util.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/helper/yaml_util.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/input/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/input/gpio.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/input/gpio_new.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/manager.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/dts1964_3f.json +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/pt100.json +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/sdm120.json +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/sdm630.json +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/sofar.json +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/modbus/utils.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/mqtt_client.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/oled.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/relay/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/relay/basic.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/relay/gpio.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/relay/mcp.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/relay/pca.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/relay/pcf.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/runner.py +1 -1
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/actions.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/actions_sensor.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/actions_switch.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/filters.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/filters_adc.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/id.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/temp_unit.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/schema/update_interval.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/adc.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/gpio.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/temp/__init__.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/temp/dallas.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/temp/lm75.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/boneio/sensor/temp/mcp9808.py +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/tests/32_5_config.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/tests/bandit.yaml +0 -0
- {boneIO-0.8.0.dev8 → boneIO-0.8.0.dev10}/tests/relay_32_5.py +0 -0
|
@@ -13,7 +13,11 @@ import sys
|
|
|
13
13
|
from colorlog import ColoredFormatter
|
|
14
14
|
from yaml import MarkedYAMLError
|
|
15
15
|
|
|
16
|
-
from boneio.modbus.modbuscli import
|
|
16
|
+
from boneio.modbus.modbuscli import (
|
|
17
|
+
async_run_modbus_set,
|
|
18
|
+
async_run_modbus_get,
|
|
19
|
+
async_run_modbus_search,
|
|
20
|
+
)
|
|
17
21
|
from boneio.modbus.client import VALUE_TYPES
|
|
18
22
|
|
|
19
23
|
|
|
@@ -99,13 +103,14 @@ def get_arguments() -> argparse.Namespace:
|
|
|
99
103
|
modbus_parser.add_argument(
|
|
100
104
|
"--address",
|
|
101
105
|
type=lambda x: int(x, 0),
|
|
102
|
-
required=
|
|
106
|
+
required=False,
|
|
107
|
+
default=1,
|
|
103
108
|
help="Current device address (hex or integer)",
|
|
104
109
|
)
|
|
105
110
|
modbus_parser.add_argument(
|
|
106
111
|
"--baudrate",
|
|
107
112
|
type=int,
|
|
108
|
-
choices=[2400, 4800, 9600, 19200],
|
|
113
|
+
choices=[2400, 4800, 9600, 14400, 19200],
|
|
109
114
|
required=True,
|
|
110
115
|
help="Current baudrate",
|
|
111
116
|
)
|
|
@@ -139,11 +144,21 @@ def get_arguments() -> argparse.Namespace:
|
|
|
139
144
|
dest="modbus_action", required=True
|
|
140
145
|
)
|
|
141
146
|
set_modbus_parser = modbus_sub_parser.add_parser("set")
|
|
147
|
+
set_modbus_parser.add_argument(
|
|
148
|
+
"--custom-value",
|
|
149
|
+
type=int,
|
|
150
|
+
help="Set Custom value",
|
|
151
|
+
)
|
|
152
|
+
set_modbus_parser.add_argument(
|
|
153
|
+
"--custom-register-address",
|
|
154
|
+
type=int,
|
|
155
|
+
help="Register address for custom value",
|
|
156
|
+
)
|
|
142
157
|
set_modbus_parser.add_argument(
|
|
143
158
|
"--device",
|
|
144
159
|
type=str,
|
|
145
|
-
choices=["cwt", "r4dcb08"],
|
|
146
|
-
help="Choose device to set modbus address/baudrate",
|
|
160
|
+
choices=["cwt", "r4dcb08", "liquid-sensor", "sht20", "custom"],
|
|
161
|
+
help="Choose device to set modbus address/baudrate. For custom you must provide --custom-value and --custom-register-address",
|
|
147
162
|
required=True,
|
|
148
163
|
)
|
|
149
164
|
set_modbus_parser_group = set_modbus_parser.add_mutually_exclusive_group()
|
|
@@ -182,6 +197,23 @@ def get_arguments() -> argparse.Namespace:
|
|
|
182
197
|
required=True,
|
|
183
198
|
)
|
|
184
199
|
parser.add_argument("--version", action="version", version=__version__)
|
|
200
|
+
search_modbus_parser = modbus_sub_parser.add_parser(
|
|
201
|
+
name="search",
|
|
202
|
+
help="Search for device. Iterate over every address 1-253 with provided register address",
|
|
203
|
+
)
|
|
204
|
+
search_modbus_parser.add_argument(
|
|
205
|
+
"--register-address",
|
|
206
|
+
type=int,
|
|
207
|
+
help="Register address",
|
|
208
|
+
required=True,
|
|
209
|
+
)
|
|
210
|
+
search_modbus_parser.add_argument(
|
|
211
|
+
"--register-type",
|
|
212
|
+
type=str,
|
|
213
|
+
choices=["input", "holding"],
|
|
214
|
+
help="Register type",
|
|
215
|
+
required=True,
|
|
216
|
+
)
|
|
185
217
|
arguments = parser.parse_args()
|
|
186
218
|
|
|
187
219
|
return arguments
|
|
@@ -223,6 +255,7 @@ def run_modbus_command(
|
|
|
223
255
|
_LOGGER.info("BoneIO %s starting.", __version__)
|
|
224
256
|
try:
|
|
225
257
|
configure_logger(log_config={}, debug=args.debug)
|
|
258
|
+
ret = 0
|
|
226
259
|
if args.modbus_action == "set":
|
|
227
260
|
ret = asyncio.run(
|
|
228
261
|
async_run_modbus_set(
|
|
@@ -235,9 +268,11 @@ def run_modbus_command(
|
|
|
235
268
|
stopbits=args.stopbits,
|
|
236
269
|
new_baudrate=args.new_baudrate,
|
|
237
270
|
new_address=args.new_address,
|
|
271
|
+
custom_address=args.custom_register_address,
|
|
272
|
+
custom_value=args.custom_value,
|
|
238
273
|
),
|
|
239
274
|
)
|
|
240
|
-
|
|
275
|
+
elif args.modbus_action == "get":
|
|
241
276
|
ret = asyncio.run(
|
|
242
277
|
async_run_modbus_get(
|
|
243
278
|
uart=args.uart,
|
|
@@ -251,6 +286,18 @@ def run_modbus_command(
|
|
|
251
286
|
value_type=args.value_type,
|
|
252
287
|
),
|
|
253
288
|
)
|
|
289
|
+
else:
|
|
290
|
+
ret = asyncio.run(
|
|
291
|
+
async_run_modbus_search(
|
|
292
|
+
uart=args.uart,
|
|
293
|
+
baudrate=args.baudrate,
|
|
294
|
+
register_address=args.register_address,
|
|
295
|
+
register_type=args.register_type,
|
|
296
|
+
parity=args.parity,
|
|
297
|
+
bytesize=args.bytesize,
|
|
298
|
+
stopbits=args.stopbits,
|
|
299
|
+
),
|
|
300
|
+
)
|
|
254
301
|
return ret
|
|
255
302
|
except (RestartRequestException, GracefulExit) as err:
|
|
256
303
|
if err is not None:
|
|
@@ -281,7 +328,10 @@ def main() -> int:
|
|
|
281
328
|
args=args,
|
|
282
329
|
)
|
|
283
330
|
|
|
284
|
-
|
|
331
|
+
if exit_code == 0:
|
|
332
|
+
_LOGGER.info("Exiting with exit code %s", exit_code)
|
|
333
|
+
else:
|
|
334
|
+
_LOGGER.error("Exiting with exit code %s", exit_code)
|
|
285
335
|
return exit_code
|
|
286
336
|
|
|
287
337
|
|
|
@@ -19,8 +19,9 @@ FILTERS = {
|
|
|
19
19
|
class Filter:
|
|
20
20
|
_filters = []
|
|
21
21
|
|
|
22
|
-
def _apply_filters(self, value: float) -> float | None:
|
|
23
|
-
|
|
22
|
+
def _apply_filters(self, value: float, filters: list = []) -> float | None:
|
|
23
|
+
filters = filters if filters else self._filters
|
|
24
|
+
for filter in filters:
|
|
24
25
|
for k, v in filter.items():
|
|
25
26
|
if k not in FILTERS:
|
|
26
27
|
_LOGGER.warning(
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import logging
|
|
4
4
|
from datetime import datetime
|
|
5
|
+
|
|
5
6
|
try:
|
|
6
7
|
from Adafruit_BBIO import GPIO
|
|
7
8
|
except ModuleNotFoundError:
|
|
@@ -21,7 +22,14 @@ from typing import Callable, Awaitable, List
|
|
|
21
22
|
|
|
22
23
|
from boneio.const import CONFIG_PIN, FALLING
|
|
23
24
|
from boneio.const import GPIO as GPIO_STR
|
|
24
|
-
from boneio.const import
|
|
25
|
+
from boneio.const import (
|
|
26
|
+
GPIO_MODE,
|
|
27
|
+
LOW,
|
|
28
|
+
ClickTypes,
|
|
29
|
+
Gpio_Edges,
|
|
30
|
+
Gpio_States,
|
|
31
|
+
InputTypes,
|
|
32
|
+
)
|
|
25
33
|
from boneio.helper.exceptions import GPIOInputException
|
|
26
34
|
from boneio.helper.timeperiod import TimePeriod
|
|
27
35
|
from concurrent.futures import ThreadPoolExecutor
|
|
@@ -79,7 +87,9 @@ def edge_detect(
|
|
|
79
87
|
) -> None:
|
|
80
88
|
"""Add detection for RISING and FALLING events."""
|
|
81
89
|
try:
|
|
82
|
-
GPIO.add_event_detect(
|
|
90
|
+
GPIO.add_event_detect(
|
|
91
|
+
gpio=pin, edge=edge, callback=callback, bouncetime=bounce
|
|
92
|
+
)
|
|
83
93
|
except RuntimeError as err:
|
|
84
94
|
raise GPIOInputException(err)
|
|
85
95
|
|
|
@@ -107,7 +117,8 @@ class GpioBaseClass:
|
|
|
107
117
|
self,
|
|
108
118
|
pin: str,
|
|
109
119
|
press_callback: Callable[
|
|
110
|
-
[ClickTypes, str, List, InputTypes, bool, float | None],
|
|
120
|
+
[ClickTypes, str, List, InputTypes, bool, float | None],
|
|
121
|
+
Awaitable[None],
|
|
111
122
|
],
|
|
112
123
|
name: str,
|
|
113
124
|
actions: dict,
|
|
@@ -118,7 +129,9 @@ class GpioBaseClass:
|
|
|
118
129
|
"""Setup GPIO Input Button"""
|
|
119
130
|
self._pin = pin
|
|
120
131
|
gpio_mode = kwargs.get(GPIO_MODE, GPIO_STR)
|
|
121
|
-
bounce_time: TimePeriod = kwargs.get(
|
|
132
|
+
bounce_time: TimePeriod = kwargs.get(
|
|
133
|
+
"bounce_time", TimePeriod(milliseconds=50)
|
|
134
|
+
)
|
|
122
135
|
self._bounce_time = bounce_time.total_in_seconds
|
|
123
136
|
self._loop = asyncio.get_running_loop()
|
|
124
137
|
self._press_callback = press_callback
|
|
@@ -129,14 +142,22 @@ class GpioBaseClass:
|
|
|
129
142
|
self._input_type = input_type
|
|
130
143
|
self._empty_message_after = empty_message_after
|
|
131
144
|
|
|
132
|
-
def press_callback(
|
|
145
|
+
def press_callback(
|
|
146
|
+
self, click_type: ClickTypes, duration: float | None = None
|
|
147
|
+
) -> None:
|
|
133
148
|
actions = self._actions.get(click_type, [])
|
|
134
|
-
self._loop.create_task(
|
|
149
|
+
self._loop.create_task(
|
|
150
|
+
self.async_press_callback(click_type, duration, actions)
|
|
151
|
+
)
|
|
135
152
|
|
|
136
153
|
async def async_press_callback(
|
|
137
|
-
self,
|
|
154
|
+
self,
|
|
155
|
+
click_type: ClickTypes,
|
|
156
|
+
duration: float | None = None,
|
|
157
|
+
actions: List = [],
|
|
138
158
|
) -> None:
|
|
139
|
-
|
|
159
|
+
|
|
160
|
+
_LOGGER.warning("press callback %s", datetime.now())
|
|
140
161
|
await self._press_callback(
|
|
141
162
|
click_type,
|
|
142
163
|
self._pin,
|
|
@@ -211,6 +211,7 @@ def create_modbus_sensors(manager: Manager, sensors, **kwargs) -> None:
|
|
|
211
211
|
update_interval=sensor.get(
|
|
212
212
|
UPDATE_INTERVAL, TimePeriod(seconds=60)
|
|
213
213
|
),
|
|
214
|
+
sensors_filters=sensor.get("sensors_filters", {}),
|
|
214
215
|
**kwargs,
|
|
215
216
|
)
|
|
216
217
|
except FileNotFoundError as err:
|
|
@@ -233,13 +233,15 @@ class HostData:
|
|
|
233
233
|
},
|
|
234
234
|
}
|
|
235
235
|
if ina219 is not None:
|
|
236
|
+
|
|
237
|
+
def get_ina_values():
|
|
238
|
+
return {
|
|
239
|
+
sensor.device_class: f"{sensor.state} {sensor.unit_of_measurement}"
|
|
240
|
+
for sensor in ina219.sensors.values()
|
|
241
|
+
}
|
|
242
|
+
|
|
236
243
|
host_stats[INA219] = {
|
|
237
|
-
"f":
|
|
238
|
-
*{
|
|
239
|
-
sensor.device_class: sensor.state
|
|
240
|
-
for sensor in ina219.sensors.values()
|
|
241
|
-
}
|
|
242
|
-
},
|
|
244
|
+
"f": get_ina_values,
|
|
243
245
|
"update_interval": TimePeriod(seconds=60),
|
|
244
246
|
}
|
|
245
247
|
self._data = {}
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
import struct
|
|
6
|
+
import time
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
9
|
from pymodbus.client.sync import BaseModbusClient, ModbusSerialClient
|
|
@@ -43,6 +44,7 @@ class Modbus:
|
|
|
43
44
|
stopbits: int = 1,
|
|
44
45
|
bytesize: int = 8,
|
|
45
46
|
parity: str = "N",
|
|
47
|
+
timeout: float = 3,
|
|
46
48
|
) -> None:
|
|
47
49
|
"""Initialize the Modbus hub."""
|
|
48
50
|
rx = uart.get(RX)
|
|
@@ -68,6 +70,7 @@ class Modbus:
|
|
|
68
70
|
stopbits=stopbits,
|
|
69
71
|
bytesize=bytesize,
|
|
70
72
|
parity=parity,
|
|
73
|
+
timeout=timeout,
|
|
71
74
|
)
|
|
72
75
|
self._read_methods = {
|
|
73
76
|
"input": self._client.read_input_registers,
|
|
@@ -129,6 +132,7 @@ class Modbus:
|
|
|
129
132
|
) -> ModbusResponse:
|
|
130
133
|
"""Call async pymodbus."""
|
|
131
134
|
async with self._lock:
|
|
135
|
+
start_time = time.perf_counter()
|
|
132
136
|
if not self._pymodbus_connect():
|
|
133
137
|
_LOGGER.error("Can't connect to Modbus.")
|
|
134
138
|
return None
|
|
@@ -151,6 +155,11 @@ class Modbus:
|
|
|
151
155
|
if not hasattr(result, REGISTERS):
|
|
152
156
|
_LOGGER.error(str(result))
|
|
153
157
|
return None
|
|
158
|
+
end_time = time.perf_counter()
|
|
159
|
+
_LOGGER.debug(
|
|
160
|
+
"Time of execution of read_registers: %s ms.",
|
|
161
|
+
round((end_time - start_time) * 1000, 3),
|
|
162
|
+
)
|
|
154
163
|
return result
|
|
155
164
|
|
|
156
165
|
def decode_value(self, payload, value_type):
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"model": "Liquid sensor",
|
|
3
|
+
"set_base": {
|
|
4
|
+
"set_address_address": 0,
|
|
5
|
+
"set_baudrate": {
|
|
6
|
+
"address": 1,
|
|
7
|
+
"possible_baudrates": {
|
|
8
|
+
"1200": 0,
|
|
9
|
+
"2400": 1,
|
|
10
|
+
"4800": 2,
|
|
11
|
+
"9600": 3,
|
|
12
|
+
"19200": 4,
|
|
13
|
+
"38400": 5,
|
|
14
|
+
"57600": 6,
|
|
15
|
+
"115200": 7
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"registers_base": [
|
|
20
|
+
{
|
|
21
|
+
"base": 0,
|
|
22
|
+
"length": 2,
|
|
23
|
+
"register_type": "holding",
|
|
24
|
+
"registers": [
|
|
25
|
+
{
|
|
26
|
+
"name": "Immersion depth",
|
|
27
|
+
"address": 0,
|
|
28
|
+
"unit_of_measurement": "mm",
|
|
29
|
+
"state_class": "measurement",
|
|
30
|
+
"device_class": "humidity",
|
|
31
|
+
"value_type": "S_WORD",
|
|
32
|
+
"filters": [{ "multiply": 0.1 }]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
@@ -78,7 +78,7 @@ class ModbusHelper:
|
|
|
78
78
|
|
|
79
79
|
def set_connection_speed(self, new_baudrate: int) -> int:
|
|
80
80
|
baudrate_model = self._model[SET_BAUDRATE]
|
|
81
|
-
ind = baudrate_model["possible_baudrates"].
|
|
81
|
+
ind = baudrate_model["possible_baudrates"].get(str(new_baudrate))
|
|
82
82
|
if ind:
|
|
83
83
|
result = self._modbus.client.write_register(
|
|
84
84
|
address=baudrate_model["address"],
|
|
@@ -110,6 +110,19 @@ class ModbusHelper:
|
|
|
110
110
|
else:
|
|
111
111
|
_LOGGER.error("Invalid new address.")
|
|
112
112
|
|
|
113
|
+
def set_custom_command(self, register_address: int, value: int | float):
|
|
114
|
+
result = self._modbus.client.write_register(
|
|
115
|
+
address=register_address,
|
|
116
|
+
value=value,
|
|
117
|
+
unit=self._device_address,
|
|
118
|
+
)
|
|
119
|
+
if result.isError():
|
|
120
|
+
_LOGGER.error("Operation failed.")
|
|
121
|
+
else:
|
|
122
|
+
_LOGGER.info(
|
|
123
|
+
"Operation succeeded. Now restart device by disconnecting it."
|
|
124
|
+
)
|
|
125
|
+
|
|
113
126
|
|
|
114
127
|
async def async_run_modbus_set(
|
|
115
128
|
device: str,
|
|
@@ -118,6 +131,8 @@ async def async_run_modbus_set(
|
|
|
118
131
|
baudrate: int,
|
|
119
132
|
new_baudrate: int,
|
|
120
133
|
new_address: int,
|
|
134
|
+
custom_address: int,
|
|
135
|
+
custom_value: int,
|
|
121
136
|
stopbits: int = 1,
|
|
122
137
|
bytesize: int = 8,
|
|
123
138
|
parity: str = "N",
|
|
@@ -125,15 +140,21 @@ async def async_run_modbus_set(
|
|
|
125
140
|
"""Run Modbus Set Function."""
|
|
126
141
|
if new_address and new_baudrate:
|
|
127
142
|
_LOGGER.error("Can't set both methods new_address and new_baudrate.")
|
|
128
|
-
|
|
129
|
-
set_base =
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
custom_cmd = True if device == "custom" else False
|
|
144
|
+
set_base = {}
|
|
145
|
+
if not custom_cmd:
|
|
146
|
+
_db = open_json(path=os.path.dirname(__file__), model=device)
|
|
147
|
+
set_base = _db.get(SET_BASE, {})
|
|
148
|
+
first_reg_base = _db.get(REGISTERS_BASE, [])[0]
|
|
149
|
+
if not first_reg_base:
|
|
150
|
+
return False
|
|
151
|
+
first_record = first_reg_base.get(REGISTERS, [])[0]
|
|
152
|
+
_LOGGER.debug(
|
|
153
|
+
f"Connecting with params uart: {uart}, baudrate: {baudrate}, stopbits: {stopbits}, bytesize: {bytesize}, parity: {parity}."
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
first_record = {}
|
|
157
|
+
first_reg_base = {}
|
|
137
158
|
modbus_helper = ModbusHelper(
|
|
138
159
|
device=device,
|
|
139
160
|
uart=uart,
|
|
@@ -146,17 +167,69 @@ async def async_run_modbus_set(
|
|
|
146
167
|
bytesize=bytesize,
|
|
147
168
|
parity=parity,
|
|
148
169
|
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
170
|
+
|
|
171
|
+
async def default_action():
|
|
172
|
+
if not await modbus_helper.check_connection():
|
|
173
|
+
_LOGGER.error("Can't connect with sensor. Exiting")
|
|
174
|
+
return 1
|
|
175
|
+
if new_baudrate:
|
|
176
|
+
return modbus_helper.set_connection_speed(new_baudrate=new_baudrate)
|
|
177
|
+
if new_address:
|
|
178
|
+
modbus_helper.set_new_address(new_address=new_address)
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
if not custom_cmd:
|
|
182
|
+
_LOGGER.debug("Invoking default action.")
|
|
183
|
+
return await default_action()
|
|
184
|
+
elif custom_address is not None and custom_value is not None:
|
|
185
|
+
_LOGGER.debug("Invoking custom command action.")
|
|
186
|
+
modbus_helper.set_custom_command(
|
|
187
|
+
register_address=custom_address, value=custom_value
|
|
188
|
+
)
|
|
157
189
|
return 1
|
|
158
190
|
|
|
159
191
|
|
|
192
|
+
async def async_run_modbus_search(
|
|
193
|
+
uart: str,
|
|
194
|
+
baudrate: int,
|
|
195
|
+
register_address: int,
|
|
196
|
+
register_type: str = "holding",
|
|
197
|
+
stopbits: int = 1,
|
|
198
|
+
bytesize: int = 8,
|
|
199
|
+
parity: str = "N",
|
|
200
|
+
):
|
|
201
|
+
"""Run Modbus Seach Function."""
|
|
202
|
+
_modbus = Modbus(
|
|
203
|
+
uart=UARTS[uart],
|
|
204
|
+
baudrate=baudrate,
|
|
205
|
+
stopbits=stopbits,
|
|
206
|
+
bytesize=bytesize,
|
|
207
|
+
parity=parity,
|
|
208
|
+
timeout=0.1,
|
|
209
|
+
)
|
|
210
|
+
units_found = []
|
|
211
|
+
for unit_id in range(1, 248): # Modbus RTU address range is 1 to 247
|
|
212
|
+
_LOGGER.debug(f"Searching device at address {unit_id}.")
|
|
213
|
+
value = await _modbus.read_registers(
|
|
214
|
+
unit=unit_id,
|
|
215
|
+
address=register_address,
|
|
216
|
+
count=2,
|
|
217
|
+
method=register_type,
|
|
218
|
+
)
|
|
219
|
+
if value:
|
|
220
|
+
units_found.append(unit_id)
|
|
221
|
+
_LOGGER.info(f"Found device at address {unit_id}.")
|
|
222
|
+
else:
|
|
223
|
+
_LOGGER.debug(f"No device found at address {unit_id}.")
|
|
224
|
+
if units_found:
|
|
225
|
+
_LOGGER.info(
|
|
226
|
+
"Found devices: [%s]", ", ".join(str(x) for x in units_found)
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
_LOGGER.info("No devices found.")
|
|
230
|
+
return 0
|
|
231
|
+
|
|
232
|
+
|
|
160
233
|
async def async_run_modbus_get(
|
|
161
234
|
uart: str,
|
|
162
235
|
device_address: int,
|
|
@@ -29,6 +29,14 @@
|
|
|
29
29
|
"state_class": "measurement",
|
|
30
30
|
"device_class": "voltage",
|
|
31
31
|
"value_type": "FP32"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "Grid Frequency",
|
|
35
|
+
"address": 18,
|
|
36
|
+
"unit_of_measurement": "V",
|
|
37
|
+
"state_class": "measurement",
|
|
38
|
+
"device_class": "voltage",
|
|
39
|
+
"value_type": "FP32"
|
|
32
40
|
}
|
|
33
41
|
]
|
|
34
42
|
}
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
"set_address_address": 254,
|
|
5
5
|
"set_baudrate": {
|
|
6
6
|
"address": 255,
|
|
7
|
-
"possible_baudrates":
|
|
7
|
+
"possible_baudrates": {
|
|
8
|
+
"1200": 0,
|
|
9
|
+
"2400": 1,
|
|
10
|
+
"4800": 2,
|
|
11
|
+
"9600": 3,
|
|
12
|
+
"19200": 4
|
|
13
|
+
}
|
|
8
14
|
}
|
|
9
15
|
},
|
|
10
16
|
"registers_base": [
|
|
@@ -4,6 +4,8 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from boneio.helper.filter import Filter
|
|
7
9
|
from .utils import CONVERT_METHODS, REGISTERS_BASE, allowed_operations
|
|
8
10
|
from boneio.const import (
|
|
9
11
|
ADDRESS,
|
|
@@ -26,7 +28,7 @@ from boneio.helper.util import open_json
|
|
|
26
28
|
_LOGGER = logging.getLogger(__name__)
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
31
|
+
class ModbusSensor(BasicMqtt, AsyncUpdater, Filter):
|
|
30
32
|
"""Represent Modbus sensor in BoneIO."""
|
|
31
33
|
|
|
32
34
|
SensorClass = None
|
|
@@ -37,6 +39,7 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
37
39
|
modbus: Modbus,
|
|
38
40
|
address: str,
|
|
39
41
|
model: str,
|
|
42
|
+
sensors_filters: dict,
|
|
40
43
|
config_helper: ConfigHelper,
|
|
41
44
|
event_bus: EventBus,
|
|
42
45
|
id: str = DefaultName,
|
|
@@ -56,6 +59,9 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
56
59
|
self._address = address
|
|
57
60
|
self._discovery_sent = False
|
|
58
61
|
self._payload_online = OFFLINE
|
|
62
|
+
self._sensors_filters = {
|
|
63
|
+
k.lower(): v for k, v in sensors_filters.items()
|
|
64
|
+
}
|
|
59
65
|
event_bus.add_haonline_listener(target=self.set_payload_offline)
|
|
60
66
|
AsyncUpdater.__init__(self, **kwargs)
|
|
61
67
|
|
|
@@ -154,7 +160,9 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
154
160
|
method=data.get("register_type", "input"),
|
|
155
161
|
)
|
|
156
162
|
if self._payload_online == OFFLINE and values:
|
|
157
|
-
_LOGGER.info(
|
|
163
|
+
_LOGGER.info(
|
|
164
|
+
"Sending online payload about device %s.", self._name
|
|
165
|
+
)
|
|
158
166
|
self._payload_online = ONLINE
|
|
159
167
|
self._send_message(
|
|
160
168
|
topic=f"{self._config_helper.topic_prefix}/{self._id}{STATE}",
|
|
@@ -204,6 +212,12 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
204
212
|
if key in allowed_operations:
|
|
205
213
|
lamda_function = allowed_operations[key]
|
|
206
214
|
decoded_value = lamda_function(decoded_value, value)
|
|
215
|
+
name = register.get("name").replace(" ", "")
|
|
216
|
+
user_filters = self._sensors_filters.get(name.lower(), [])
|
|
217
|
+
decoded_value = self._apply_filters(
|
|
218
|
+
value=decoded_value,
|
|
219
|
+
filters=user_filters,
|
|
220
|
+
)
|
|
207
221
|
output[register.get("name").replace(" ", "")] = decoded_value
|
|
208
222
|
self._send_message(
|
|
209
223
|
topic=f"{self._send_topic}/{data[BASE]}",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"model": "SHT20 Temp and Humidity sensor",
|
|
3
|
+
"set_base": {
|
|
4
|
+
"set_address_address": 257,
|
|
5
|
+
"set_baudrate": {
|
|
6
|
+
"address": 258,
|
|
7
|
+
"possible_baudrates": { "9600": 9600, "14400": 14400, "19200": 19200 }
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"registers_base": [
|
|
11
|
+
{
|
|
12
|
+
"base": 1,
|
|
13
|
+
"length": 2,
|
|
14
|
+
"register_type": "input",
|
|
15
|
+
"registers": [
|
|
16
|
+
{
|
|
17
|
+
"name": "Temperature",
|
|
18
|
+
"address": 1,
|
|
19
|
+
"unit_of_measurement": "°C",
|
|
20
|
+
"state_class": "measurement",
|
|
21
|
+
"device_class": "temperature",
|
|
22
|
+
"value_type": "S_WORD",
|
|
23
|
+
"filters": [{ "multiply": 0.1 }]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "Humidity",
|
|
27
|
+
"address": 2,
|
|
28
|
+
"unit_of_measurement": "%",
|
|
29
|
+
"state_class": "measurement",
|
|
30
|
+
"device_class": "humidity",
|
|
31
|
+
"value_type": "S_WORD",
|
|
32
|
+
"filters": [{ "multiply": 0.1 }]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|