boneIO 0.7.2.dev4__py3-none-any.whl → 0.8.0.dev2__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-0.7.2.dev4.dist-info → boneIO-0.8.0.dev2.dist-info}/METADATA +1 -1
- {boneIO-0.7.2.dev4.dist-info → boneIO-0.8.0.dev2.dist-info}/RECORD +28 -23
- boneio/bonecli.py +153 -16
- boneio/helper/filter.py +4 -1
- boneio/helper/loader.py +21 -8
- boneio/helper/mqtt.py +5 -1
- boneio/helper/state_manager.py +8 -4
- boneio/helper/util.py +16 -2
- boneio/manager.py +1 -1
- boneio/modbus/__init__.py +5 -0
- boneio/{modbus.py → modbus/client.py} +19 -3
- boneio/modbus/cwt.json +34 -0
- boneio/modbus/modbuscli.py +175 -0
- boneio/modbus/orno-or-we-517.json +20 -0
- boneio/{sensor/modbus/__init__.py → modbus/sensor.py} +9 -87
- boneio/{sensor/modbus → modbus}/socomec_e23.json +7 -6
- boneio/modbus/utils.py +73 -0
- boneio/schema/schema.yaml +2 -1
- boneio/version.py +1 -1
- {boneIO-0.7.2.dev4.dist-info → boneIO-0.8.0.dev2.dist-info}/WHEEL +0 -0
- {boneIO-0.7.2.dev4.dist-info → boneIO-0.8.0.dev2.dist-info}/entry_points.txt +0 -0
- {boneIO-0.7.2.dev4.dist-info → boneIO-0.8.0.dev2.dist-info}/licenses/LICENSE +0 -0
- /boneio/{sensor/modbus → modbus}/dts1964_3f.json +0 -0
- /boneio/{sensor/modbus → modbus}/pt100.json +0 -0
- /boneio/{sensor/modbus → modbus}/r4dcb08.json +0 -0
- /boneio/{sensor/modbus → modbus}/sdm120.json +0 -0
- /boneio/{sensor/modbus → modbus}/sdm630.json +0 -0
- /boneio/{sensor/modbus → modbus}/sofar.json +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
boneio/__init__.py,sha256=hoLKnu6h1uEsiCHA9Ql2MMDC7VQS0E-s_2cxBS-CTOg,147
|
|
2
|
-
boneio/bonecli.py,sha256=
|
|
2
|
+
boneio/bonecli.py,sha256=JR4ILR5E3fVFkVL28-_tJcxckrKUIQwbNFkVF3YPMXs,7280
|
|
3
3
|
boneio/const.py,sha256=eIpq1EvFYh1p9I83xaDSOrF_JfdYr-1-yXO5vsAaqCg,5093
|
|
4
4
|
boneio/cover.py,sha256=EfO1DAOGTPQykfgJHzg1WqPs0U8b1AnD2NMI-sLZccM,8444
|
|
5
5
|
boneio/example_config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -22,12 +22,12 @@ boneio/helper/click_timer.py,sha256=Gu8sEi1S8GG42SYbfaMju7WXFhnnil9gYfirLZX7kJk,
|
|
|
22
22
|
boneio/helper/config.py,sha256=5cDnJ7usJDlUvXv8K2rSWEx30OFcU2k1U1vUEjO69WY,2431
|
|
23
23
|
boneio/helper/events.py,sha256=f4JDMMkyp0lWz9w8ABZ_hlKyAbJWtYHGnGTs2vv4vac,8648
|
|
24
24
|
boneio/helper/exceptions.py,sha256=vyYSUojwB7OttOPUjbxeHFM7C_o5ZQmRUcZ_vYpxYYI,717
|
|
25
|
-
boneio/helper/filter.py,sha256=
|
|
25
|
+
boneio/helper/filter.py,sha256=8jgSbHDaInEVlAPd7nrahnFYsi1y6C2fmI0yn55XzK0,935
|
|
26
26
|
boneio/helper/gpio.py,sha256=q2r2uklPWePuuekPO63kVfoaQdkvNObUJ3ktwsHgOB0,4771
|
|
27
27
|
boneio/helper/ha_discovery.py,sha256=B2t1cfOfKWys4EM1H7OxvOaYL8H_v18CTuHCEVP2w2c,7107
|
|
28
|
-
boneio/helper/loader.py,sha256=
|
|
28
|
+
boneio/helper/loader.py,sha256=mkvM6Fg_4xHI079iiWFcUQFW1w8819AXAkiBja0vKxo,17473
|
|
29
29
|
boneio/helper/logger.py,sha256=zyF_Vk3o2eSHuXypJhNzK99eZgkWDWybuB5KcJgurDs,1801
|
|
30
|
-
boneio/helper/mqtt.py,sha256=
|
|
30
|
+
boneio/helper/mqtt.py,sha256=1YgwO0RjkarN5qS5HS5rONf4H3_m3ak7tT_hHm-HeOQ,931
|
|
31
31
|
boneio/helper/oled.py,sha256=f0XYbaFstUbJ6ISmFe1vQCnmNVMctQWtnStNdROrblc,351
|
|
32
32
|
boneio/helper/onewire/W1ThermSensor.py,sha256=Pu5Fp29pKbxjJqDbFs05Mjg3Vy-9z_DTVoBpSWM_byA,2430
|
|
33
33
|
boneio/helper/onewire/__init__.py,sha256=4djH-uxiq5XhAiFTOxyqjvWVoJe0c8U8iN0BfmH8nSU,448
|
|
@@ -36,16 +36,29 @@ boneio/helper/onewire/onewire.py,sha256=tHofTu1l0qG7hH2XoSf-lj8gW7wVKe27j57qQWpV
|
|
|
36
36
|
boneio/helper/pcf8575.py,sha256=qeNYLqV5foDEQCyAz1WRQRi1CYYxtbPIha06Z5TyZVQ,311
|
|
37
37
|
boneio/helper/queue.py,sha256=7fE7d344-BEW8sljN40wmqsbBr4kPLvz4ShBjkT04p8,1066
|
|
38
38
|
boneio/helper/sensor/ina_219_smbus.py,sha256=eXp2epjUgsqaXamTSWSn3LMow5X_YxAxdREaWD7N3mk,6170
|
|
39
|
-
boneio/helper/state_manager.py,sha256=
|
|
39
|
+
boneio/helper/state_manager.py,sha256=6OeqJsLQPCyQhhmFlNxCd80cTYLnKb02L4AZ1ZFFVBE,2616
|
|
40
40
|
boneio/helper/stats.py,sha256=Ard04Z0WpkV7U6hgv1Uf4KxkjIde5iIW8W2cqRGXZyg,6731
|
|
41
41
|
boneio/helper/timeperiod.py,sha256=xpg9oe30Z5WiLDQPq3EBKKScejOnN47NhcpeFvDd3bY,5806
|
|
42
|
-
boneio/helper/util.py,sha256=
|
|
42
|
+
boneio/helper/util.py,sha256=DyppBTE2zTWoxVpl83Kb4SpJLEqOOSEkxXEITCrwnfk,938
|
|
43
43
|
boneio/helper/yaml_util.py,sha256=aaXMwXC2JoDYkBMcZkkB8nic3QOyGOhxPTRxWvMf3G4,11334
|
|
44
44
|
boneio/input/__init__.py,sha256=8Hcps-wCCl6bCAMoEmDJecR-Q9giFL29AvG1ZGe0dZw,200
|
|
45
45
|
boneio/input/gpio.py,sha256=mqmce4P_dNzmNHqNNiNAmKeeZKbbKtPUtQ6zd8-R-9I,2438
|
|
46
46
|
boneio/input/gpio_new.py,sha256=gpPfDr39B8FVUyh9XHtLgVQr4glMGFBgQsyIMzXDPaw,2822
|
|
47
|
-
boneio/manager.py,sha256=
|
|
48
|
-
boneio/modbus.py,sha256=
|
|
47
|
+
boneio/manager.py,sha256=06smEmgB9UDWUjCvy2l_lEHFyONU6N6ppW_dOiRv-wM,29866
|
|
48
|
+
boneio/modbus/__init__.py,sha256=yeHIxZ0_u7eCL7CqScvob5Utcru6Iza0LEtTagEtNtw,116
|
|
49
|
+
boneio/modbus/client.py,sha256=tmArCt9ac6rxUymdmFMEn4xpZtGdU7Yby7r3RkGJVxM,6030
|
|
50
|
+
boneio/modbus/cwt.json,sha256=4zJAHAV-h1JsV_Yp4VF6ecg_0eef-EHOdzRnhnMHRQs,814
|
|
51
|
+
boneio/modbus/dts1964_3f.json,sha256=WABph_2X4OgMidSM28vw4dLd9ZByRoiUP-RqE0UMpR8,8810
|
|
52
|
+
boneio/modbus/modbuscli.py,sha256=kt9T707FHCOzWE7SJEMTbLyM8BPPnTmcWsY5-0q9zqU,5502
|
|
53
|
+
boneio/modbus/orno-or-we-517.json,sha256=PtaG0B_uNLCemN2vrIXFS4Kzt6JBVoGCYhNMG-1wZhg,505
|
|
54
|
+
boneio/modbus/pt100.json,sha256=bzKNYDc3lMto94Ut8gutVIsHytQiMobwFCkonOiMegs,526
|
|
55
|
+
boneio/modbus/r4dcb08.json,sha256=oj4eK3bD1bG8BTRE4STVHB5L92luWaJ3FYFHQvFg-ZA,2735
|
|
56
|
+
boneio/modbus/sdm120.json,sha256=4sueFfSw7G6a77gdIUhbfJvE-MNVJFOeVbRvHDw5Rvk,4145
|
|
57
|
+
boneio/modbus/sdm630.json,sha256=TUiuc060IpF64p_HSDvdxEQcwkWHWUpnd48_xaGU-_o,8255
|
|
58
|
+
boneio/modbus/sensor.py,sha256=tfFYQNK0dE1yA0Otw66dxoM5wJ1b6nvW2_k4y8WAk_g,8303
|
|
59
|
+
boneio/modbus/socomec_e23.json,sha256=Is51YelEd4d6SYmz4Gd_oKx4Qi3bp-VHsl9zxWOFI5s,2882
|
|
60
|
+
boneio/modbus/sofar.json,sha256=A_YztQN_ctyItIm2LH1tfcs-pSyG18I530qleLd2tOw,5592
|
|
61
|
+
boneio/modbus/utils.py,sha256=cG75mmKTGaw_qrCG_D1qw-jl9XybUXa03i1ap5oBxGE,1753
|
|
49
62
|
boneio/mqtt_client.py,sha256=hzGT3IyftPHNrQXSKbWUXGYZBpP-VdRBkOrILkXDNHs,8665
|
|
50
63
|
boneio/oled.py,sha256=dLfAaH2Qto9P3mil_JK4siGvkSQ7LebhCabhL-OYGaM,5505
|
|
51
64
|
boneio/relay/__init__.py,sha256=Cz6D37GKHxm8DiuPuxE2EMaey2wdUdJYVet_hfR0lzA,231
|
|
@@ -61,7 +74,7 @@ boneio/schema/actions_switch.yaml,sha256=cDgSi12k4S-TWADQ20Fy9NvYKOXnm7YkyMaWpPy
|
|
|
61
74
|
boneio/schema/filters.yaml,sha256=WopX1SmCZsSZXNiGccLoi7uMCsUpdbs0eVfsAjmbay4,286
|
|
62
75
|
boneio/schema/filters_adc.yaml,sha256=3e0ts92MoFKOnfGOOn6WYIHxu0-tnZe9OjAjN9m7hiI,304
|
|
63
76
|
boneio/schema/id.yaml,sha256=vHI_6dxkJwoiGp5ien_pPpyaWA1F06eirTgfWMyWa1E,75
|
|
64
|
-
boneio/schema/schema.yaml,sha256=
|
|
77
|
+
boneio/schema/schema.yaml,sha256=R9EqtON1dTNsyCtIXDy6B1vJQArGnSQlnSLExJhVmqc,18418
|
|
65
78
|
boneio/schema/temp_unit.yaml,sha256=m32VGz9RTW92Lz08l9n6RftkcVNZquOGPPr5mc3eZVc,65
|
|
66
79
|
boneio/schema/update_interval.yaml,sha256=L_YLddAZdybcdO-T57Qhlv1GaWIVDwhvH-GadD1oOIo,133
|
|
67
80
|
boneio/sensor/__init__.py,sha256=Ztj-xzey-SWrEdVEozcwFsZSuEmJW8STVaf_vPMWiVc,619
|
|
@@ -69,21 +82,13 @@ boneio/sensor/adc.py,sha256=tCUdr3KN5uSbgXiwhNXbMjeNkO2CzPYhIOncHR5_LD0,1328
|
|
|
69
82
|
boneio/sensor/gpio.py,sha256=8m07tW1xDsrhBM2dZ9_VvmvYRn-5hLQVTTUhPlKg1rg,1199
|
|
70
83
|
boneio/sensor/gpio_new.py,sha256=m54agOF2gUh3aoC6f8WpCPAmBRADvqlKeWLqwlon9fg,1223
|
|
71
84
|
boneio/sensor/ina219.py,sha256=kBwaCjY63CNFj1mRZ2EEtm2BrMZ6BW4ahjAD2LT-0jo,3439
|
|
72
|
-
boneio/sensor/modbus/__init__.py,sha256=qB7MGcC6c-2X0U0iN7ai5qSo7tHp4zwZ_FX6TcQmiNU,10129
|
|
73
|
-
boneio/sensor/modbus/dts1964_3f.json,sha256=WABph_2X4OgMidSM28vw4dLd9ZByRoiUP-RqE0UMpR8,8810
|
|
74
|
-
boneio/sensor/modbus/pt100.json,sha256=bzKNYDc3lMto94Ut8gutVIsHytQiMobwFCkonOiMegs,526
|
|
75
|
-
boneio/sensor/modbus/r4dcb08.json,sha256=oj4eK3bD1bG8BTRE4STVHB5L92luWaJ3FYFHQvFg-ZA,2735
|
|
76
|
-
boneio/sensor/modbus/sdm120.json,sha256=4sueFfSw7G6a77gdIUhbfJvE-MNVJFOeVbRvHDw5Rvk,4145
|
|
77
|
-
boneio/sensor/modbus/sdm630.json,sha256=TUiuc060IpF64p_HSDvdxEQcwkWHWUpnd48_xaGU-_o,8255
|
|
78
|
-
boneio/sensor/modbus/socomec_e23.json,sha256=G4dEvOQbJpkLonubpjI77KFtlt13iwVxmArMgUzuwm0,2859
|
|
79
|
-
boneio/sensor/modbus/sofar.json,sha256=A_YztQN_ctyItIm2LH1tfcs-pSyG18I530qleLd2tOw,5592
|
|
80
85
|
boneio/sensor/temp/__init__.py,sha256=JLNH6Nc78ezleftnIYylIcNtKIm0rdqetbPZLsuCgDc,1844
|
|
81
86
|
boneio/sensor/temp/dallas.py,sha256=xrvd1LNBh1fRcjz_yd_9S29Lv26ItVtLCmhZgVQWJos,2485
|
|
82
87
|
boneio/sensor/temp/lm75.py,sha256=-mpOv78429plRPBp9s09Ljz1x2Bdxuagalqz40vlAKE,243
|
|
83
88
|
boneio/sensor/temp/mcp9808.py,sha256=yAh5yJzZLGTqa39xV4fyqsruj7oy3niOZAUV2W6eyCM,271
|
|
84
|
-
boneio/version.py,sha256=
|
|
85
|
-
boneIO-0.
|
|
86
|
-
boneIO-0.
|
|
87
|
-
boneIO-0.
|
|
88
|
-
boneIO-0.
|
|
89
|
-
boneIO-0.
|
|
89
|
+
boneio/version.py,sha256=qUYOwKU4jFQvDJRLavdrZJyoMqaNsiJCiYpN35E3_Ak,41
|
|
90
|
+
boneIO-0.8.0.dev2.dist-info/entry_points.txt,sha256=Igl1SMDEWwMg144bwRz28UOCb80hdxomC99QJ3u6qC0,46
|
|
91
|
+
boneIO-0.8.0.dev2.dist-info/WHEEL,sha256=B19PGBCYhWaz2p_UjAoRVh767nYQfk14Sn4TpIZ-nfU,87
|
|
92
|
+
boneIO-0.8.0.dev2.dist-info/METADATA,sha256=hcxcGSVtqYAvfGFFwZuI5FBtvvMd6Kf_Ingf-D-OWlo,1801
|
|
93
|
+
boneIO-0.8.0.dev2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
94
|
+
boneIO-0.8.0.dev2.dist-info/RECORD,,
|
boneio/bonecli.py
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
"""Bonecli script."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import asyncio
|
|
5
|
-
import logging
|
|
6
|
-
import sys
|
|
4
|
+
|
|
7
5
|
import os
|
|
8
|
-
from typing import Any
|
|
9
6
|
|
|
10
7
|
os.environ["W1THERMSENSOR_NO_KERNEL_MODULE"] = "1"
|
|
11
8
|
|
|
9
|
+
import argparse
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import sys
|
|
12
13
|
from colorlog import ColoredFormatter
|
|
13
14
|
from yaml import MarkedYAMLError
|
|
14
15
|
|
|
16
|
+
from boneio.modbus.modbuscli import async_run_modbus_set, async_run_modbus_get
|
|
17
|
+
from boneio.modbus.client import VALUE_TYPES
|
|
18
|
+
|
|
19
|
+
|
|
15
20
|
from boneio.const import ACTION
|
|
16
21
|
from boneio.helper import load_config_from_file
|
|
17
|
-
from boneio.helper.exceptions import
|
|
22
|
+
from boneio.helper.exceptions import (
|
|
23
|
+
ConfigurationException,
|
|
24
|
+
RestartRequestException,
|
|
25
|
+
)
|
|
18
26
|
from boneio.helper.events import GracefulExit
|
|
19
27
|
from boneio.helper.logger import configure_logger
|
|
20
28
|
from boneio.runner import async_run
|
|
@@ -49,30 +57,114 @@ def get_arguments() -> argparse.Namespace:
|
|
|
49
57
|
parser = argparse.ArgumentParser(
|
|
50
58
|
description="boneIO app for BeagleBone Black.",
|
|
51
59
|
)
|
|
52
|
-
parser.
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
subparsers = parser.add_subparsers(dest=ACTION, required=True)
|
|
61
|
+
run_parser = subparsers.add_parser("run")
|
|
62
|
+
run_parser.add_argument(
|
|
63
|
+
"--debug",
|
|
64
|
+
"-d",
|
|
65
|
+
action="count",
|
|
66
|
+
help="Start boneIO in debug mode",
|
|
67
|
+
default=0,
|
|
68
|
+
)
|
|
69
|
+
run_parser.add_argument(
|
|
55
70
|
"-c",
|
|
56
71
|
"--config",
|
|
57
72
|
metavar="path_to_config_dir",
|
|
58
73
|
default="./config.yaml",
|
|
59
74
|
help="File which contains boneIO configuration",
|
|
60
75
|
)
|
|
61
|
-
|
|
62
|
-
"--
|
|
76
|
+
run_parser.add_argument(
|
|
77
|
+
"--mqttusername",
|
|
78
|
+
help="Mqtt username to use if you don't want provide in file.",
|
|
79
|
+
)
|
|
80
|
+
run_parser.add_argument(
|
|
81
|
+
"--mqttpassword",
|
|
82
|
+
help="Mqtt password to use if you don't want provide in file.",
|
|
83
|
+
)
|
|
84
|
+
modbus_parser = subparsers.add_parser("modbus")
|
|
85
|
+
modbus_parser.add_argument(
|
|
86
|
+
"--debug",
|
|
87
|
+
"-d",
|
|
88
|
+
action="count",
|
|
89
|
+
help="Start boneIO in debug mode",
|
|
90
|
+
default=0,
|
|
91
|
+
)
|
|
92
|
+
modbus_parser.add_argument(
|
|
93
|
+
"--uart",
|
|
94
|
+
type=str,
|
|
95
|
+
choices=["uart1", "uart4"],
|
|
96
|
+
help="Choose UART",
|
|
97
|
+
required=True,
|
|
98
|
+
)
|
|
99
|
+
modbus_parser.add_argument(
|
|
100
|
+
"--address",
|
|
101
|
+
type=lambda x: int(x, 0),
|
|
102
|
+
required=True,
|
|
103
|
+
help="Current device address (hex or integer)",
|
|
104
|
+
)
|
|
105
|
+
modbus_parser.add_argument(
|
|
106
|
+
"--baudrate",
|
|
107
|
+
type=int,
|
|
108
|
+
choices=[2400, 4800, 9600, 19200],
|
|
109
|
+
required=True,
|
|
110
|
+
help="Current baudrate",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
modbus_sub_parser = modbus_parser.add_subparsers(
|
|
114
|
+
dest="modbus_action", required=True
|
|
63
115
|
)
|
|
64
|
-
|
|
65
|
-
|
|
116
|
+
set_modbus_parser = modbus_sub_parser.add_parser("set")
|
|
117
|
+
set_modbus_parser.add_argument(
|
|
118
|
+
"--device",
|
|
119
|
+
type=str,
|
|
120
|
+
choices=["cwt"],
|
|
121
|
+
help="Choose device to set modbus address/baudrate",
|
|
122
|
+
required=True,
|
|
66
123
|
)
|
|
67
|
-
|
|
68
|
-
|
|
124
|
+
set_modbus_parser_group = set_modbus_parser.add_mutually_exclusive_group()
|
|
125
|
+
set_modbus_parser_group.add_argument(
|
|
126
|
+
"--new-address",
|
|
127
|
+
type=lambda x: int(x, 0),
|
|
128
|
+
help="Set new address (hex or integer / 1 - 253/)",
|
|
69
129
|
)
|
|
130
|
+
|
|
131
|
+
set_modbus_parser_group.add_argument(
|
|
132
|
+
"--new-baudrate",
|
|
133
|
+
type=int,
|
|
134
|
+
choices=[2400, 4800, 9600, 19200],
|
|
135
|
+
help="Choose new baudrate to set",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
get_modbus_parser = modbus_sub_parser.add_parser("get")
|
|
139
|
+
get_modbus_parser.add_argument(
|
|
140
|
+
"--register-address",
|
|
141
|
+
type=int,
|
|
142
|
+
help="Register address",
|
|
143
|
+
required=True,
|
|
144
|
+
)
|
|
145
|
+
get_modbus_parser.add_argument(
|
|
146
|
+
"--register-type",
|
|
147
|
+
type=str,
|
|
148
|
+
choices=["input", "holding"],
|
|
149
|
+
help="Register type",
|
|
150
|
+
required=True,
|
|
151
|
+
)
|
|
152
|
+
get_modbus_parser.add_argument(
|
|
153
|
+
"--value-type",
|
|
154
|
+
type=str,
|
|
155
|
+
choices=VALUE_TYPES.keys(),
|
|
156
|
+
help="Value types",
|
|
157
|
+
required=True,
|
|
158
|
+
)
|
|
159
|
+
parser.add_argument("--version", action="version", version=__version__)
|
|
70
160
|
arguments = parser.parse_args()
|
|
71
161
|
|
|
72
162
|
return arguments
|
|
73
163
|
|
|
74
164
|
|
|
75
|
-
def run(
|
|
165
|
+
def run(
|
|
166
|
+
config: str, debug: int, mqttusername: str = "", mqttpassword: str = ""
|
|
167
|
+
) -> int:
|
|
76
168
|
"""Run BoneIO."""
|
|
77
169
|
_LOGGER.info("BoneIO %s starting.", __version__)
|
|
78
170
|
try:
|
|
@@ -99,6 +191,45 @@ def run(config: str, debug: int, mqttusername: str = "", mqttpassword: str = "")
|
|
|
99
191
|
return 1
|
|
100
192
|
|
|
101
193
|
|
|
194
|
+
def run_modbus_command(
|
|
195
|
+
args: argparse.Namespace,
|
|
196
|
+
) -> int:
|
|
197
|
+
"""Run BoneIO."""
|
|
198
|
+
_LOGGER.info("BoneIO %s starting.", __version__)
|
|
199
|
+
try:
|
|
200
|
+
configure_logger(log_config={}, debug=args.debug)
|
|
201
|
+
if args.modbus_action == "set":
|
|
202
|
+
ret = asyncio.run(
|
|
203
|
+
async_run_modbus_set(
|
|
204
|
+
device=args.device,
|
|
205
|
+
uart=args.uart,
|
|
206
|
+
address=args.address,
|
|
207
|
+
baudrate=args.baudrate,
|
|
208
|
+
new_baudrate=args.new_baudrate,
|
|
209
|
+
new_address=args.new_address,
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
ret = asyncio.run(
|
|
214
|
+
async_run_modbus_get(
|
|
215
|
+
uart=args.uart,
|
|
216
|
+
device_address=args.address,
|
|
217
|
+
baudrate=args.baudrate,
|
|
218
|
+
register_address=args.register_address,
|
|
219
|
+
register_type=args.register_type,
|
|
220
|
+
value_type=args.value_type,
|
|
221
|
+
),
|
|
222
|
+
)
|
|
223
|
+
return ret
|
|
224
|
+
except (RestartRequestException, GracefulExit) as err:
|
|
225
|
+
if err is not None:
|
|
226
|
+
_LOGGER.info(err)
|
|
227
|
+
return 0
|
|
228
|
+
except (ConfigurationException, MarkedYAMLError) as err:
|
|
229
|
+
_LOGGER.error("Failed to load config. %s Exiting.", err)
|
|
230
|
+
return 1
|
|
231
|
+
|
|
232
|
+
|
|
102
233
|
def main() -> int:
|
|
103
234
|
"""Start boneIO."""
|
|
104
235
|
|
|
@@ -113,6 +244,12 @@ def main() -> int:
|
|
|
113
244
|
mqttpassword=args.mqttpassword,
|
|
114
245
|
debug=debug,
|
|
115
246
|
)
|
|
247
|
+
elif args.action == "modbus":
|
|
248
|
+
_LOGGER.info("BoneIO Modbus helper %s .", __version__)
|
|
249
|
+
exit_code = run_modbus_command(
|
|
250
|
+
args=args,
|
|
251
|
+
)
|
|
252
|
+
|
|
116
253
|
_LOGGER.info("Exiting with exit code %s", exit_code)
|
|
117
254
|
return exit_code
|
|
118
255
|
|
boneio/helper/filter.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Filter class to adjust sensor values."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
import logging
|
|
4
5
|
|
|
@@ -22,7 +23,9 @@ class Filter:
|
|
|
22
23
|
for filter in self._filters:
|
|
23
24
|
for k, v in filter.items():
|
|
24
25
|
if k not in FILTERS:
|
|
25
|
-
_LOGGER.
|
|
26
|
+
_LOGGER.warning(
|
|
27
|
+
"Filter %s doesn't exists. Fix it in config.", k
|
|
28
|
+
)
|
|
26
29
|
continue
|
|
27
30
|
value = FILTERS[k](value, v)
|
|
28
31
|
if value is None:
|
boneio/helper/loader.py
CHANGED
|
@@ -103,7 +103,9 @@ def create_adc(manager: Manager, topic_prefix: str, adc_list: list = []):
|
|
|
103
103
|
manager=manager,
|
|
104
104
|
send_message=manager.send_message,
|
|
105
105
|
topic_prefix=topic_prefix,
|
|
106
|
-
update_interval=gpio.get(
|
|
106
|
+
update_interval=gpio.get(
|
|
107
|
+
UPDATE_INTERVAL, TimePeriod(seconds=60)
|
|
108
|
+
),
|
|
107
109
|
filters=gpio.get(FILTERS, []),
|
|
108
110
|
)
|
|
109
111
|
if gpio.get(SHOW_HA, True):
|
|
@@ -163,7 +165,10 @@ expander_class = {MCP: MCP23017, PCA: PCA9685, PCF: PCF8575}
|
|
|
163
165
|
|
|
164
166
|
|
|
165
167
|
def create_expander(
|
|
166
|
-
expander_dict: dict,
|
|
168
|
+
expander_dict: dict,
|
|
169
|
+
expander_config: list,
|
|
170
|
+
exp_type: ExpanderTypes,
|
|
171
|
+
i2cbusio: I2C,
|
|
167
172
|
) -> dict:
|
|
168
173
|
grouped_outputs = {}
|
|
169
174
|
for expander in expander_config:
|
|
@@ -189,7 +194,7 @@ def create_expander(
|
|
|
189
194
|
|
|
190
195
|
def create_modbus_sensors(manager: Manager, sensors, **kwargs) -> None:
|
|
191
196
|
"""Create Modbus sensor for each device."""
|
|
192
|
-
from boneio.sensor
|
|
197
|
+
from boneio.modbus.sensor import ModbusSensor
|
|
193
198
|
|
|
194
199
|
for sensor in sensors:
|
|
195
200
|
name = sensor.get(ID)
|
|
@@ -202,7 +207,9 @@ def create_modbus_sensors(manager: Manager, sensors, **kwargs) -> None:
|
|
|
202
207
|
manager=manager,
|
|
203
208
|
model=sensor[MODEL],
|
|
204
209
|
send_message=manager.send_message,
|
|
205
|
-
update_interval=sensor.get(
|
|
210
|
+
update_interval=sensor.get(
|
|
211
|
+
UPDATE_INTERVAL, TimePeriod(seconds=60)
|
|
212
|
+
),
|
|
206
213
|
**kwargs,
|
|
207
214
|
)
|
|
208
215
|
except FileNotFoundError as err:
|
|
@@ -272,7 +279,9 @@ def configure_relay(
|
|
|
272
279
|
if restore_state
|
|
273
280
|
else False
|
|
274
281
|
)
|
|
275
|
-
if output_type == NONE and state_manager.get(
|
|
282
|
+
if output_type == NONE and state_manager.get(
|
|
283
|
+
attr_type=RELAY, attr=relay_id
|
|
284
|
+
):
|
|
276
285
|
state_manager.del_attribute(attr_type=RELAY, attribute=relay_id)
|
|
277
286
|
restored_state = False
|
|
278
287
|
|
|
@@ -347,7 +356,7 @@ def configure_event_sensor(
|
|
|
347
356
|
pin: str,
|
|
348
357
|
press_callback: Callable,
|
|
349
358
|
send_ha_autodiscovery: Callable,
|
|
350
|
-
input: GpioEventButtonOld | GpioEventButtonNew | None = None
|
|
359
|
+
input: GpioEventButtonOld | GpioEventButtonNew | None = None,
|
|
351
360
|
) -> GpioEventButtonOld | GpioEventButtonNew | None:
|
|
352
361
|
"""Configure input sensor or button."""
|
|
353
362
|
try:
|
|
@@ -473,7 +482,9 @@ def configure_cover(
|
|
|
473
482
|
return cover
|
|
474
483
|
|
|
475
484
|
|
|
476
|
-
def configure_ds2482(
|
|
485
|
+
def configure_ds2482(
|
|
486
|
+
i2cbusio: I2C, address: str = DS2482_ADDRESS
|
|
487
|
+
) -> OneWireBus:
|
|
477
488
|
ds2482 = DS2482(i2c=i2cbusio, address=address)
|
|
478
489
|
ow_bus = OneWireBus(ds2482=ds2482)
|
|
479
490
|
return ow_bus
|
|
@@ -493,7 +504,9 @@ def find_onewire_devices(
|
|
|
493
504
|
devices = ow_bus.scan()
|
|
494
505
|
for device in devices:
|
|
495
506
|
_addr: int = device.int_address
|
|
496
|
-
_LOGGER.debug(
|
|
507
|
+
_LOGGER.debug(
|
|
508
|
+
"Found device on bus %s with address %s", bus_id, hex(_addr)
|
|
509
|
+
)
|
|
497
510
|
out[_addr] = device
|
|
498
511
|
except RuntimeError as err:
|
|
499
512
|
_LOGGER.error("Problem with scanning %s bus. %s", bus_type, err)
|
boneio/helper/mqtt.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Class to help initialize classes which uses mqtt send."""
|
|
2
|
+
|
|
2
3
|
from typing import Callable, Union
|
|
3
4
|
from boneio.helper.util import strip_accents
|
|
5
|
+
from typing import Optional
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class BasicMqtt:
|
|
@@ -11,7 +13,9 @@ class BasicMqtt:
|
|
|
11
13
|
id: str,
|
|
12
14
|
topic_prefix: str,
|
|
13
15
|
name: str,
|
|
14
|
-
send_message: Callable[
|
|
16
|
+
send_message: Callable[
|
|
17
|
+
[str, Union[str, int, dict, None], Optional[bool]], None
|
|
18
|
+
],
|
|
15
19
|
topic_type: str,
|
|
16
20
|
**kwargs,
|
|
17
21
|
):
|
boneio/helper/state_manager.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""State files manager."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
import asyncio
|
|
4
5
|
import logging
|
|
@@ -38,16 +39,19 @@ class StateManager:
|
|
|
38
39
|
if attr_type in self._state and attribute in self._state[attr_type]:
|
|
39
40
|
del self._state[attr_type][attribute]
|
|
40
41
|
|
|
41
|
-
def save_attribute(
|
|
42
|
+
def save_attribute(
|
|
43
|
+
self, attr_type: str, attribute: str, value: str
|
|
44
|
+
) -> None:
|
|
42
45
|
"""Save single attribute to file."""
|
|
43
46
|
if attr_type not in self._state:
|
|
44
47
|
self._state[attr_type] = {}
|
|
45
48
|
self._state[attr_type][attribute] = value
|
|
46
49
|
if self._save_attributes_callback is not None:
|
|
47
|
-
print(self._save_attributes_callback)
|
|
48
50
|
self._save_attributes_callback.cancel()
|
|
49
51
|
self._save_attributes_callback = None
|
|
50
|
-
self._save_attributes_callback = self._loop.call_later(
|
|
52
|
+
self._save_attributes_callback = self._loop.call_later(
|
|
53
|
+
1, lambda: self._loop.create_task(self.save_state())
|
|
54
|
+
)
|
|
51
55
|
|
|
52
56
|
def get(self, attr_type: str, attr: str, default_value: Any = None) -> Any:
|
|
53
57
|
"""Retrieve attribute from json."""
|
|
@@ -60,7 +64,7 @@ class StateManager:
|
|
|
60
64
|
def state(self) -> dict:
|
|
61
65
|
"""Retrieve all states."""
|
|
62
66
|
return self._state
|
|
63
|
-
|
|
67
|
+
|
|
64
68
|
def _save_state(self) -> None:
|
|
65
69
|
with open(self._file, "w+", encoding="utf-8") as f:
|
|
66
70
|
json.dump(self._state, f, indent=2)
|
boneio/helper/util.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import unicodedata
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
2
4
|
from typing import Any, Callable, TypeVar
|
|
3
5
|
|
|
4
6
|
CALLABLE_T = TypeVar("CALLABLE_T", bound=Callable[..., Any])
|
|
@@ -17,5 +19,17 @@ def is_callback(func: Callable[..., Any]) -> bool:
|
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
def strip_accents(s):
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
|
|
23
|
+
return "".join(
|
|
24
|
+
c
|
|
25
|
+
for c in unicodedata.normalize("NFD", s)
|
|
26
|
+
if unicodedata.category(c) != "Mn" and c != " "
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def open_json(path: str, model: str) -> dict:
|
|
31
|
+
"""Open json file."""
|
|
32
|
+
file = f"{os.path.join(path)}/{model}.json"
|
|
33
|
+
with open(file, "r") as db_file:
|
|
34
|
+
datastore = json.load(db_file)
|
|
35
|
+
return datastore
|
boneio/manager.py
CHANGED
|
@@ -74,7 +74,7 @@ from boneio.helper.loader import (
|
|
|
74
74
|
)
|
|
75
75
|
from boneio.helper.logger import configure_logger
|
|
76
76
|
from boneio.helper.yaml_util import load_config_from_file
|
|
77
|
-
from boneio.modbus import Modbus
|
|
77
|
+
from boneio.modbus.client import Modbus
|
|
78
78
|
|
|
79
79
|
from w1thermsensor.errors import KernelModuleLoadError
|
|
80
80
|
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
+
import struct
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from pymodbus.client.sync import BaseModbusClient, ModbusSerialClient
|
|
@@ -27,6 +28,8 @@ VALUE_TYPES = {
|
|
|
27
28
|
"U_QWORD": {"f": "decode_64bit_uint", "byteorder": Endian.Big},
|
|
28
29
|
"S_QWORD": {"f": "decode_64bit_int", "byteorder": Endian.Big},
|
|
29
30
|
"U_QWORD_R": {"f": "decode_64bit_uint", "byteorder": Endian.Little},
|
|
31
|
+
"FP32": {"f": "decode_32bit_float", "byteorder": Endian.Big},
|
|
32
|
+
"FP32_R": {"f": "decode_32bit_float", "byteorder": Endian.Little},
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
|
|
@@ -73,6 +76,11 @@ class Modbus:
|
|
|
73
76
|
except ModbusException as exception_error:
|
|
74
77
|
_LOGGER.error(exception_error)
|
|
75
78
|
|
|
79
|
+
@property
|
|
80
|
+
def client(self) -> ModbusSerialClient | None:
|
|
81
|
+
"""Return client."""
|
|
82
|
+
return self._client
|
|
83
|
+
|
|
76
84
|
async def async_close(self) -> None:
|
|
77
85
|
"""Disconnect client."""
|
|
78
86
|
async with self._lock:
|
|
@@ -94,7 +102,11 @@ class Modbus:
|
|
|
94
102
|
return False
|
|
95
103
|
|
|
96
104
|
async def read_single_register(
|
|
97
|
-
self,
|
|
105
|
+
self,
|
|
106
|
+
unit: int | str,
|
|
107
|
+
address: int,
|
|
108
|
+
count: int = 2,
|
|
109
|
+
method: str = "input",
|
|
98
110
|
) -> float | None:
|
|
99
111
|
"""Call sync. pymodbus."""
|
|
100
112
|
async with self._lock:
|
|
@@ -106,7 +118,7 @@ class Modbus:
|
|
|
106
118
|
result: ReadInputRegistersResponse = self._read_methods[method](
|
|
107
119
|
address, **kwargs
|
|
108
120
|
)
|
|
109
|
-
except ModbusException as exception_error:
|
|
121
|
+
except (ModbusException, struct.error) as exception_error:
|
|
110
122
|
_LOGGER.error(exception_error)
|
|
111
123
|
return None
|
|
112
124
|
if not hasattr(result, REGISTERS):
|
|
@@ -117,7 +129,11 @@ class Modbus:
|
|
|
117
129
|
).decode_32bit_float()
|
|
118
130
|
|
|
119
131
|
async def read_multiple_registers(
|
|
120
|
-
self,
|
|
132
|
+
self,
|
|
133
|
+
unit: int | str, # device address
|
|
134
|
+
address: int, # modbus register address
|
|
135
|
+
count: int = 2, # number of registers to read
|
|
136
|
+
method: str = "input", # type of register: input, holding
|
|
121
137
|
) -> ModbusResponse:
|
|
122
138
|
"""Call sync. pymodbus."""
|
|
123
139
|
async with self._lock:
|
boneio/modbus/cwt.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"model": "CWT Temp and Humidity sensor",
|
|
3
|
+
"set_base": {
|
|
4
|
+
"set_address_address": 2000,
|
|
5
|
+
"set_baudrate_address": 2001
|
|
6
|
+
},
|
|
7
|
+
"registers_base": [
|
|
8
|
+
{
|
|
9
|
+
"base": 0,
|
|
10
|
+
"length": 2,
|
|
11
|
+
"register_type": "holding",
|
|
12
|
+
"registers": [
|
|
13
|
+
{
|
|
14
|
+
"name": "Humidity",
|
|
15
|
+
"address": 0,
|
|
16
|
+
"unit_of_measurement": "%",
|
|
17
|
+
"state_class": "measurement",
|
|
18
|
+
"device_class": "humidity",
|
|
19
|
+
"value_type": "S_WORD",
|
|
20
|
+
"filters": [{ "multiply": 0.1 }]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "Temperature",
|
|
24
|
+
"address": 1,
|
|
25
|
+
"unit_of_measurement": "°C",
|
|
26
|
+
"state_class": "measurement",
|
|
27
|
+
"device_class": "temperature",
|
|
28
|
+
"value_type": "S_WORD",
|
|
29
|
+
"filters": [{ "multiply": 0.1 }]
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from ..helper.util import open_json
|
|
5
|
+
from boneio.const import UARTS, REGISTERS
|
|
6
|
+
from .client import Modbus
|
|
7
|
+
from .utils import REGISTERS_BASE, allowed_operations
|
|
8
|
+
|
|
9
|
+
_LOGGER = logging.getLogger(__name__)
|
|
10
|
+
SET_BASE = "set_base"
|
|
11
|
+
SET_ADDRESS = "set_address_address"
|
|
12
|
+
SET_BAUDRATE = "set_baudrate_address"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ModbusHelper:
|
|
16
|
+
"""Modbus Helper."""
|
|
17
|
+
|
|
18
|
+
baud_rates = [2400, 4800, 9600, 19200]
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
device: str,
|
|
23
|
+
uart: str,
|
|
24
|
+
address: int,
|
|
25
|
+
baudrate: int,
|
|
26
|
+
model: dict,
|
|
27
|
+
check_record: dict,
|
|
28
|
+
check_record_method: str,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Initialize Modbus Helper."""
|
|
31
|
+
self._modbus = Modbus(
|
|
32
|
+
uart=UARTS[uart],
|
|
33
|
+
baudrate=baudrate,
|
|
34
|
+
stopbits=1,
|
|
35
|
+
bytesize=8,
|
|
36
|
+
parity="N",
|
|
37
|
+
)
|
|
38
|
+
self._model = model
|
|
39
|
+
self._device = device
|
|
40
|
+
self._device_address = address
|
|
41
|
+
self._check_record = check_record
|
|
42
|
+
self._check_record_method = check_record_method
|
|
43
|
+
|
|
44
|
+
async def check_connection(self) -> bool:
|
|
45
|
+
"""Check Modbus connection."""
|
|
46
|
+
name = self._check_record.get("name")
|
|
47
|
+
address = self._check_record.get("address", 1)
|
|
48
|
+
value_type = self._check_record.get("value_type")
|
|
49
|
+
_LOGGER.info(
|
|
50
|
+
f"Checking connection {self._device_address}, address {address}."
|
|
51
|
+
)
|
|
52
|
+
count = 1 if value_type == "S_WORD" or value_type == "U_WORD" else 2
|
|
53
|
+
value = await self._modbus.read_multiple_registers(
|
|
54
|
+
unit=self._device_address,
|
|
55
|
+
address=address,
|
|
56
|
+
count=count,
|
|
57
|
+
method=self._check_record_method,
|
|
58
|
+
)
|
|
59
|
+
if not value:
|
|
60
|
+
_LOGGER.error("No returned value.")
|
|
61
|
+
return False
|
|
62
|
+
payload = value.registers[0:count]
|
|
63
|
+
decoded_value = self._modbus.decode_value(payload, value_type)
|
|
64
|
+
for filter in self._check_record.get("filters", []):
|
|
65
|
+
for key, value in filter.items():
|
|
66
|
+
if key in allowed_operations:
|
|
67
|
+
lamda_function = allowed_operations[key]
|
|
68
|
+
decoded_value = lamda_function(decoded_value, value)
|
|
69
|
+
_LOGGER.info(
|
|
70
|
+
f"Checked {name} with address {address} and value {decoded_value}"
|
|
71
|
+
)
|
|
72
|
+
if not decoded_value:
|
|
73
|
+
return False
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
def set_connection_speed(self, new_baudrate: int):
|
|
77
|
+
ind = self.baud_rates.index(new_baudrate)
|
|
78
|
+
if ind:
|
|
79
|
+
result = self._modbus.client.write_register(
|
|
80
|
+
address=self._model[SET_BAUDRATE],
|
|
81
|
+
value=ind,
|
|
82
|
+
unit=self._device_address,
|
|
83
|
+
)
|
|
84
|
+
if result.isError():
|
|
85
|
+
_LOGGER.error("Operation failed.")
|
|
86
|
+
else:
|
|
87
|
+
_LOGGER.info(
|
|
88
|
+
"Operation succeeded. Now restart device by disconnecting it."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def set_new_address(self, new_address: int):
|
|
92
|
+
if 0 < new_address < 253:
|
|
93
|
+
result = self._modbus.client.write_register(
|
|
94
|
+
address=self._model[SET_ADDRESS],
|
|
95
|
+
value=new_address,
|
|
96
|
+
unit=self._device_address,
|
|
97
|
+
)
|
|
98
|
+
if result.isError():
|
|
99
|
+
_LOGGER.error("Operation failed.")
|
|
100
|
+
else:
|
|
101
|
+
_LOGGER.info(
|
|
102
|
+
"Operation succeeded. Now restart device by disconnecting it."
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
_LOGGER.error("Invalid new address.")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async def async_run_modbus_set(
|
|
109
|
+
device: str,
|
|
110
|
+
uart: str,
|
|
111
|
+
address: int,
|
|
112
|
+
baudrate: int,
|
|
113
|
+
new_baudrate: int,
|
|
114
|
+
new_address: int,
|
|
115
|
+
):
|
|
116
|
+
"""Run Modbus Set Function."""
|
|
117
|
+
if new_address and new_baudrate:
|
|
118
|
+
_LOGGER.error("Can't set both methods new_address and new_baudrate.")
|
|
119
|
+
_db = open_json(path=os.path.dirname(__file__), model=device)
|
|
120
|
+
set_base = _db.get(SET_BASE, {})
|
|
121
|
+
first_reg_base = _db.get(REGISTERS_BASE, [])[0]
|
|
122
|
+
if not first_reg_base:
|
|
123
|
+
return False
|
|
124
|
+
first_record = first_reg_base.get(REGISTERS, [])[0]
|
|
125
|
+
modbus_helper = ModbusHelper(
|
|
126
|
+
device=device,
|
|
127
|
+
uart=uart,
|
|
128
|
+
address=address,
|
|
129
|
+
baudrate=baudrate,
|
|
130
|
+
model=set_base,
|
|
131
|
+
check_record=first_record,
|
|
132
|
+
check_record_method=first_reg_base.get("register_type", "input"),
|
|
133
|
+
)
|
|
134
|
+
if not await modbus_helper.check_connection():
|
|
135
|
+
_LOGGER.error("Can't connect with sensor. Exiting")
|
|
136
|
+
return 1
|
|
137
|
+
if new_baudrate:
|
|
138
|
+
modbus_helper.set_connection_speed(new_baudrate=new_baudrate)
|
|
139
|
+
return 0
|
|
140
|
+
if new_address:
|
|
141
|
+
modbus_helper.set_new_address(new_address=new_address)
|
|
142
|
+
return 0
|
|
143
|
+
return 1
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
async def async_run_modbus_get(
|
|
147
|
+
uart: str,
|
|
148
|
+
device_address: int,
|
|
149
|
+
register_address: int,
|
|
150
|
+
register_type: str,
|
|
151
|
+
baudrate: int,
|
|
152
|
+
value_type: str,
|
|
153
|
+
):
|
|
154
|
+
"""Run Modbus Get Function."""
|
|
155
|
+
_modbus = Modbus(
|
|
156
|
+
uart=UARTS[uart],
|
|
157
|
+
baudrate=baudrate,
|
|
158
|
+
stopbits=1,
|
|
159
|
+
bytesize=8,
|
|
160
|
+
parity="N",
|
|
161
|
+
)
|
|
162
|
+
count = 1 if value_type == "S_WORD" or value_type == "U_WORD" else 2
|
|
163
|
+
value = await _modbus.read_multiple_registers(
|
|
164
|
+
unit=device_address,
|
|
165
|
+
address=register_address,
|
|
166
|
+
count=count,
|
|
167
|
+
method=register_type,
|
|
168
|
+
)
|
|
169
|
+
if value:
|
|
170
|
+
payload = value.registers[0:count]
|
|
171
|
+
decoded_value = _modbus.decode_value(payload, value_type)
|
|
172
|
+
_LOGGER.info("Value: %s", decoded_value)
|
|
173
|
+
return 0
|
|
174
|
+
_LOGGER.error("No returned value.")
|
|
175
|
+
return 1
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"model": "orno-or-we-517",
|
|
3
|
+
"registers_base": [
|
|
4
|
+
{
|
|
5
|
+
"base": 14,
|
|
6
|
+
"length": 4,
|
|
7
|
+
"register_type": "holding",
|
|
8
|
+
"registers": [
|
|
9
|
+
{
|
|
10
|
+
"name": "Napiecie_L1",
|
|
11
|
+
"address": 14,
|
|
12
|
+
"unit_of_measurement": "V",
|
|
13
|
+
"state_class": "measurement",
|
|
14
|
+
"device_class": "voltage",
|
|
15
|
+
"return_type": "float32"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import asyncio
|
|
3
|
-
import json
|
|
4
3
|
import logging
|
|
5
4
|
import os
|
|
6
5
|
from datetime import datetime
|
|
7
|
-
from
|
|
8
|
-
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from .utils import CONVERT_METHODS, REGISTERS_BASE, allowed_operations
|
|
9
8
|
from boneio.const import (
|
|
10
9
|
ADDRESS,
|
|
11
10
|
BASE,
|
|
@@ -21,88 +20,11 @@ from boneio.helper import BasicMqtt, AsyncUpdater
|
|
|
21
20
|
from boneio.helper.config import ConfigHelper
|
|
22
21
|
from boneio.helper.events import EventBus
|
|
23
22
|
from boneio.helper.ha_discovery import modbus_sensor_availabilty_message
|
|
24
|
-
from
|
|
23
|
+
from .client import Modbus
|
|
24
|
+
from boneio.helper.util import open_json
|
|
25
25
|
|
|
26
26
|
_LOGGER = logging.getLogger(__name__)
|
|
27
27
|
|
|
28
|
-
allowed_operations = {"multiply": lambda x, y: x * y}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def float32(result, base, addr):
|
|
32
|
-
"""Read Float value from register."""
|
|
33
|
-
low = result.getRegister(addr - base)
|
|
34
|
-
high = result.getRegister(addr - base + 1)
|
|
35
|
-
data = bytearray(4)
|
|
36
|
-
data[0] = high & 0xFF
|
|
37
|
-
data[1] = high >> 8
|
|
38
|
-
data[2] = low & 0xFF
|
|
39
|
-
data[3] = low >> 8
|
|
40
|
-
val = unpack("f", bytes(data))
|
|
41
|
-
return val[0]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def floatsofar(result, base, addr):
|
|
45
|
-
"""Read Float value from register."""
|
|
46
|
-
low = result.getRegister(addr - base)
|
|
47
|
-
high = result.getRegister(addr - base + 1)
|
|
48
|
-
return high + low
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def multiply0_1(result, base, addr):
|
|
52
|
-
low = result.getRegister(addr - base)
|
|
53
|
-
return round(low * 0.1, 4)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def multiply0_01(result, base, addr):
|
|
57
|
-
low = result.getRegister(addr - base)
|
|
58
|
-
return round(low * 0.01, 4)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def multiply0_001(result, base, addr):
|
|
62
|
-
low = result.getRegister(addr - base)
|
|
63
|
-
return round(low * 0.001, 4)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def multiply10(result, base, addr):
|
|
67
|
-
low = result.getRegister(addr - base)
|
|
68
|
-
return round(low * 10, 4)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def multiply100(result, base, addr):
|
|
72
|
-
low = result.getRegister(addr - base)
|
|
73
|
-
return round(low * 100, 4)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def multiply1000(result, base, addr):
|
|
77
|
-
low = result.getRegister(addr - base)
|
|
78
|
-
return round(low * 1000, 4)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def regular_result(result, base, addr):
|
|
82
|
-
return result.getRegister(addr - base)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
CONVERT_METHODS = {
|
|
86
|
-
"float32": float32,
|
|
87
|
-
"multiply0_1": multiply0_1,
|
|
88
|
-
"multiply0_01": multiply0_01,
|
|
89
|
-
"multiply0_001": multiply0_001,
|
|
90
|
-
"floatsofar": floatsofar,
|
|
91
|
-
"multiply10": multiply10,
|
|
92
|
-
"multiply100": multiply100,
|
|
93
|
-
"multiply1000": multiply1000,
|
|
94
|
-
"regular": regular_result,
|
|
95
|
-
}
|
|
96
|
-
REGISTERS_BASE = "registers_base"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def open_json(model: str) -> dict:
|
|
100
|
-
"""Open json file."""
|
|
101
|
-
file = f"{os.path.join(os.path.dirname(__file__))}/{model}.json"
|
|
102
|
-
with open(file, "r") as db_file:
|
|
103
|
-
datastore = json.load(db_file)
|
|
104
|
-
return datastore
|
|
105
|
-
|
|
106
28
|
|
|
107
29
|
class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
108
30
|
"""Represent Modbus sensor in BoneIO."""
|
|
@@ -129,7 +51,7 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
129
51
|
)
|
|
130
52
|
self._config_helper = config_helper
|
|
131
53
|
self._modbus = modbus
|
|
132
|
-
self._db = open_json(model=model)
|
|
54
|
+
self._db = open_json(path=os.path.dirname(__file__), model=model)
|
|
133
55
|
self._model = self._db[MODEL]
|
|
134
56
|
self._address = address
|
|
135
57
|
self._discovery_sent = False
|
|
@@ -164,7 +86,7 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
164
86
|
)
|
|
165
87
|
self._send_message(topic=topic, payload=payload)
|
|
166
88
|
|
|
167
|
-
def _send_discovery_for_all_registers(self
|
|
89
|
+
def _send_discovery_for_all_registers(self) -> datetime:
|
|
168
90
|
"""Send discovery message to HA for each register."""
|
|
169
91
|
for data in self._db[REGISTERS_BASE]:
|
|
170
92
|
for register in data[REGISTERS]:
|
|
@@ -207,7 +129,7 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
207
129
|
)
|
|
208
130
|
if register is not None:
|
|
209
131
|
self._discovery_sent = (
|
|
210
|
-
self._send_discovery_for_all_registers(
|
|
132
|
+
self._send_discovery_for_all_registers()
|
|
211
133
|
)
|
|
212
134
|
await asyncio.sleep(2)
|
|
213
135
|
break
|
|
@@ -243,10 +165,10 @@ class ModbusSensor(BasicMqtt, AsyncUpdater):
|
|
|
243
165
|
# Let's assume device is offline.
|
|
244
166
|
self.set_payload_offline()
|
|
245
167
|
self._send_message(
|
|
246
|
-
topic=f"{self._config_helper.topic_prefix}/{self._id}
|
|
168
|
+
topic=f"{self._config_helper.topic_prefix}/{self._id}aa",
|
|
247
169
|
payload=self._payload_online,
|
|
248
170
|
)
|
|
249
|
-
_LOGGER.
|
|
171
|
+
_LOGGER.warning(
|
|
250
172
|
"Can't fetch data from modbus device %s. Will sleep for %s seconds",
|
|
251
173
|
self.id,
|
|
252
174
|
update_interval,
|
|
@@ -83,18 +83,19 @@
|
|
|
83
83
|
{
|
|
84
84
|
"name": "Total_Positive_Active_Energy",
|
|
85
85
|
"address": 50946,
|
|
86
|
-
"unit_of_measurement": "
|
|
87
|
-
"
|
|
86
|
+
"unit_of_measurement": "kWh",
|
|
87
|
+
"value_type": "U_DWORD",
|
|
88
|
+
"state_class": "total",
|
|
88
89
|
"device_class": "energy",
|
|
89
|
-
"
|
|
90
|
+
"filters": [{ "multiply": 0.01 }]
|
|
90
91
|
},
|
|
91
92
|
{
|
|
92
93
|
"name": "Total_Negative_Active_Energy",
|
|
93
94
|
"address": 50952,
|
|
94
|
-
"unit_of_measurement": "
|
|
95
|
-
"state_class": "
|
|
95
|
+
"unit_of_measurement": "kWh",
|
|
96
|
+
"state_class": "total",
|
|
96
97
|
"device_class": "energy",
|
|
97
|
-
"
|
|
98
|
+
"filters": [{ "multiply": 0.01 }]
|
|
98
99
|
}
|
|
99
100
|
]
|
|
100
101
|
}
|
boneio/modbus/utils.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from struct import unpack
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
allowed_operations = {"multiply": lambda x, y: x * y}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def float32(result, base, addr):
|
|
9
|
+
"""Read Float value from register."""
|
|
10
|
+
low = result.getRegister(addr - base)
|
|
11
|
+
high = result.getRegister(addr - base + 1)
|
|
12
|
+
data = bytearray(4)
|
|
13
|
+
data[0] = high & 0xFF
|
|
14
|
+
data[1] = high >> 8
|
|
15
|
+
data[2] = low & 0xFF
|
|
16
|
+
data[3] = low >> 8
|
|
17
|
+
val = unpack("f", bytes(data))
|
|
18
|
+
return val[0]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def floatsofar(result, base, addr):
|
|
22
|
+
"""Read Float value from register."""
|
|
23
|
+
low = result.getRegister(addr - base)
|
|
24
|
+
high = result.getRegister(addr - base + 1)
|
|
25
|
+
return high + low
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def multiply0_1(result, base, addr):
|
|
29
|
+
low = result.getRegister(addr - base)
|
|
30
|
+
return round(low * 0.1, 4)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def multiply0_01(result, base, addr):
|
|
34
|
+
low = result.getRegister(addr - base)
|
|
35
|
+
return round(low * 0.01, 4)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def multiply0_001(result, base, addr):
|
|
39
|
+
low = result.getRegister(addr - base)
|
|
40
|
+
return round(low * 0.001, 4)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def multiply10(result, base, addr):
|
|
44
|
+
low = result.getRegister(addr - base)
|
|
45
|
+
return round(low * 10, 4)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def multiply100(result, base, addr):
|
|
49
|
+
low = result.getRegister(addr - base)
|
|
50
|
+
return round(low * 100, 4)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def multiply1000(result, base, addr):
|
|
54
|
+
low = result.getRegister(addr - base)
|
|
55
|
+
return round(low * 1000, 4)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def regular_result(result, base, addr):
|
|
59
|
+
return result.getRegister(addr - base)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
CONVERT_METHODS = {
|
|
63
|
+
"float32": float32,
|
|
64
|
+
"multiply0_1": multiply0_1,
|
|
65
|
+
"multiply0_01": multiply0_01,
|
|
66
|
+
"multiply0_001": multiply0_001,
|
|
67
|
+
"floatsofar": floatsofar,
|
|
68
|
+
"multiply10": multiply10,
|
|
69
|
+
"multiply100": multiply100,
|
|
70
|
+
"multiply1000": multiply1000,
|
|
71
|
+
"regular": regular_result,
|
|
72
|
+
}
|
|
73
|
+
REGISTERS_BASE = "registers_base"
|
boneio/schema/schema.yaml
CHANGED
|
@@ -154,7 +154,8 @@ modbus_sensors:
|
|
|
154
154
|
model:
|
|
155
155
|
type: string
|
|
156
156
|
required: True
|
|
157
|
-
allowed:
|
|
157
|
+
allowed:
|
|
158
|
+
['sdm630', 'sdm120', 'sofar', 'socomec_e23', 'cwt', 'orno-or-we-517']
|
|
158
159
|
meta:
|
|
159
160
|
label: Model to load from database.
|
|
160
161
|
update_interval:
|
boneio/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# flake8: noqa
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.8.0dev2"
|
|
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
|