boneIO 0.1.0.dev26__tar.gz → 0.1.2__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.1.0.dev26/boneio.egg-info → boneio-0.1.2}/PKG-INFO +17 -9
- boneio-0.1.2/README.md +22 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/bonecli.py +14 -5
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/const.py +17 -1
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/__init__.py +12 -12
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/gpio.py +3 -3
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/ha_discovery.py +35 -10
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/stats.py +6 -1
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/manager.py +81 -35
- boneio-0.1.2/boneio/modbus.py +107 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/mqtt_client.py +0 -14
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/relay/mcp.py +0 -2
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/schema.yaml +35 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/sensor/__init__.py +7 -1
- boneio-0.1.2/boneio/sensor/sdm630.py +193 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/sensor/temp/__init__.py +4 -6
- boneio-0.1.2/boneio/version.py +2 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2/boneio.egg-info}/PKG-INFO +17 -9
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio.egg-info/SOURCES.txt +2 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio.egg-info/requires.txt +3 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/requirements.txt +3 -1
- {boneio-0.1.0.dev26 → boneio-0.1.2}/setup.py +6 -3
- boneio-0.1.0.dev26/README.md +0 -14
- boneio-0.1.0.dev26/boneio/version.py +0 -2
- {boneio-0.1.0.dev26 → boneio-0.1.2}/LICENSE +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/MANIFEST.in +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/__init__.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/example_config/__init__.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/example_config/adc.yaml +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/example_config/config.yaml +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/example_config/input.yaml +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/example_config/output24x16A.yaml +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/example_config/output32x5A.yaml +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/fonts/danube__.ttf +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/exceptions.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/mqtt.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/oled.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/queue.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/helper/yaml.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/input/__init__.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/input/gpio.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/oled.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/relay/__init__.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/relay/basic.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/relay/gpio.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/sensor/adc.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio/sensor/gpio.py +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio.egg-info/dependency_links.txt +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio.egg-info/entry_points.txt +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/boneio.egg-info/top_level.txt +0 -0
- {boneio-0.1.0.dev26 → boneio-0.1.2}/setup.cfg +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: boneio
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Python
|
|
5
|
-
Home-page: https://github.com/
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Python App for BoneIO
|
|
5
|
+
Home-page: https://github.com/boneIO-eu/app_bbb
|
|
6
6
|
License: GNU General Public License v3.0
|
|
7
|
-
Download-URL: https://github.com/
|
|
7
|
+
Download-URL: https://github.com/boneIO-eu/app_bbb/archive/0.1.2.zip
|
|
8
8
|
Platform: UNKNOWN
|
|
9
9
|
Classifier: Programming Language :: Python
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.7
|
|
@@ -16,16 +16,24 @@ License-File: LICENSE
|
|
|
16
16
|
Example usage:
|
|
17
17
|
boneio run -dd -c config.yaml
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
19
|
# Installation instructions
|
|
22
20
|
|
|
23
21
|
```
|
|
24
22
|
sudo apt-get install libopenjp2-7-dev libatlas-base-dev python3-venv
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
mkdir ~/boneio
|
|
24
|
+
python3 -m venv ~/boneio/venv
|
|
25
|
+
source ~/boneio/venv/bin/activate
|
|
26
|
+
pip3 install --upgrade boneio
|
|
27
|
+
cp ~/venv/lib/python3.7/site-packages/boneio/example_config/*.yaml ~/boneio/
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Edit config.yaml
|
|
31
|
+
|
|
32
|
+
# Start app
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
source ~/boneio/venv/bin/activate
|
|
36
|
+
boneio run -c ~/boneio/config.yaml -dd
|
|
37
|
+
```
|
|
30
38
|
|
|
31
39
|
|
boneio-0.1.2/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Example usage:
|
|
2
|
+
boneio run -dd -c config.yaml
|
|
3
|
+
|
|
4
|
+
# Installation instructions
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
sudo apt-get install libopenjp2-7-dev libatlas-base-dev python3-venv
|
|
8
|
+
mkdir ~/boneio
|
|
9
|
+
python3 -m venv ~/boneio/venv
|
|
10
|
+
source ~/boneio/venv/bin/activate
|
|
11
|
+
pip3 install --upgrade boneio
|
|
12
|
+
cp ~/venv/lib/python3.7/site-packages/boneio/example_config/*.yaml ~/boneio/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Edit config.yaml
|
|
16
|
+
|
|
17
|
+
# Start app
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
source ~/boneio/venv/bin/activate
|
|
21
|
+
boneio run -c ~/boneio/config.yaml -dd
|
|
22
|
+
```
|
|
@@ -7,21 +7,24 @@ import click
|
|
|
7
7
|
from colorlog import ColoredFormatter
|
|
8
8
|
|
|
9
9
|
from boneio.const import (
|
|
10
|
+
ADC,
|
|
10
11
|
ENABLED,
|
|
11
|
-
INPUT,
|
|
12
12
|
HA_DISCOVERY,
|
|
13
13
|
HOST,
|
|
14
|
+
INPUT,
|
|
15
|
+
LM75,
|
|
14
16
|
MCP23017,
|
|
15
17
|
MCP_TEMP_9808,
|
|
18
|
+
MODBUS,
|
|
16
19
|
MQTT,
|
|
17
20
|
OLED,
|
|
18
21
|
OUTPUT,
|
|
19
22
|
PAHO,
|
|
20
23
|
PASSWORD,
|
|
24
|
+
PYMODBUS,
|
|
25
|
+
SDM630,
|
|
21
26
|
TOPIC_PREFIX,
|
|
22
27
|
USERNAME,
|
|
23
|
-
LM75,
|
|
24
|
-
ADC,
|
|
25
28
|
)
|
|
26
29
|
from boneio.helper import CustomValidator, load_yaml_file
|
|
27
30
|
from boneio.manager import Manager
|
|
@@ -117,6 +120,8 @@ async def run(ctx, debug: int, config: str, mqttpassword: str = ""):
|
|
|
117
120
|
_LOGGER.debug(f"Lib version is {__version__}")
|
|
118
121
|
if debug > 1:
|
|
119
122
|
logging.getLogger(PAHO).setLevel(logging.DEBUG)
|
|
123
|
+
logging.getLogger(PYMODBUS).setLevel(logging.DEBUG)
|
|
124
|
+
logging.getLogger("pymodbus.client").setLevel(logging.DEBUG)
|
|
120
125
|
else:
|
|
121
126
|
logging.getLogger(PAHO).setLevel(logging.WARN)
|
|
122
127
|
schema = load_yaml_file(os.path.join(MAINPATH, "schema.yaml"))
|
|
@@ -140,9 +145,13 @@ async def run(ctx, debug: int, config: str, mqttpassword: str = ""):
|
|
|
140
145
|
input_pins=_config.get(INPUT, []),
|
|
141
146
|
ha_discovery=_config[MQTT][HA_DISCOVERY][ENABLED],
|
|
142
147
|
ha_discovery_prefix=_config[MQTT][HA_DISCOVERY][TOPIC_PREFIX],
|
|
148
|
+
sensors={
|
|
149
|
+
LM75: _config.get(LM75),
|
|
150
|
+
MCP_TEMP_9808: _config.get(MCP_TEMP_9808),
|
|
151
|
+
SDM630: _config.get(SDM630),
|
|
152
|
+
},
|
|
143
153
|
mcp23017=_config.get(MCP23017, []),
|
|
144
|
-
|
|
145
|
-
mcp9808=_config.get(MCP_TEMP_9808),
|
|
154
|
+
modbus=_config.get(MODBUS),
|
|
146
155
|
oled=_config.get(OLED),
|
|
147
156
|
adc_list=_config.get(ADC, []),
|
|
148
157
|
)
|
|
@@ -25,7 +25,16 @@ ADC = "adc"
|
|
|
25
25
|
IP = "ip"
|
|
26
26
|
MASK = "mask"
|
|
27
27
|
MAC = "mac"
|
|
28
|
-
NONE="none"
|
|
28
|
+
NONE = "none"
|
|
29
|
+
MODBUS = "modbus"
|
|
30
|
+
UART = "uart"
|
|
31
|
+
RX = "rx"
|
|
32
|
+
TX = "tx"
|
|
33
|
+
|
|
34
|
+
UARTS = {
|
|
35
|
+
"uart1": {ID: "/dev/ttyS1", TX: "P9.24", RX: "P9.26"},
|
|
36
|
+
"uart4": {ID: "/dev/ttyS4", TX: "P9.13", RX: "P9.11"},
|
|
37
|
+
}
|
|
29
38
|
|
|
30
39
|
# TIMINGS FOR BUTTONS
|
|
31
40
|
DEBOUNCE_DURATION = timedelta(seconds=0.2)
|
|
@@ -65,11 +74,13 @@ RELEASED = "released"
|
|
|
65
74
|
|
|
66
75
|
# MQTT CONST
|
|
67
76
|
PAHO = "paho.mqtt.client"
|
|
77
|
+
PYMODBUS = "pymodbus"
|
|
68
78
|
MQTT = "mqtt"
|
|
69
79
|
HOST = "host"
|
|
70
80
|
USERNAME = "username"
|
|
71
81
|
PASSWORD = "password"
|
|
72
82
|
ONLINE = "online"
|
|
83
|
+
OFFLINE = "offline"
|
|
73
84
|
TOPIC_PREFIX = "topic_prefix"
|
|
74
85
|
|
|
75
86
|
# I2C and MCP CONST
|
|
@@ -86,6 +97,11 @@ BINARY_SENSOR = "binary_sensor"
|
|
|
86
97
|
LM75 = "lm75"
|
|
87
98
|
MCP_TEMP_9808 = "mcp9808"
|
|
88
99
|
INPUT_SENSOR = "inputsensor"
|
|
100
|
+
SDM630 = "sdm630"
|
|
101
|
+
|
|
102
|
+
BASE = "base"
|
|
103
|
+
LENGTH = "length"
|
|
104
|
+
REGISTERS = "registers"
|
|
89
105
|
|
|
90
106
|
# TYPING
|
|
91
107
|
ClickTypes = Literal[SINGLE, DOUBLE, LONG, PRESSED, RELEASED]
|
|
@@ -13,12 +13,12 @@ from boneio.helper.mqtt import BasicMqtt
|
|
|
13
13
|
from boneio.helper.stats import HostData, host_stats
|
|
14
14
|
from boneio.helper.yaml import CustomValidator, load_yaml_file
|
|
15
15
|
from boneio.helper.ha_discovery import (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
ha_relay_availabilty_message,
|
|
17
|
+
ha_sensor_temp_availabilty_message,
|
|
18
|
+
ha_binary_sensor_availabilty_message,
|
|
19
|
+
ha_input_availabilty_message,
|
|
20
|
+
ha_sensor_availabilty_message,
|
|
21
|
+
ha_adc_sensor_availabilty_message,
|
|
22
22
|
)
|
|
23
23
|
from boneio.helper.exceptions import GPIOInputException, I2CError
|
|
24
24
|
from boneio.helper.queue import UniqueQueue
|
|
@@ -35,12 +35,12 @@ __all__ = [
|
|
|
35
35
|
"read_input",
|
|
36
36
|
"write_output",
|
|
37
37
|
"make_font",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
38
|
+
"ha_relay_availabilty_message",
|
|
39
|
+
"ha_sensor_availabilty_message",
|
|
40
|
+
"ha_adc_sensor_availabilty_message",
|
|
41
|
+
"ha_sensor_temp_availabilty_message",
|
|
42
|
+
"ha_binary_sensor_availabilty_message",
|
|
43
|
+
"ha_input_availabilty_message",
|
|
44
44
|
"GPIOInputException",
|
|
45
45
|
"I2CError",
|
|
46
46
|
"GpioBaseClass",
|
|
@@ -16,11 +16,11 @@ from boneio.const import (
|
|
|
16
16
|
_LOGGER = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def configure_pin(pin: str) -> None:
|
|
19
|
+
def configure_pin(pin: str, mode: str = GPIO_STR) -> None:
|
|
20
20
|
pin = f"{pin[0:3]}0{pin[3]}" if len(pin) == 4 else pin
|
|
21
|
-
_LOGGER.debug(f"Configuring pin for
|
|
21
|
+
_LOGGER.debug(f"Configuring pin {pin} for mode {mode}.")
|
|
22
22
|
subprocess.run(
|
|
23
|
-
[CONFIG_PIN, pin,
|
|
23
|
+
[CONFIG_PIN, pin, mode],
|
|
24
24
|
stdout=subprocess.DEVNULL,
|
|
25
25
|
stderr=subprocess.STDOUT,
|
|
26
26
|
timeout=1,
|
|
@@ -4,7 +4,7 @@ from boneio.version import __version__
|
|
|
4
4
|
from boneio.const import INPUT, OFF, ON, RELAY, STATE, SENSOR, INPUT_SENSOR
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def ha_relay_availabilty_message(id: str, name: str, topic: str = "boneio"):
|
|
8
8
|
"""Create availability topic for HA."""
|
|
9
9
|
return {
|
|
10
10
|
"availability": [{"topic": f"{topic}/{STATE}"}],
|
|
@@ -25,7 +25,7 @@ def ha_relay_availibilty_message(id: str, name: str, topic: str = "boneio"):
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def
|
|
28
|
+
def ha_availabilty_message(
|
|
29
29
|
id: str, name: str, topic: str = "boneio", sensor_type: str = INPUT
|
|
30
30
|
):
|
|
31
31
|
"""Create availability topic for HA."""
|
|
@@ -45,25 +45,25 @@ def ha_availibilty_message(
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def
|
|
49
|
-
return
|
|
48
|
+
def ha_input_availabilty_message(**kwargs):
|
|
49
|
+
return ha_availabilty_message(sensor_type=INPUT, **kwargs)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def
|
|
53
|
-
msg =
|
|
52
|
+
def ha_adc_sensor_availabilty_message(**kwargs):
|
|
53
|
+
msg = ha_availabilty_message(sensor_type=SENSOR, **kwargs)
|
|
54
54
|
msg["unit_of_measurement"] = "V"
|
|
55
55
|
msg["device_class"] = "voltage"
|
|
56
56
|
msg["state_class"] = "measurement"
|
|
57
57
|
return msg
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def
|
|
61
|
-
msg =
|
|
60
|
+
def ha_sensor_availabilty_message(unit_of_measurement: str = None, **kwargs):
|
|
61
|
+
msg = ha_availabilty_message(sensor_type=SENSOR, **kwargs)
|
|
62
62
|
if not unit_of_measurement:
|
|
63
63
|
return msg
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def
|
|
66
|
+
def ha_binary_sensor_availabilty_message(id: str, name: str, topic: str = "boneio"):
|
|
67
67
|
"""Create availability topic for HA."""
|
|
68
68
|
return {
|
|
69
69
|
"availability": [{"topic": f"{topic}/{STATE}"}],
|
|
@@ -82,7 +82,7 @@ def ha_binary_sensor_availibilty_message(id: str, name: str, topic: str = "bonei
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
def
|
|
85
|
+
def ha_sensor_temp_availabilty_message(id: str, name: str, topic: str = "boneio"):
|
|
86
86
|
"""Create availability topic for HA."""
|
|
87
87
|
return {
|
|
88
88
|
"availability": [{"topic": f"{topic}/{STATE}"}],
|
|
@@ -101,3 +101,28 @@ def ha_sensor_temp_availibilty_message(id: str, name: str, topic: str = "boneio"
|
|
|
101
101
|
"unit_of_measurement": "°C",
|
|
102
102
|
"value_template": "{{ value_json.state }}",
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def sdm630_availabilty_message(
|
|
107
|
+
id: str,
|
|
108
|
+
sensor_id: str,
|
|
109
|
+
name: str,
|
|
110
|
+
topic: str = "sdm630",
|
|
111
|
+
sensor_type: str = SENSOR,
|
|
112
|
+
**kwargs,
|
|
113
|
+
):
|
|
114
|
+
"""Create SDM630 availability topic for HA."""
|
|
115
|
+
return {
|
|
116
|
+
"availability": [{"topic": f"{topic}/{id}{STATE}"}],
|
|
117
|
+
"device": {
|
|
118
|
+
"identifiers": [topic, id],
|
|
119
|
+
"manufacturer": "BoneIO",
|
|
120
|
+
"model": "SDM630",
|
|
121
|
+
"name": f"BoneIO {name.upper()}",
|
|
122
|
+
"sw_version": __version__,
|
|
123
|
+
},
|
|
124
|
+
"name": sensor_id,
|
|
125
|
+
"state_topic": f"{topic}/{sensor_type}/{id}/{sensor_id}",
|
|
126
|
+
"unique_id": f"{topic}{sensor_id.replace('_', '').lower()}",
|
|
127
|
+
**kwargs,
|
|
128
|
+
}
|
|
@@ -145,7 +145,12 @@ class HostData:
|
|
|
145
145
|
|
|
146
146
|
data = {UPTIME: {}, NETWORK: {}, CPU: {}, DISK: {}, MEMORY: {}, SWAP: {}}
|
|
147
147
|
|
|
148
|
-
def __init__(
|
|
148
|
+
def __init__(
|
|
149
|
+
self,
|
|
150
|
+
output: dict,
|
|
151
|
+
callback: Callable,
|
|
152
|
+
temp_sensor: Callable[[LM75Sensor, MCP9808Sensor], None],
|
|
153
|
+
) -> None:
|
|
149
154
|
"""Initialize HostData."""
|
|
150
155
|
self._hostname = socket.gethostname()
|
|
151
156
|
self.data[UPTIME] = {HOST: self._hostname, UPTIME: 0}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from boneio.modbus import Modbus
|
|
2
3
|
import logging
|
|
3
4
|
import time
|
|
4
5
|
from typing import Any, Callable, List, Optional, Set, Union
|
|
@@ -28,6 +29,7 @@ from boneio.const import (
|
|
|
28
29
|
OUTPUT,
|
|
29
30
|
PIN,
|
|
30
31
|
RELAY,
|
|
32
|
+
SDM630,
|
|
31
33
|
STATE,
|
|
32
34
|
ClickTypes,
|
|
33
35
|
InputTypes,
|
|
@@ -35,15 +37,17 @@ from boneio.const import (
|
|
|
35
37
|
SHOW_HA,
|
|
36
38
|
BINARY_SENSOR,
|
|
37
39
|
INPUT_SENSOR,
|
|
40
|
+
UART,
|
|
38
41
|
UPDATE_INTERVAL,
|
|
42
|
+
UARTS,
|
|
39
43
|
)
|
|
40
44
|
from boneio.helper import (
|
|
41
45
|
HostData,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
ha_relay_availabilty_message,
|
|
47
|
+
ha_input_availabilty_message,
|
|
48
|
+
ha_adc_sensor_availabilty_message,
|
|
49
|
+
ha_sensor_temp_availabilty_message,
|
|
50
|
+
ha_binary_sensor_availabilty_message,
|
|
47
51
|
host_stats,
|
|
48
52
|
GPIOInputException,
|
|
49
53
|
I2CError,
|
|
@@ -65,8 +69,8 @@ class Manager:
|
|
|
65
69
|
topic_prefix: str,
|
|
66
70
|
relay_pins: List,
|
|
67
71
|
input_pins: List,
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
sensors: dict = {},
|
|
73
|
+
modbus: dict = None,
|
|
70
74
|
ha_discovery: bool = True,
|
|
71
75
|
ha_discovery_prefix: str = HOMEASSISTANT,
|
|
72
76
|
mcp23017: Optional[List] = None,
|
|
@@ -77,6 +81,7 @@ class Manager:
|
|
|
77
81
|
_LOGGER.info("Initializing manager module.")
|
|
78
82
|
self._loop = asyncio.get_event_loop()
|
|
79
83
|
self._host_data = None
|
|
84
|
+
self._ha_discovery = ha_discovery
|
|
80
85
|
|
|
81
86
|
self.send_message = send_message
|
|
82
87
|
self._topic_prefix = topic_prefix
|
|
@@ -88,6 +93,9 @@ class Manager:
|
|
|
88
93
|
self._oled = None
|
|
89
94
|
self._tasks: List[asyncio.Task] = []
|
|
90
95
|
self._temp_sensors = []
|
|
96
|
+
self._modbus = None
|
|
97
|
+
if modbus and modbus.get(UART) in UARTS:
|
|
98
|
+
self._modbus = Modbus(UARTS[modbus.get(UART)])
|
|
91
99
|
|
|
92
100
|
def create_temp_sensor(sensor_type: str, temp_def: dict = {}):
|
|
93
101
|
"""Create LM sensor in manager."""
|
|
@@ -111,9 +119,9 @@ class Manager:
|
|
|
111
119
|
self.send_ha_autodiscovery(
|
|
112
120
|
id=id,
|
|
113
121
|
name=name,
|
|
114
|
-
ha_type=
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
ha_type=SENSOR,
|
|
123
|
+
ha_discovery_prefix=ha_discovery_prefix,
|
|
124
|
+
availabilty_msg_func=ha_sensor_temp_availabilty_message,
|
|
117
125
|
)
|
|
118
126
|
self._tasks.append(asyncio.create_task(temp_sensor.send_state()))
|
|
119
127
|
self._temp_sensors.append(temp_sensor)
|
|
@@ -138,11 +146,12 @@ class Manager:
|
|
|
138
146
|
pass
|
|
139
147
|
|
|
140
148
|
def create_adc():
|
|
149
|
+
"""Create ADC sensor."""
|
|
141
150
|
from boneio.sensor import initialize_adc, GpioADCSensor
|
|
142
151
|
|
|
143
152
|
initialize_adc()
|
|
144
153
|
|
|
145
|
-
|
|
154
|
+
# TODO: find what exception can ADC gpio throw.
|
|
146
155
|
for gpio in adc_list:
|
|
147
156
|
name = gpio.get(ID)
|
|
148
157
|
id = name.replace(" ", "")
|
|
@@ -160,8 +169,8 @@ class Manager:
|
|
|
160
169
|
id=id,
|
|
161
170
|
name=name,
|
|
162
171
|
ha_type=SENSOR,
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
ha_discovery_prefix=ha_discovery_prefix,
|
|
173
|
+
availabilty_msg_func=ha_adc_sensor_availabilty_message,
|
|
165
174
|
)
|
|
166
175
|
self._tasks.append(asyncio.create_task(adc.send_state()))
|
|
167
176
|
except I2CError as err:
|
|
@@ -170,11 +179,43 @@ class Manager:
|
|
|
170
179
|
|
|
171
180
|
return True
|
|
172
181
|
|
|
173
|
-
|
|
174
|
-
|
|
182
|
+
def create_sdm_630(sensors):
|
|
183
|
+
"""Create SDM sensor for each device."""
|
|
184
|
+
from boneio.sensor.sdm630 import Sdm630
|
|
175
185
|
|
|
176
|
-
|
|
177
|
-
|
|
186
|
+
for sensor in sensors:
|
|
187
|
+
name = sensor.get(ID)
|
|
188
|
+
id = name.replace(" ", "")
|
|
189
|
+
try:
|
|
190
|
+
sdm = Sdm630(
|
|
191
|
+
modbus=self._modbus,
|
|
192
|
+
address=sensor[ADDRESS],
|
|
193
|
+
id=id,
|
|
194
|
+
name=name,
|
|
195
|
+
send_message=self.send_message,
|
|
196
|
+
topic_prefix=topic_prefix,
|
|
197
|
+
ha_discovery=ha_discovery,
|
|
198
|
+
ha_discovery_prefix=ha_discovery_prefix,
|
|
199
|
+
update_interval=sensor.get(UPDATE_INTERVAL, 30),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
self._tasks.append(asyncio.create_task(sdm.send_state()))
|
|
203
|
+
except I2CError as err:
|
|
204
|
+
_LOGGER.error("Can't configure SDM630 sensor %s. %s", id, err)
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
if sensors.get(LM75):
|
|
210
|
+
create_temp_sensor(sensor_type=LM75, temp_def=sensors.get(LM75))
|
|
211
|
+
|
|
212
|
+
if sensors.get(MCP_TEMP_9808):
|
|
213
|
+
create_temp_sensor(
|
|
214
|
+
sensor_type=MCP_TEMP_9808, temp_def=sensors.get(MCP_TEMP_9808)
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if sensors.get(SDM630) and self._modbus:
|
|
218
|
+
create_sdm_630(sensors=sensors.get(SDM630))
|
|
178
219
|
|
|
179
220
|
if mcp23017:
|
|
180
221
|
create_mcp23017()
|
|
@@ -204,7 +245,7 @@ class Manager:
|
|
|
204
245
|
self._grouped_outputs[mcp_id][relay_id] = mcp_relay
|
|
205
246
|
return mcp_relay
|
|
206
247
|
elif gpio[KIND] == GPIO:
|
|
207
|
-
if not
|
|
248
|
+
if GPIO not in self._grouped_outputs:
|
|
208
249
|
self._grouped_outputs[GPIO] = {}
|
|
209
250
|
gpio_relay = GpioRelay(
|
|
210
251
|
pin=gpio[PIN],
|
|
@@ -220,14 +261,13 @@ class Manager:
|
|
|
220
261
|
gpio[ID].replace(" ", ""): configure_relay(gpio) for gpio in relay_pins
|
|
221
262
|
}
|
|
222
263
|
for out in self.output.values():
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
264
|
+
self.send_ha_autodiscovery(
|
|
265
|
+
id=out.id,
|
|
266
|
+
name=out.name,
|
|
267
|
+
ha_type=out.ha_type,
|
|
268
|
+
ha_discovery_prefix=ha_discovery_prefix,
|
|
269
|
+
availabilty_msg_func=ha_relay_availabilty_message,
|
|
270
|
+
)
|
|
231
271
|
self._loop.call_soon_threadsafe(
|
|
232
272
|
self._loop.call_later,
|
|
233
273
|
0.5,
|
|
@@ -249,7 +289,7 @@ class Manager:
|
|
|
249
289
|
),
|
|
250
290
|
rest_pin=gpio,
|
|
251
291
|
)
|
|
252
|
-
|
|
292
|
+
availabilty_msg_func = ha_binary_sensor_availabilty_message
|
|
253
293
|
ha_type = BINARY_SENSOR
|
|
254
294
|
else:
|
|
255
295
|
GpioInputButton(
|
|
@@ -262,15 +302,15 @@ class Manager:
|
|
|
262
302
|
),
|
|
263
303
|
rest_pin=gpio,
|
|
264
304
|
)
|
|
265
|
-
|
|
305
|
+
availabilty_msg_func = ha_input_availabilty_message
|
|
266
306
|
ha_type = SENSOR
|
|
267
307
|
if gpio.get(SHOW_HA, True):
|
|
268
308
|
self.send_ha_autodiscovery(
|
|
269
309
|
id=pin,
|
|
270
310
|
name=gpio.get(ID, pin),
|
|
271
311
|
ha_type=ha_type,
|
|
272
|
-
|
|
273
|
-
|
|
312
|
+
ha_discovery_prefix=ha_discovery_prefix,
|
|
313
|
+
availabilty_msg_func=availabilty_msg_func,
|
|
274
314
|
)
|
|
275
315
|
except GPIOInputException as err:
|
|
276
316
|
_LOGGER.error("This PIN %s can't be configured. %s", pin, err)
|
|
@@ -301,7 +341,7 @@ class Manager:
|
|
|
301
341
|
self.send_message(topic=f"{topic_prefix}/{STATE}", payload=ONLINE)
|
|
302
342
|
_LOGGER.info("BoneIO manager is ready.")
|
|
303
343
|
|
|
304
|
-
def _host_data_callback(self, type):
|
|
344
|
+
def _host_data_callback(self, type: str):
|
|
305
345
|
if self._oled:
|
|
306
346
|
self._oled.handle_data_update(type)
|
|
307
347
|
|
|
@@ -329,13 +369,19 @@ class Manager:
|
|
|
329
369
|
self,
|
|
330
370
|
id: str,
|
|
331
371
|
name: str,
|
|
332
|
-
|
|
333
|
-
|
|
372
|
+
ha_discovery_prefix: str,
|
|
373
|
+
availabilty_msg_func: Callable,
|
|
334
374
|
ha_type: str = "switch",
|
|
375
|
+
topic_prefix: str = None,
|
|
376
|
+
**kwargs,
|
|
335
377
|
) -> None:
|
|
336
378
|
"""Send HA autodiscovery information for each relay."""
|
|
337
|
-
|
|
338
|
-
|
|
379
|
+
if not self._ha_discovery:
|
|
380
|
+
return
|
|
381
|
+
if not topic_prefix:
|
|
382
|
+
topic_prefix = self._topic_prefix
|
|
383
|
+
msg = availabilty_msg_func(topic=topic_prefix, id=id, name=name, **kwargs)
|
|
384
|
+
topic = f"{ha_discovery_prefix}/{ha_type}/{topic_prefix}/{id}/config"
|
|
339
385
|
_LOGGER.debug("Sending HA discovery for %s, %s.", ha_type, name)
|
|
340
386
|
self.send_message(topic=topic, payload=msg)
|
|
341
387
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
from boneio.helper import configure_pin
|
|
6
|
+
from boneio.const import ID, RX, TX, UART, REGISTERS
|
|
7
|
+
from pymodbus.client.sync import (
|
|
8
|
+
BaseModbusClient,
|
|
9
|
+
ModbusSerialClient,
|
|
10
|
+
)
|
|
11
|
+
from pymodbus.register_read_message import ReadInputRegistersResponse
|
|
12
|
+
from pymodbus.payload import BinaryPayloadDecoder
|
|
13
|
+
from pymodbus.constants import Endian
|
|
14
|
+
from typing import Any
|
|
15
|
+
from pymodbus.exceptions import ModbusException
|
|
16
|
+
from pymodbus.pdu import ModbusResponse
|
|
17
|
+
|
|
18
|
+
_LOGGER = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Modbus:
|
|
22
|
+
"""Represent modbus connection over chosen UART."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, uart: dict[str, Any]) -> None:
|
|
25
|
+
"""Initialize the Modbus hub."""
|
|
26
|
+
|
|
27
|
+
configure_pin(pin=uart[RX], mode=UART)
|
|
28
|
+
configure_pin(pin=uart[TX], mode=UART)
|
|
29
|
+
self._uart = uart
|
|
30
|
+
|
|
31
|
+
# generic configuration
|
|
32
|
+
self._client: BaseModbusClient | None = None
|
|
33
|
+
self._lock = asyncio.Lock()
|
|
34
|
+
try:
|
|
35
|
+
self._client = ModbusSerialClient(
|
|
36
|
+
port=self._uart[ID],
|
|
37
|
+
method="rtu",
|
|
38
|
+
baudrate=9600,
|
|
39
|
+
stopbits=1,
|
|
40
|
+
bytesize=8,
|
|
41
|
+
parity="N",
|
|
42
|
+
)
|
|
43
|
+
except ModbusException as exception_error:
|
|
44
|
+
_LOGGER.error(exception_error)
|
|
45
|
+
|
|
46
|
+
async def async_close(self) -> None:
|
|
47
|
+
"""Disconnect client."""
|
|
48
|
+
async with self._lock:
|
|
49
|
+
if self._client:
|
|
50
|
+
try:
|
|
51
|
+
self._client.close()
|
|
52
|
+
except ModbusException as exception_error:
|
|
53
|
+
_LOGGER.error(exception_error)
|
|
54
|
+
del self._client
|
|
55
|
+
self._client = None
|
|
56
|
+
_LOGGER.warning("modbus communication closed")
|
|
57
|
+
|
|
58
|
+
def _pymodbus_connect(self) -> bool:
|
|
59
|
+
"""Connect client."""
|
|
60
|
+
try:
|
|
61
|
+
self._client.connect() # type: ignore[union-attr]
|
|
62
|
+
except ModbusException as exception_error:
|
|
63
|
+
_LOGGER.error(exception_error)
|
|
64
|
+
return False
|
|
65
|
+
else:
|
|
66
|
+
_LOGGER.info("modbus communication open")
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
def read_single_register(self, unit: int, address: int, count: int = 2) -> float:
|
|
70
|
+
"""Call sync. pymodbus."""
|
|
71
|
+
if not self._pymodbus_connect:
|
|
72
|
+
_LOGGER.error("Can't connect to Modbus.")
|
|
73
|
+
return None
|
|
74
|
+
kwargs = {"unit": unit, "count": count} if unit else {}
|
|
75
|
+
try:
|
|
76
|
+
result: ReadInputRegistersResponse = self._client.read_input_registers(
|
|
77
|
+
address, **kwargs
|
|
78
|
+
)
|
|
79
|
+
except ModbusException as exception_error:
|
|
80
|
+
_LOGGER.error(exception_error)
|
|
81
|
+
return None
|
|
82
|
+
if not hasattr(result, REGISTERS):
|
|
83
|
+
_LOGGER.error(str(result))
|
|
84
|
+
return None
|
|
85
|
+
return BinaryPayloadDecoder.fromRegisters(
|
|
86
|
+
result.registers, byteorder=Endian.Big, wordorder=Endian.Big
|
|
87
|
+
).decode_32bit_float()
|
|
88
|
+
|
|
89
|
+
def read_multiple_registers(
|
|
90
|
+
self, unit: int, address: int, count: int = 2
|
|
91
|
+
) -> ModbusResponse:
|
|
92
|
+
"""Call sync. pymodbus."""
|
|
93
|
+
if not self._pymodbus_connect:
|
|
94
|
+
_LOGGER.error("Can't connect to Modbus.")
|
|
95
|
+
return None
|
|
96
|
+
kwargs = {"unit": unit, "count": count} if unit else {}
|
|
97
|
+
try:
|
|
98
|
+
result: ReadInputRegistersResponse = self._client.read_input_registers(
|
|
99
|
+
address, **kwargs
|
|
100
|
+
)
|
|
101
|
+
except ModbusException as exception_error:
|
|
102
|
+
_LOGGER.error(exception_error)
|
|
103
|
+
return None
|
|
104
|
+
if not hasattr(result, REGISTERS):
|
|
105
|
+
_LOGGER.error(str(result))
|
|
106
|
+
return None
|
|
107
|
+
return result
|
|
@@ -49,20 +49,6 @@ class MQTTClient:
|
|
|
49
49
|
**self.client_options,
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
async def connect(self, *, timeout: float = 10.0) -> None:
|
|
53
|
-
"""Connect to the broker.
|
|
54
|
-
|
|
55
|
-
Can raise asyncio_mqtt.MqttError.
|
|
56
|
-
"""
|
|
57
|
-
await self.asyncio_client.connect(timeout=timeout)
|
|
58
|
-
|
|
59
|
-
async def disconnect(self, *, timeout: float = 10.0) -> None:
|
|
60
|
-
"""Disconnect from the broker.
|
|
61
|
-
|
|
62
|
-
Can raise asyncio_mqtt.MqttError.
|
|
63
|
-
"""
|
|
64
|
-
await self.asyncio_client.disconnect(timeout=timeout)
|
|
65
|
-
|
|
66
52
|
async def publish( # pylint:disable=too-many-arguments
|
|
67
53
|
self,
|
|
68
54
|
topic: str,
|
|
@@ -52,6 +52,41 @@ oled:
|
|
|
52
52
|
type: boolean
|
|
53
53
|
coerce: to_bool
|
|
54
54
|
|
|
55
|
+
modbus:
|
|
56
|
+
type: dict
|
|
57
|
+
required: False
|
|
58
|
+
schema:
|
|
59
|
+
uart:
|
|
60
|
+
type: string
|
|
61
|
+
required: True
|
|
62
|
+
meta:
|
|
63
|
+
label: Uart ID to use
|
|
64
|
+
|
|
65
|
+
sdm630:
|
|
66
|
+
type: list
|
|
67
|
+
required: False
|
|
68
|
+
meta:
|
|
69
|
+
label: SDM630 sensor
|
|
70
|
+
schema:
|
|
71
|
+
type: dict
|
|
72
|
+
schema:
|
|
73
|
+
id:
|
|
74
|
+
type: string
|
|
75
|
+
required: False
|
|
76
|
+
meta:
|
|
77
|
+
label: Id of SDM630
|
|
78
|
+
address:
|
|
79
|
+
type: string
|
|
80
|
+
required: True
|
|
81
|
+
default: 0x33
|
|
82
|
+
meta:
|
|
83
|
+
label: Address of SDM to use.
|
|
84
|
+
update_interval:
|
|
85
|
+
type: integer
|
|
86
|
+
required: True
|
|
87
|
+
default: 30
|
|
88
|
+
meta:
|
|
89
|
+
label: Update interval in seconds.
|
|
55
90
|
lm75:
|
|
56
91
|
type: dict
|
|
57
92
|
required: False
|
|
@@ -3,4 +3,10 @@ from boneio.sensor.temp import LM75Sensor, MCP9808Sensor
|
|
|
3
3
|
from boneio.sensor.gpio import GpioInputSensor
|
|
4
4
|
from boneio.sensor.adc import initialize_adc, GpioADCSensor
|
|
5
5
|
|
|
6
|
-
__all__ = [
|
|
6
|
+
__all__ = [
|
|
7
|
+
"LM75Sensor",
|
|
8
|
+
"MCP9808Sensor",
|
|
9
|
+
"GpioInputSensor",
|
|
10
|
+
"initialize_adc",
|
|
11
|
+
"GpioADCSensor",
|
|
12
|
+
]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import asyncio
|
|
3
|
+
from struct import unpack
|
|
4
|
+
from collections import namedtuple
|
|
5
|
+
|
|
6
|
+
from boneio.const import SDM630, SENSOR, STATE, ONLINE, OFFLINE, BASE, LENGTH, REGISTERS
|
|
7
|
+
from boneio.helper import BasicMqtt
|
|
8
|
+
from boneio.helper.ha_discovery import sdm630_availabilty_message
|
|
9
|
+
|
|
10
|
+
_LOGGER = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def float32(result, base, addr):
|
|
14
|
+
"""Read Float value from register."""
|
|
15
|
+
low = result.getRegister(addr - base)
|
|
16
|
+
high = result.getRegister(addr - base + 1)
|
|
17
|
+
data = bytearray(4)
|
|
18
|
+
data[0] = high & 0xFF
|
|
19
|
+
data[1] = high >> 8
|
|
20
|
+
data[2] = low & 0xFF
|
|
21
|
+
data[3] = low >> 8
|
|
22
|
+
|
|
23
|
+
val = unpack("f", bytes(data))
|
|
24
|
+
|
|
25
|
+
return val[0]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
RegistryEntry = namedtuple(
|
|
29
|
+
"RegistryEntry", "sensor_id address unit_of_measurement state_class device_class"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
REGISTER_BASE = [
|
|
33
|
+
{
|
|
34
|
+
BASE: 0x00,
|
|
35
|
+
LENGTH: 60,
|
|
36
|
+
REGISTERS: [
|
|
37
|
+
RegistryEntry("Voltage_Phase1", 0x0000, "V", "measurement", "voltage"),
|
|
38
|
+
RegistryEntry("Voltage_Phase2", 0x0002, "V", "measurement", "voltage"),
|
|
39
|
+
RegistryEntry("Voltage_Phase3", 0x0004, "V", "measurement", "voltage"),
|
|
40
|
+
RegistryEntry("Current_Phase1", 0x0006, "A", "measurement", "current"),
|
|
41
|
+
RegistryEntry("Current_Phase2", 0x0008, "A", "measurement", "current"),
|
|
42
|
+
RegistryEntry("Current_Phase3", 0x000A, "A", "measurement", "current"),
|
|
43
|
+
RegistryEntry("Power_Phase1", 0x000C, "W", "measurement", "power"),
|
|
44
|
+
RegistryEntry("Power_Phase2", 0x000E, "W", "measurement", "power"),
|
|
45
|
+
RegistryEntry("Power_Phase3", 0x0010, "W", "measurement", "power"),
|
|
46
|
+
RegistryEntry("Power_VA_Phase1", 0x0012, "VA", "measurement", None),
|
|
47
|
+
RegistryEntry("Power_VA_Phase2", 0x0014, "VA", "measurement", None),
|
|
48
|
+
RegistryEntry("Power_VA_Phase3", 0x0016, "VA", "measurement", None),
|
|
49
|
+
RegistryEntry("Power_VAr_Phase1", 0x0018, "var", "measurement", None),
|
|
50
|
+
RegistryEntry("Power_VAr_Phase2", 0x001A, "var", "measurement", None),
|
|
51
|
+
RegistryEntry("Power_VAr_Phase3", 0x001C, "var", "measurement", None),
|
|
52
|
+
RegistryEntry("Power_Factor_Phase1", 0x001E, "%", "measurement", None),
|
|
53
|
+
RegistryEntry("Power_Factor_Phase2", 0x0020, "%", "measurement", None),
|
|
54
|
+
RegistryEntry("Power_Factor_Phase3", 0x0022, "%", "measurement", None),
|
|
55
|
+
RegistryEntry("Phase_Angle_Phase1", 0x0024, "°", "measurement", None),
|
|
56
|
+
RegistryEntry("Phase_Angle_Phase2", 0x0026, "°", "measurement", None),
|
|
57
|
+
RegistryEntry("Phase_Angle_Phase3", 0x0028, "°", "measurement", None),
|
|
58
|
+
RegistryEntry(
|
|
59
|
+
"Line_to_Neutral_AVG_Volts", 0x002A, "V", "measurement", None
|
|
60
|
+
),
|
|
61
|
+
RegistryEntry("AVG_Current", 0x002E, "A", "measurement", "current"),
|
|
62
|
+
RegistryEntry("Sum_Current", 0x0030, "A", "measurement", "current"),
|
|
63
|
+
RegistryEntry("Total_Power_Watt", 0x0034, "W", "measurement", "power"),
|
|
64
|
+
RegistryEntry("Total_Power_VA", 0x0038, "VA", "measurement", None),
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
BASE: 0x003C,
|
|
69
|
+
LENGTH: 48,
|
|
70
|
+
REGISTERS: [
|
|
71
|
+
RegistryEntry("System_VAr_Total", 0x003C, "var", "measurement", None),
|
|
72
|
+
RegistryEntry(
|
|
73
|
+
"System_Power_Factor_Total", 0x003E, "%", "measurement", None
|
|
74
|
+
),
|
|
75
|
+
RegistryEntry("System_PhaseAngle", 0x0042, "°", "measurement", None),
|
|
76
|
+
RegistryEntry("Frequency", 0x0046, "Hz", "measurement", None),
|
|
77
|
+
RegistryEntry(
|
|
78
|
+
"Import_Energy_kWh_Total", 0x0048, "kWh", "total_increasing", "energy"
|
|
79
|
+
),
|
|
80
|
+
RegistryEntry(
|
|
81
|
+
"Export_Energy_kWh_Total", 0x004A, "kWh", "total_increasing", "energy"
|
|
82
|
+
),
|
|
83
|
+
RegistryEntry(
|
|
84
|
+
"Import_Energy_kVArh_Total", 0x004C, "kvArh", "total_increasing", None
|
|
85
|
+
),
|
|
86
|
+
RegistryEntry(
|
|
87
|
+
"Export_Energy_kVArh_Total", 0x004E, "kvArh", "total_increasing", None
|
|
88
|
+
),
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
BASE: 0x0156,
|
|
93
|
+
LENGTH: 4,
|
|
94
|
+
REGISTERS: [
|
|
95
|
+
RegistryEntry("kWh_Total", 0x0156, "kWh", "total_increasing", "energy"),
|
|
96
|
+
RegistryEntry("kvarh_Total", 0x0158, "kvArh", "total_increasing", None),
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class Sdm630(BasicMqtt):
|
|
103
|
+
"""Represent Sdm630 sensor in BoneIO."""
|
|
104
|
+
|
|
105
|
+
SensorClass = None
|
|
106
|
+
DefaultName = SDM630
|
|
107
|
+
|
|
108
|
+
def __init__(
|
|
109
|
+
self,
|
|
110
|
+
modbus,
|
|
111
|
+
address: str,
|
|
112
|
+
ha_discovery_prefix: str,
|
|
113
|
+
topic_prefix: str,
|
|
114
|
+
ha_discovery: bool = False,
|
|
115
|
+
id: str = DefaultName,
|
|
116
|
+
update_interval: int = 60,
|
|
117
|
+
**kwargs,
|
|
118
|
+
):
|
|
119
|
+
"""Initialize Sdm630 class."""
|
|
120
|
+
super().__init__(
|
|
121
|
+
id=id or address, topic_type=SENSOR, topic_prefix=topic_prefix, **kwargs
|
|
122
|
+
)
|
|
123
|
+
self._topic_prefix = topic_prefix
|
|
124
|
+
self._modbus = modbus
|
|
125
|
+
self._address = address
|
|
126
|
+
self._ha_discovery = ha_discovery
|
|
127
|
+
self._discovery_sent = False
|
|
128
|
+
self._ha_discovery_prefix = ha_discovery_prefix
|
|
129
|
+
self._update_interval = update_interval
|
|
130
|
+
|
|
131
|
+
def _send_ha_autodiscovery(
|
|
132
|
+
self, id: str, sdm_name: str, sensor_id: str, **kwargs
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Send HA autodiscovery information for each SDM sensor."""
|
|
135
|
+
_LOGGER.debug("Sending HA discovery for sensor %s %s.", sdm_name, sensor_id)
|
|
136
|
+
self._send_message(
|
|
137
|
+
topic=(
|
|
138
|
+
f"{self._ha_discovery_prefix}/{SENSOR}/{self._topic_prefix}"
|
|
139
|
+
f"/{id}{sensor_id.replace('_', '').lower()}/config"
|
|
140
|
+
),
|
|
141
|
+
payload=sdm630_availabilty_message(
|
|
142
|
+
topic=self._topic_prefix,
|
|
143
|
+
id=id,
|
|
144
|
+
name=sdm_name,
|
|
145
|
+
sensor_id=sensor_id,
|
|
146
|
+
**kwargs,
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def _send_discovery_for_all_registers(self, register: int = 0) -> bool:
|
|
151
|
+
"""Send discovery message to HA for each register."""
|
|
152
|
+
if register > 0:
|
|
153
|
+
for data in REGISTER_BASE:
|
|
154
|
+
for register in data["registers"]:
|
|
155
|
+
kwargs = {
|
|
156
|
+
"unit_of_measurement": getattr(register, "unit_of_measurement"),
|
|
157
|
+
"state_class": getattr(register, "state_class"),
|
|
158
|
+
"value_template": "{{ value | round(2)}}",
|
|
159
|
+
"sensor_id": getattr(register, "sensor_id"),
|
|
160
|
+
}
|
|
161
|
+
if getattr(register, "device_class"):
|
|
162
|
+
kwargs["device_class"] = getattr(register, "device_class")
|
|
163
|
+
self._send_ha_autodiscovery(
|
|
164
|
+
id=self._id, sdm_name=self._name, **kwargs
|
|
165
|
+
)
|
|
166
|
+
return True
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
async def send_state(self) -> None:
|
|
170
|
+
"""Fetch state periodically and send to MQTT."""
|
|
171
|
+
register = self._modbus.read_single_register(unit=self._address, address=0)
|
|
172
|
+
if not self._discovery_sent and self._ha_discovery:
|
|
173
|
+
self._discovery_sent = self._send_discovery_for_all_registers(register)
|
|
174
|
+
while True:
|
|
175
|
+
payload_online = OFFLINE
|
|
176
|
+
for data in REGISTER_BASE:
|
|
177
|
+
values = self._modbus.read_multiple_registers(
|
|
178
|
+
unit=self._address, address=data[BASE], count=data[LENGTH]
|
|
179
|
+
)
|
|
180
|
+
if payload_online == OFFLINE:
|
|
181
|
+
payload_online = ONLINE
|
|
182
|
+
self._send_message(
|
|
183
|
+
topic=f"{self._topic_prefix}/{self._id}{STATE}",
|
|
184
|
+
payload=payload_online,
|
|
185
|
+
)
|
|
186
|
+
for register in data["registers"]:
|
|
187
|
+
self._send_message(
|
|
188
|
+
topic=f'{self._send_topic}/{getattr(register, "sensor_id")}',
|
|
189
|
+
payload=float32(
|
|
190
|
+
values, data[BASE], getattr(register, "address")
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
await asyncio.sleep(self._update_interval)
|
|
@@ -9,14 +9,9 @@ from boneio.helper.exceptions import I2CError
|
|
|
9
9
|
from boneio.helper import BasicMqtt
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
from boneio.const import STATE, SENSOR, LM75
|
|
13
|
-
|
|
14
|
-
from boneio.helper.exceptions import I2CError
|
|
15
|
-
from boneio.helper import BasicMqtt
|
|
16
|
-
|
|
17
|
-
|
|
18
12
|
class TempSensor(BasicMqtt):
|
|
19
13
|
"""Represent Temp sensor in BoneIO."""
|
|
14
|
+
|
|
20
15
|
SensorClass = None
|
|
21
16
|
DefaultName = TEMPERATURE
|
|
22
17
|
|
|
@@ -45,10 +40,13 @@ class TempSensor(BasicMqtt):
|
|
|
45
40
|
|
|
46
41
|
class LM75Sensor(TempSensor):
|
|
47
42
|
"""Represent LM75 sensor in BoneIO."""
|
|
43
|
+
|
|
48
44
|
SensorClass = PCT2075
|
|
49
45
|
DefaultName = LM75
|
|
50
46
|
|
|
47
|
+
|
|
51
48
|
class MCP9808Sensor(TempSensor):
|
|
52
49
|
"""Represent MCP9808 sensor in BoneIO."""
|
|
50
|
+
|
|
53
51
|
SensorClass = MCP9808
|
|
54
52
|
DefaultName = MCP_TEMP_9808
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: boneio
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Python
|
|
5
|
-
Home-page: https://github.com/
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Python App for BoneIO
|
|
5
|
+
Home-page: https://github.com/boneIO-eu/app_bbb
|
|
6
6
|
License: GNU General Public License v3.0
|
|
7
|
-
Download-URL: https://github.com/
|
|
7
|
+
Download-URL: https://github.com/boneIO-eu/app_bbb/archive/0.1.2.zip
|
|
8
8
|
Platform: UNKNOWN
|
|
9
9
|
Classifier: Programming Language :: Python
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.7
|
|
@@ -16,16 +16,24 @@ License-File: LICENSE
|
|
|
16
16
|
Example usage:
|
|
17
17
|
boneio run -dd -c config.yaml
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
19
|
# Installation instructions
|
|
22
20
|
|
|
23
21
|
```
|
|
24
22
|
sudo apt-get install libopenjp2-7-dev libatlas-base-dev python3-venv
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
mkdir ~/boneio
|
|
24
|
+
python3 -m venv ~/boneio/venv
|
|
25
|
+
source ~/boneio/venv/bin/activate
|
|
26
|
+
pip3 install --upgrade boneio
|
|
27
|
+
cp ~/venv/lib/python3.7/site-packages/boneio/example_config/*.yaml ~/boneio/
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Edit config.yaml
|
|
31
|
+
|
|
32
|
+
# Start app
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
source ~/boneio/venv/bin/activate
|
|
36
|
+
boneio run -c ~/boneio/config.yaml -dd
|
|
37
|
+
```
|
|
30
38
|
|
|
31
39
|
|
|
@@ -7,6 +7,7 @@ boneio/__init__.py
|
|
|
7
7
|
boneio/bonecli.py
|
|
8
8
|
boneio/const.py
|
|
9
9
|
boneio/manager.py
|
|
10
|
+
boneio/modbus.py
|
|
10
11
|
boneio/mqtt_client.py
|
|
11
12
|
boneio/oled.py
|
|
12
13
|
boneio/schema.yaml
|
|
@@ -42,4 +43,5 @@ boneio/relay/mcp.py
|
|
|
42
43
|
boneio/sensor/__init__.py
|
|
43
44
|
boneio/sensor/adc.py
|
|
44
45
|
boneio/sensor/gpio.py
|
|
46
|
+
boneio/sensor/sdm630.py
|
|
45
47
|
boneio/sensor/temp/__init__.py
|
|
@@ -23,7 +23,7 @@ luma.core==2.3.1
|
|
|
23
23
|
luma.oled==3.8.1
|
|
24
24
|
numpy==1.21.4
|
|
25
25
|
paho-mqtt==1.6.1
|
|
26
|
-
Pillow==
|
|
26
|
+
Pillow==9.0.0
|
|
27
27
|
pkg-resources==0.0.0
|
|
28
28
|
psutil==5.8.0
|
|
29
29
|
pyaml==21.10.1
|
|
@@ -33,6 +33,8 @@ pyparsing==2.2.0
|
|
|
33
33
|
PyQRCode==1.2.1
|
|
34
34
|
pyserial==3.5
|
|
35
35
|
pyusb==1.2.1
|
|
36
|
+
pymodbus==2.5.3
|
|
37
|
+
pyserial-asyncio==0.6
|
|
36
38
|
PyYAML==6.0
|
|
37
39
|
recordclass==0.16.3
|
|
38
40
|
requests==2.26.0
|
|
@@ -12,11 +12,11 @@ with open("boneio/version.py") as f:
|
|
|
12
12
|
setup(
|
|
13
13
|
name="boneio",
|
|
14
14
|
version=__version__, # type: ignore # noqa: F821,
|
|
15
|
-
description="Python
|
|
15
|
+
description="Python App for BoneIO",
|
|
16
16
|
long_description=long_description,
|
|
17
17
|
long_description_content_type="text/markdown",
|
|
18
|
-
url="https://github.com/
|
|
19
|
-
download_url="https://github.com/
|
|
18
|
+
url="https://github.com/boneIO-eu/app_bbb",
|
|
19
|
+
download_url="https://github.com/boneIO-eu/app_bbb/archive/{}.zip".format(
|
|
20
20
|
__version__ # type: ignore # noqa: F821
|
|
21
21
|
),
|
|
22
22
|
packages=find_packages(exclude=["tests*"]),
|
|
@@ -44,9 +44,12 @@ setup(
|
|
|
44
44
|
"luma.core==2.3.1",
|
|
45
45
|
"luma.oled==3.8.1",
|
|
46
46
|
"numpy==1.21.4",
|
|
47
|
+
"Pillow==9.0.0",
|
|
47
48
|
"paho-mqtt==1.6.1",
|
|
48
49
|
"psutil==5.8.0",
|
|
49
50
|
"pyaml==21.10.1",
|
|
51
|
+
"pymodbus==2.5.3",
|
|
52
|
+
"pyserial-asyncio==0.6",
|
|
50
53
|
"typing-extensions==3.10.0.2",
|
|
51
54
|
],
|
|
52
55
|
entry_points={"console_scripts": ["boneio=boneio.bonecli:cli"]},
|
boneio-0.1.0.dev26/README.md
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Example usage:
|
|
2
|
-
boneio run -dd -c config.yaml
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Installation instructions
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
sudo apt-get install libopenjp2-7-dev libatlas-base-dev python3-venv
|
|
10
|
-
python3 -m venv venv
|
|
11
|
-
wget https://raw.githubusercontent.com/pszafer/boneIO-1/main/Code/BoneIO/example_config/config.yaml
|
|
12
|
-
wget https://raw.githubusercontent.com/pszafer/boneIO-1/main/Code/BoneIO/example_config/output.yaml
|
|
13
|
-
```
|
|
14
|
-
|
|
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
|