boneIO 0.1.0.dev7__tar.gz → 0.1.0.dev8__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.dev7 → boneio-0.1.0.dev8}/MANIFEST.in +1 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/PKG-INFO +2 -2
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/bonecli.py +15 -12
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/const.py +5 -1
- boneio-0.1.0.dev8/boneio/fonts/danube__.ttf +0 -0
- boneio-0.1.0.dev8/boneio/helper/__init__.py +14 -0
- boneio-0.1.0.dev8/boneio/helper/oled.py +15 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/helper/stats.py +21 -31
- boneio-0.1.0.dev8/boneio/input/__init__.py +5 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/input/gpio.py +11 -10
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/manager.py +53 -26
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/mqtt_client.py +3 -1
- boneio-0.1.0.dev8/boneio/oled.py +108 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/relay/basic.py +11 -2
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/relay/gpio.py +3 -2
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/relay/mcp.py +16 -3
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/schema.yaml +7 -1
- boneio-0.1.0.dev8/boneio/version.py +2 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio.egg-info/PKG-INFO +2 -2
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio.egg-info/SOURCES.txt +2 -0
- boneio-0.1.0.dev7/boneio/helper/__init__.py +0 -4
- boneio-0.1.0.dev7/boneio/input/__init__.py +0 -5
- boneio-0.1.0.dev7/boneio/oled.py +0 -82
- boneio-0.1.0.dev7/boneio/version.py +0 -2
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/README.md +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/__init__.py +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/config.example.yaml +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/config.yaml +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/helper/gpio.py +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/helper/yaml.py +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio/relay/__init__.py +1 -1
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio.egg-info/dependency_links.txt +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio.egg-info/entry_points.txt +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio.egg-info/requires.txt +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/boneio.egg-info/top_level.txt +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/requirements.txt +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/setup.cfg +0 -0
- {boneio-0.1.0.dev7 → boneio-0.1.0.dev8}/setup.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: boneio
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev8
|
|
4
4
|
Summary: Python Helper for BoneIO
|
|
5
5
|
Home-page: https://github.com/maciejk1984/boneIO
|
|
6
6
|
License: GNU General Public License v3.0
|
|
7
|
-
Download-URL: https://github.com/maciejk1984/boneIO/archive/0.1.0.
|
|
7
|
+
Download-URL: https://github.com/maciejk1984/boneIO/archive/0.1.0.dev8.zip
|
|
8
8
|
Platform: UNKNOWN
|
|
9
9
|
Classifier: Programming Language :: Python
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.7
|
|
@@ -1,28 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
import click
|
|
1
|
+
import asyncio
|
|
3
2
|
import logging
|
|
4
|
-
|
|
3
|
+
import os
|
|
4
|
+
from functools import wraps
|
|
5
|
+
|
|
6
|
+
import click
|
|
5
7
|
from colorlog import ColoredFormatter
|
|
6
|
-
|
|
7
|
-
from .manager import Manager
|
|
8
|
+
|
|
8
9
|
from .const import (
|
|
9
10
|
ENABLED,
|
|
10
11
|
GPIO_INPUT,
|
|
11
12
|
HA_DISCOVERY,
|
|
13
|
+
HOST,
|
|
12
14
|
MCP23017,
|
|
15
|
+
MQTT,
|
|
13
16
|
OLED,
|
|
14
17
|
OUTPUT,
|
|
15
18
|
PAHO,
|
|
16
|
-
|
|
17
|
-
HOST,
|
|
19
|
+
PASSWORD,
|
|
18
20
|
TOPIC_PREFIX,
|
|
19
21
|
USERNAME,
|
|
20
|
-
PASSWORD,
|
|
21
22
|
)
|
|
23
|
+
from .helper import CustomValidator, load_yaml_file
|
|
24
|
+
from .manager import Manager
|
|
25
|
+
from .mqtt_client import MQTTClient
|
|
22
26
|
from .version import __version__
|
|
23
|
-
import asyncio
|
|
24
|
-
import os
|
|
25
|
-
from functools import wraps
|
|
26
27
|
|
|
27
28
|
_LOGGER = logging.getLogger(__name__)
|
|
28
29
|
logging.basicConfig(level=logging.INFO)
|
|
@@ -103,6 +104,7 @@ _options = [
|
|
|
103
104
|
@coro
|
|
104
105
|
async def run(ctx, debug: int, config: str, mqttpassword: str = ""):
|
|
105
106
|
"""Run BoneIO."""
|
|
107
|
+
_LOGGER.info("BoneIO starting.")
|
|
106
108
|
if debug == 0:
|
|
107
109
|
logging.getLogger().setLevel(logging.INFO)
|
|
108
110
|
if debug > 0:
|
|
@@ -121,6 +123,7 @@ async def run(ctx, debug: int, config: str, mqttpassword: str = ""):
|
|
|
121
123
|
_LOGGER.info("Missing file.")
|
|
122
124
|
return
|
|
123
125
|
_config = v.normalized(config_yaml)
|
|
126
|
+
_LOGGER.info("Connecting to MQTT.")
|
|
124
127
|
client = MQTTClient(
|
|
125
128
|
host=_config[MQTT][HOST],
|
|
126
129
|
username=_config[MQTT].get(USERNAME),
|
|
@@ -138,8 +141,8 @@ async def run(ctx, debug: int, config: str, mqttpassword: str = ""):
|
|
|
138
141
|
oled=_config.get(OLED),
|
|
139
142
|
)
|
|
140
143
|
tasks = set()
|
|
141
|
-
tasks.add(client.start_client(manager))
|
|
142
144
|
tasks.update(manager.get_tasks())
|
|
145
|
+
tasks.add(client.start_client(manager))
|
|
143
146
|
await asyncio.gather(*tasks)
|
|
144
147
|
|
|
145
148
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# from typing import Literal
|
|
2
|
-
from typing_extensions import Literal
|
|
3
2
|
from datetime import timedelta
|
|
4
3
|
|
|
4
|
+
from typing_extensions import Literal
|
|
5
|
+
|
|
5
6
|
DEBOUNCE_DURATION = timedelta(seconds=0.2)
|
|
6
7
|
LONG_PRESS_DURATION = timedelta(seconds=0.7)
|
|
7
8
|
DELAY_DURATION = 0.1
|
|
@@ -21,6 +22,7 @@ HOST = "host"
|
|
|
21
22
|
USERNAME = "username"
|
|
22
23
|
PASSWORD = "password"
|
|
23
24
|
TOPIC_PREFIX = "topic_prefix"
|
|
25
|
+
HOMEASSISTANT = "homeassistant"
|
|
24
26
|
HA_DISCOVERY = "ha_discovery"
|
|
25
27
|
ENABLED = "enabled"
|
|
26
28
|
GPIO_INPUT = "gpio_input"
|
|
@@ -52,4 +54,6 @@ MEMORY = "memory"
|
|
|
52
54
|
SWAP = "swap"
|
|
53
55
|
WHITE = "white"
|
|
54
56
|
OLED = "oled"
|
|
57
|
+
FONTS = "fonts"
|
|
55
58
|
OledDataTypes = Literal[UPTIME, NETWORK, CPU, DISK, MEMORY, SWAP, OUTPUT]
|
|
59
|
+
INIT_SLEEP = "init_sleep"
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Helper dir for BoneIO."""
|
|
2
|
+
from .gpio import edge_detect, setup_input
|
|
3
|
+
from .oled import make_font
|
|
4
|
+
from .stats import HostData
|
|
5
|
+
from .yaml import CustomValidator, load_yaml_file
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"CustomValidator",
|
|
9
|
+
"load_yaml_file",
|
|
10
|
+
"HostData",
|
|
11
|
+
"setup_input",
|
|
12
|
+
"edge_detect",
|
|
13
|
+
"make_font",
|
|
14
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from PIL import ImageFont
|
|
4
|
+
|
|
5
|
+
from ..const import FONTS
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def make_font(name: str, size: int, local: bool = False):
|
|
9
|
+
"""Prepare ImageFont for Oled screen."""
|
|
10
|
+
font_path = (
|
|
11
|
+
name
|
|
12
|
+
if not local
|
|
13
|
+
else os.path.join(os.path.dirname(__file__), "..", FONTS, name)
|
|
14
|
+
)
|
|
15
|
+
return ImageFont.truetype(font_path, size)
|
|
@@ -1,27 +1,15 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import
|
|
3
|
-
from math import floor
|
|
4
|
-
from ..const import (
|
|
5
|
-
GIGABYTE,
|
|
6
|
-
MEGABYTE,
|
|
7
|
-
NETWORK,
|
|
8
|
-
DISK,
|
|
9
|
-
MEMORY,
|
|
10
|
-
CPU,
|
|
11
|
-
SWAP,
|
|
12
|
-
UPTIME,
|
|
13
|
-
HOST,
|
|
14
|
-
OUTPUT,
|
|
15
|
-
OledDataTypes,
|
|
16
|
-
)
|
|
2
|
+
import socket
|
|
17
3
|
import time
|
|
4
|
+
from functools import partial
|
|
5
|
+
from math import floor
|
|
6
|
+
from typing import Callable
|
|
7
|
+
|
|
8
|
+
import psutil
|
|
9
|
+
|
|
10
|
+
from ..const import CPU, DISK, GIGABYTE, HOST, MEGABYTE, MEMORY, NETWORK, SWAP, UPTIME
|
|
18
11
|
|
|
19
|
-
intervals = (
|
|
20
|
-
("d", 86400), # 60 * 60 * 24
|
|
21
|
-
("h", 3600), # 60 * 60
|
|
22
|
-
("m", 60),
|
|
23
|
-
("s", 1),
|
|
24
|
-
)
|
|
12
|
+
intervals = (("d", 86400), ("h", 3600), ("m", 60))
|
|
25
13
|
|
|
26
14
|
|
|
27
15
|
def display_time(seconds):
|
|
@@ -46,7 +34,7 @@ async def get_network_info(host_data):
|
|
|
46
34
|
NETWORK,
|
|
47
35
|
{"ip": net[0].address, "mask": net[0].netmask, "mac": net[2].address},
|
|
48
36
|
)
|
|
49
|
-
await asyncio.sleep(
|
|
37
|
+
await asyncio.sleep(60)
|
|
50
38
|
|
|
51
39
|
|
|
52
40
|
async def get_cpu_info(host_data):
|
|
@@ -114,7 +102,7 @@ async def get_uptime(host_data):
|
|
|
114
102
|
while True:
|
|
115
103
|
uptime = display_time(time.clock_gettime(time.CLOCK_BOOTTIME))
|
|
116
104
|
host_data.write_uptime(uptime)
|
|
117
|
-
await asyncio.sleep(
|
|
105
|
+
await asyncio.sleep(30)
|
|
118
106
|
|
|
119
107
|
|
|
120
108
|
host_stats = {
|
|
@@ -126,37 +114,39 @@ host_stats = {
|
|
|
126
114
|
UPTIME: get_uptime,
|
|
127
115
|
}
|
|
128
116
|
|
|
129
|
-
import socket
|
|
130
|
-
|
|
131
117
|
|
|
132
118
|
class HostData:
|
|
133
119
|
"""Helper class to store host data."""
|
|
134
120
|
|
|
135
121
|
data = {UPTIME: {}, NETWORK: {}, CPU: {}, DISK: {}, MEMORY: {}, SWAP: {}}
|
|
136
122
|
|
|
137
|
-
def __init__(self, output: dict) -> None:
|
|
123
|
+
def __init__(self, output: dict, callback: Callable) -> None:
|
|
138
124
|
"""Initialize HostData."""
|
|
139
125
|
self._hostname = socket.gethostname()
|
|
140
126
|
self.data[UPTIME] = {HOST: self._hostname, UPTIME: 0}
|
|
141
127
|
self._output = output
|
|
128
|
+
self._callback = callback
|
|
129
|
+
self._loop = asyncio.get_running_loop()
|
|
142
130
|
|
|
143
131
|
def write(self, type: str, data: dict) -> None:
|
|
144
132
|
"""Write data of chosen type."""
|
|
145
133
|
self.data[type] = data
|
|
134
|
+
self._loop.call_soon_threadsafe(partial(self._callback, type))
|
|
146
135
|
|
|
147
136
|
def write_uptime(self, uptime: str) -> None:
|
|
148
137
|
"""Write uptime."""
|
|
149
138
|
self.data[UPTIME][UPTIME] = uptime
|
|
139
|
+
self._loop.call_soon_threadsafe(partial(self._callback, UPTIME))
|
|
150
140
|
|
|
151
|
-
def get(self, type:
|
|
141
|
+
def get(self, type: str) -> dict:
|
|
152
142
|
"""Get saved stats."""
|
|
153
|
-
if type
|
|
154
|
-
return self._get_output()
|
|
143
|
+
if type in self._output:
|
|
144
|
+
return self._get_output(type)
|
|
155
145
|
return self.data[type]
|
|
156
146
|
|
|
157
|
-
def _get_output(self) -> dict:
|
|
147
|
+
def _get_output(self, type: str) -> dict:
|
|
158
148
|
"""Get stats for output."""
|
|
159
149
|
out = {}
|
|
160
|
-
for output in self._output.values():
|
|
150
|
+
for output in self._output[type].values():
|
|
161
151
|
out[output.id] = output.state
|
|
162
152
|
return out
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
"""GPIOInputButton to receive signals."""
|
|
2
|
-
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
3
4
|
import subprocess
|
|
4
|
-
from functools import partial
|
|
5
|
-
from typing import Callable, Any
|
|
6
5
|
from datetime import datetime
|
|
7
|
-
import
|
|
8
|
-
import
|
|
6
|
+
from functools import partial
|
|
7
|
+
from typing import Any, Callable
|
|
8
|
+
|
|
9
9
|
from ..const import (
|
|
10
10
|
CONFIG_PIN,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
DEBOUNCE_DURATION,
|
|
12
|
+
DELAY_DURATION,
|
|
13
13
|
DOUBLE,
|
|
14
|
+
GPIO,
|
|
14
15
|
LONG,
|
|
15
|
-
ClickTypes,
|
|
16
|
-
DEBOUNCE_DURATION,
|
|
17
16
|
LONG_PRESS_DURATION,
|
|
18
|
-
DELAY_DURATION,
|
|
19
17
|
SECOND_DELAY_DURATION,
|
|
18
|
+
SINGLE,
|
|
19
|
+
ClickTypes,
|
|
20
20
|
)
|
|
21
|
+
from ..helper.gpio import edge_detect, read_input, setup_input
|
|
21
22
|
|
|
22
23
|
_LOGGER = logging.getLogger(__name__)
|
|
23
24
|
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
from .input import GpioInputButton
|
|
2
|
-
from .relay import GpioRelay, MCPRelay
|
|
3
|
-
from .oled import Oled
|
|
4
|
-
from .helper.stats import host_stats, HostData
|
|
5
|
-
from typing import Set
|
|
6
1
|
import asyncio
|
|
7
2
|
import logging
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Callable, List, Optional, Set, Union
|
|
5
|
+
|
|
6
|
+
from adafruit_mcp230xx.mcp23017 import MCP23017
|
|
7
|
+
from board import SCL, SDA
|
|
8
|
+
from busio import I2C
|
|
8
9
|
|
|
9
10
|
from .const import (
|
|
10
11
|
ACTION,
|
|
11
|
-
OUTPUT,
|
|
12
12
|
ACTIONS,
|
|
13
13
|
ADDRESS,
|
|
14
14
|
GPIO,
|
|
15
15
|
HA_TYPE,
|
|
16
|
+
HOMEASSISTANT,
|
|
16
17
|
ID,
|
|
18
|
+
INIT_SLEEP,
|
|
17
19
|
KIND,
|
|
18
20
|
MCP,
|
|
19
21
|
MCP_ID,
|
|
22
|
+
OFF,
|
|
23
|
+
ON,
|
|
24
|
+
ONLINE,
|
|
20
25
|
OUTPUT,
|
|
21
26
|
PIN,
|
|
22
27
|
RELAY,
|
|
23
|
-
ON,
|
|
24
|
-
OFF,
|
|
25
|
-
ONLINE,
|
|
26
28
|
STATE,
|
|
27
29
|
ClickTypes,
|
|
28
30
|
)
|
|
29
|
-
from
|
|
30
|
-
import
|
|
31
|
-
import
|
|
32
|
-
from
|
|
33
|
-
from board import SCL, SDA
|
|
34
|
-
from adafruit_mcp230xx.mcp23017 import MCP23017
|
|
31
|
+
from .helper.stats import HostData, host_stats
|
|
32
|
+
from .input import GpioInputButton
|
|
33
|
+
from .oled import Oled
|
|
34
|
+
from .relay import GpioRelay, MCPRelay
|
|
35
35
|
|
|
36
36
|
_LOGGER = logging.getLogger(__name__)
|
|
37
37
|
|
|
@@ -67,12 +67,13 @@ class Manager:
|
|
|
67
67
|
relay_pins: List,
|
|
68
68
|
input_pins: List,
|
|
69
69
|
ha_discovery: bool = True,
|
|
70
|
-
ha_discovery_prefix: str =
|
|
70
|
+
ha_discovery_prefix: str = HOMEASSISTANT,
|
|
71
71
|
mcp23017: Optional[List] = None,
|
|
72
72
|
oled: bool = False,
|
|
73
|
-
web: bool =
|
|
73
|
+
web: bool = True,
|
|
74
74
|
) -> None:
|
|
75
75
|
"""Initialize the manager."""
|
|
76
|
+
_LOGGER.info("Initializing manager module.")
|
|
76
77
|
loop = asyncio.get_event_loop()
|
|
77
78
|
self._host_data = None
|
|
78
79
|
|
|
@@ -82,37 +83,54 @@ class Manager:
|
|
|
82
83
|
self._input_pins = input_pins
|
|
83
84
|
self._i2cbusio = I2C(SCL, SDA)
|
|
84
85
|
self._mcp = {}
|
|
86
|
+
self._grouped_outputs = {}
|
|
85
87
|
self._oled = None
|
|
86
88
|
self._tasks: List[asyncio.Task] = []
|
|
87
89
|
|
|
88
90
|
if mcp23017:
|
|
89
91
|
for mcp in mcp23017:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
id = mcp[ID] or mcp[ADDRESS]
|
|
93
|
+
self._mcp[id] = MCP23017(i2c=self._i2cbusio, address=mcp[ADDRESS])
|
|
94
|
+
sleep_time = mcp.get(INIT_SLEEP, 0)
|
|
95
|
+
_LOGGER.debug(
|
|
96
|
+
f"Sleeping for {sleep_time}s while MCP {id} is initializing."
|
|
92
97
|
)
|
|
98
|
+
time.sleep(sleep_time)
|
|
99
|
+
self._grouped_outputs[id] = {}
|
|
93
100
|
|
|
94
101
|
def configure_relay(gpio: dict) -> Any:
|
|
95
102
|
"""Configure kind of relay. Most common MCP."""
|
|
96
103
|
if gpio[KIND] == MCP:
|
|
97
|
-
|
|
104
|
+
mcp_id = gpio.get(MCP_ID, "")
|
|
105
|
+
mcp = self._mcp.get(mcp_id)
|
|
98
106
|
if not mcp:
|
|
99
107
|
_LOGGER.error("No such MCP configured!")
|
|
100
108
|
return
|
|
101
|
-
|
|
109
|
+
relay_id = gpio[ID]
|
|
110
|
+
mcp_relay = MCPRelay(
|
|
102
111
|
pin=int(gpio[PIN]),
|
|
103
|
-
id=
|
|
112
|
+
id=relay_id,
|
|
104
113
|
send_message=self.send_message,
|
|
105
114
|
topic_prefix=topic_prefix,
|
|
106
115
|
mcp=mcp,
|
|
116
|
+
mcp_id=mcp_id,
|
|
107
117
|
ha_type=gpio[HA_TYPE],
|
|
118
|
+
callback=lambda: self._host_data_callback(mcp_id),
|
|
108
119
|
)
|
|
120
|
+
self._grouped_outputs[mcp_id][relay_id] = mcp_relay
|
|
121
|
+
return mcp_relay
|
|
109
122
|
elif gpio[KIND] == GPIO:
|
|
110
|
-
|
|
123
|
+
if not GPIO in self._grouped_outputs:
|
|
124
|
+
self._grouped_outputs[GPIO] = {}
|
|
125
|
+
gpio_relay = GpioRelay(
|
|
111
126
|
pin=gpio[PIN],
|
|
112
127
|
id=gpio[ID],
|
|
113
128
|
send_message=self.send_message,
|
|
114
129
|
topic_prefix=topic_prefix,
|
|
130
|
+
callback=lambda: self._host_data_callback(GPIO),
|
|
115
131
|
)
|
|
132
|
+
self._grouped_outputs[GPIO][gpio[ID]] = gpio_relay
|
|
133
|
+
return gpio_relay
|
|
116
134
|
|
|
117
135
|
self.output = {gpio[ID]: configure_relay(gpio) for gpio in relay_pins}
|
|
118
136
|
for out in self.output.values():
|
|
@@ -137,15 +155,24 @@ class Manager:
|
|
|
137
155
|
]
|
|
138
156
|
|
|
139
157
|
if oled or web:
|
|
140
|
-
self._host_data = HostData(
|
|
158
|
+
self._host_data = HostData(
|
|
159
|
+
output=self._grouped_outputs, callback=self._host_data_callback
|
|
160
|
+
)
|
|
141
161
|
for f in host_stats.values():
|
|
142
162
|
self._tasks.append(asyncio.create_task(f(self._host_data)))
|
|
163
|
+
_LOGGER.debug("Gathering host data enabled.")
|
|
143
164
|
|
|
144
165
|
if oled:
|
|
145
|
-
self._oled = Oled(
|
|
146
|
-
|
|
166
|
+
self._oled = Oled(
|
|
167
|
+
host_data=self._host_data, output_groups=list(self._grouped_outputs)
|
|
168
|
+
)
|
|
147
169
|
|
|
148
170
|
self.send_message(topic=f"{topic_prefix}/{STATE}", payload=ONLINE)
|
|
171
|
+
_LOGGER.info("BoneIO manager is ready.")
|
|
172
|
+
|
|
173
|
+
def _host_data_callback(self, type):
|
|
174
|
+
if self._oled:
|
|
175
|
+
self._oled.handle_data_update(type)
|
|
149
176
|
|
|
150
177
|
def get_tasks(self) -> Set[asyncio.Task]:
|
|
151
178
|
return self._tasks
|
|
@@ -6,10 +6,12 @@ import uuid
|
|
|
6
6
|
from contextlib import AsyncExitStack
|
|
7
7
|
from typing import Any, Callable, Optional, Set, Union
|
|
8
8
|
|
|
9
|
-
from asyncio_mqtt import Client as AsyncioClient, MqttError
|
|
10
9
|
import paho.mqtt.client as mqtt
|
|
10
|
+
from asyncio_mqtt import Client as AsyncioClient
|
|
11
|
+
from asyncio_mqtt import MqttError
|
|
11
12
|
from paho.mqtt.properties import Properties
|
|
12
13
|
from paho.mqtt.subscribeoptions import SubscribeOptions
|
|
14
|
+
|
|
13
15
|
from .const import PAHO
|
|
14
16
|
|
|
15
17
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from itertools import cycle
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from luma.core.interface.serial import i2c
|
|
6
|
+
from luma.core.render import canvas
|
|
7
|
+
from luma.oled.device import sh1106
|
|
8
|
+
from PIL import ImageDraw
|
|
9
|
+
|
|
10
|
+
from .const import CPU, DISK, MEMORY, NETWORK, OLED_PIN, SWAP, UPTIME, WHITE
|
|
11
|
+
from .helper import HostData, edge_detect, make_font, setup_input
|
|
12
|
+
from .input import configure_pin
|
|
13
|
+
|
|
14
|
+
_LOGGER = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
fontBig = make_font("DejaVuSans.ttf", 12)
|
|
18
|
+
fontSmall = make_font("DejaVuSans.ttf", 10)
|
|
19
|
+
fontExtraSmall = make_font("DejaVuSans.ttf", 7)
|
|
20
|
+
danube = make_font("danube__.ttf", 15, local=True)
|
|
21
|
+
|
|
22
|
+
screen_order = [UPTIME, NETWORK, CPU, DISK, MEMORY, SWAP]
|
|
23
|
+
|
|
24
|
+
STANDARD_ROWS = [17, 32, 47]
|
|
25
|
+
UPTIME_ROWS = list(range(25, 40, 10))
|
|
26
|
+
OUTPUT_ROWS = list(range(14, 60, 6))
|
|
27
|
+
OUTPUT_COLS = range(0, 101, 50)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Oled:
|
|
31
|
+
"""Oled display class."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, host_data: HostData, output_groups: List[str]) -> None:
|
|
34
|
+
"""Initialize OLED screen."""
|
|
35
|
+
self._screen_order = cycle(screen_order + output_groups)
|
|
36
|
+
self._output_groups = output_groups
|
|
37
|
+
self._current_screen = next(self._screen_order)
|
|
38
|
+
self._host_data = host_data
|
|
39
|
+
configure_pin(OLED_PIN)
|
|
40
|
+
setup_input(OLED_PIN)
|
|
41
|
+
edge_detect(OLED_PIN, callback=self._handle_press, bounce=50)
|
|
42
|
+
serial = i2c(port=2, address=0x3C)
|
|
43
|
+
self._device = sh1106(serial)
|
|
44
|
+
_LOGGER.debug("Configuring OLED screen.")
|
|
45
|
+
|
|
46
|
+
def _draw_standard(self, data: dict, draw: ImageDraw) -> None:
|
|
47
|
+
"""Draw standard information about host screen."""
|
|
48
|
+
draw.text((1, 1), self._current_screen, font=fontBig, fill=WHITE)
|
|
49
|
+
i = 0
|
|
50
|
+
for k in data:
|
|
51
|
+
draw.text(
|
|
52
|
+
(3, STANDARD_ROWS[i]),
|
|
53
|
+
f"{k} {data[k]}",
|
|
54
|
+
font=fontSmall,
|
|
55
|
+
fill=WHITE,
|
|
56
|
+
)
|
|
57
|
+
i += 1
|
|
58
|
+
|
|
59
|
+
def _draw_uptime(self, data: dict, draw: ImageDraw) -> None:
|
|
60
|
+
"""Draw uptime screen with boneIO logo."""
|
|
61
|
+
draw.text((3, 5), "bone", font=danube, fill=WHITE)
|
|
62
|
+
draw.text((53, 5), "iO", font=danube, fill=WHITE)
|
|
63
|
+
i = 0
|
|
64
|
+
for k in data:
|
|
65
|
+
draw.text(
|
|
66
|
+
(3, UPTIME_ROWS[i]),
|
|
67
|
+
f"{k}: {data[k]}",
|
|
68
|
+
font=fontSmall,
|
|
69
|
+
fill=WHITE,
|
|
70
|
+
)
|
|
71
|
+
i += 1
|
|
72
|
+
|
|
73
|
+
def _draw_output(self, data: dict, draw: ImageDraw) -> None:
|
|
74
|
+
"Draw outputs of GPIO/MCP relays."
|
|
75
|
+
cols = cycle(OUTPUT_COLS)
|
|
76
|
+
draw.text((1, 1), f"Relay {self._current_screen}", font=fontSmall, fill=WHITE)
|
|
77
|
+
i = 0
|
|
78
|
+
j = next(cols)
|
|
79
|
+
for k in data:
|
|
80
|
+
if len(OUTPUT_ROWS) == i:
|
|
81
|
+
j = next(cols)
|
|
82
|
+
i = 0
|
|
83
|
+
draw.text(
|
|
84
|
+
(j, OUTPUT_ROWS[i]), f"{k} {data[k]}", font=fontExtraSmall, fill=WHITE
|
|
85
|
+
)
|
|
86
|
+
i += 1
|
|
87
|
+
|
|
88
|
+
def render_display(self) -> None:
|
|
89
|
+
"""Render display."""
|
|
90
|
+
data = self._host_data.get(self._current_screen)
|
|
91
|
+
if data:
|
|
92
|
+
with canvas(self._device) as draw:
|
|
93
|
+
if self._current_screen in self._output_groups:
|
|
94
|
+
self._draw_output(data, draw)
|
|
95
|
+
elif self._current_screen == UPTIME:
|
|
96
|
+
self._draw_uptime(data, draw)
|
|
97
|
+
else:
|
|
98
|
+
self._draw_standard(data, draw)
|
|
99
|
+
|
|
100
|
+
def handle_data_update(self, type: str):
|
|
101
|
+
"""Callback to handle new data present into screen."""
|
|
102
|
+
if type == self._current_screen:
|
|
103
|
+
self.render_display()
|
|
104
|
+
|
|
105
|
+
def _handle_press(self, pin: any) -> None:
|
|
106
|
+
"""Handle press of PIN for OLED display."""
|
|
107
|
+
self._current_screen = next(self._screen_order)
|
|
108
|
+
self.render_display()
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Basic Relay module."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
from typing import Callable, Union
|
|
5
|
-
from ..const import STATE, RELAY, ON, OFF, SWITCH
|
|
6
4
|
import logging
|
|
5
|
+
from typing import Callable, Union
|
|
6
|
+
|
|
7
|
+
from ..const import OFF, ON, RELAY, STATE, SWITCH
|
|
7
8
|
|
|
8
9
|
_LOGGER = logging.getLogger(__name__)
|
|
9
10
|
|
|
@@ -14,6 +15,7 @@ class BasicRelay:
|
|
|
14
15
|
def __init__(
|
|
15
16
|
self,
|
|
16
17
|
send_message: Callable[[str, Union[str, dict]], None],
|
|
18
|
+
callback: Callable,
|
|
17
19
|
topic_prefix: str,
|
|
18
20
|
id: str = None,
|
|
19
21
|
ha_type=SWITCH,
|
|
@@ -25,8 +27,14 @@ class BasicRelay:
|
|
|
25
27
|
self._relay_topic = f"{topic_prefix}/{RELAY}/"
|
|
26
28
|
self._send_topic = f"{self._relay_topic}{self.id}"
|
|
27
29
|
self._state = False
|
|
30
|
+
self._callback = callback
|
|
28
31
|
self._loop = asyncio.get_running_loop()
|
|
29
32
|
|
|
33
|
+
@property
|
|
34
|
+
def is_mcp_type(self) -> bool:
|
|
35
|
+
"""Check if relay is mcp type."""
|
|
36
|
+
return False
|
|
37
|
+
|
|
30
38
|
@property
|
|
31
39
|
def id(self) -> bool:
|
|
32
40
|
"""Id of the relay."""
|
|
@@ -45,6 +53,7 @@ class BasicRelay:
|
|
|
45
53
|
topic=self._send_topic,
|
|
46
54
|
payload={STATE: state},
|
|
47
55
|
)
|
|
56
|
+
self._loop.call_soon_threadsafe(self._callback)
|
|
48
57
|
|
|
49
58
|
def toggle(self) -> None:
|
|
50
59
|
"""Toggle relay."""
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
Created just in case.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from ..helper.gpio import setup_output, read_input, write_output, HIGH, LOW
|
|
7
|
-
from .basic import BasicRelay
|
|
8
6
|
import logging
|
|
9
7
|
|
|
8
|
+
from ..helper.gpio import HIGH, LOW, read_input, setup_output, write_output
|
|
9
|
+
from .basic import BasicRelay
|
|
10
|
+
|
|
10
11
|
_LOGGER = logging.getLogger(__name__)
|
|
11
12
|
|
|
12
13
|
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
"""MCP23017 Relay module."""
|
|
2
2
|
|
|
3
|
-
from .basic import BasicRelay
|
|
4
|
-
from adafruit_mcp230xx.mcp23017 import MCP23017
|
|
5
3
|
import logging
|
|
6
4
|
|
|
5
|
+
from adafruit_mcp230xx.mcp23017 import MCP23017
|
|
6
|
+
|
|
7
|
+
from .basic import BasicRelay
|
|
8
|
+
|
|
7
9
|
_LOGGER = logging.getLogger(__name__)
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class MCPRelay(BasicRelay):
|
|
11
13
|
"""Represents MCP Relay output"""
|
|
12
14
|
|
|
13
|
-
def __init__(self, pin: int, mcp: MCP23017, **kwargs) -> None:
|
|
15
|
+
def __init__(self, pin: int, mcp: MCP23017, mcp_id: str, **kwargs) -> None:
|
|
14
16
|
"""Initialize MCP relay."""
|
|
15
17
|
super().__init__(**kwargs)
|
|
16
18
|
self._pin_id = pin
|
|
17
19
|
self._pin = mcp.get_pin(pin)
|
|
18
20
|
self._pin.switch_to_output(value=True)
|
|
21
|
+
self._mcp_id = mcp_id
|
|
19
22
|
self._pin.value = False
|
|
20
23
|
_LOGGER.debug("Setup MCP with pin %s", self._pin_id)
|
|
21
24
|
|
|
25
|
+
@property
|
|
26
|
+
def is_mcp_type(self) -> bool:
|
|
27
|
+
"""Check if relay is mcp type."""
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def mcp_id(self) -> str:
|
|
32
|
+
"""Retrieve parent MCP ID."""
|
|
33
|
+
return self._mcp_id
|
|
34
|
+
|
|
22
35
|
@property
|
|
23
36
|
def is_active(self) -> bool:
|
|
24
37
|
"""Is relay active."""
|
|
@@ -69,7 +69,13 @@ mcp23017:
|
|
|
69
69
|
type: string
|
|
70
70
|
required: True
|
|
71
71
|
meta:
|
|
72
|
-
label: GPIO of I2C SDA
|
|
72
|
+
label: GPIO of I2C SDA
|
|
73
|
+
init_sleep:
|
|
74
|
+
type: integer
|
|
75
|
+
required: True
|
|
76
|
+
default: 0
|
|
77
|
+
meta:
|
|
78
|
+
label: How long to sleep for MCP to initialize.
|
|
73
79
|
|
|
74
80
|
output:
|
|
75
81
|
type: list
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: boneio
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev8
|
|
4
4
|
Summary: Python Helper for BoneIO
|
|
5
5
|
Home-page: https://github.com/maciejk1984/boneIO
|
|
6
6
|
License: GNU General Public License v3.0
|
|
7
|
-
Download-URL: https://github.com/maciejk1984/boneIO/archive/0.1.0.
|
|
7
|
+
Download-URL: https://github.com/maciejk1984/boneIO/archive/0.1.0.dev8.zip
|
|
8
8
|
Platform: UNKNOWN
|
|
9
9
|
Classifier: Programming Language :: Python
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.7
|
|
@@ -18,8 +18,10 @@ boneio.egg-info/dependency_links.txt
|
|
|
18
18
|
boneio.egg-info/entry_points.txt
|
|
19
19
|
boneio.egg-info/requires.txt
|
|
20
20
|
boneio.egg-info/top_level.txt
|
|
21
|
+
boneio/fonts/danube__.ttf
|
|
21
22
|
boneio/helper/__init__.py
|
|
22
23
|
boneio/helper/gpio.py
|
|
24
|
+
boneio/helper/oled.py
|
|
23
25
|
boneio/helper/stats.py
|
|
24
26
|
boneio/helper/yaml.py
|
|
25
27
|
boneio/input/__init__.py
|
boneio-0.1.0.dev7/boneio/oled.py
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
from .helper.gpio import setup_input, edge_detect
|
|
2
|
-
from .helper.stats import HostData
|
|
3
|
-
from .input.gpio import configure_pin
|
|
4
|
-
from .const import (
|
|
5
|
-
OLED_PIN,
|
|
6
|
-
NETWORK,
|
|
7
|
-
CPU,
|
|
8
|
-
DISK,
|
|
9
|
-
MEMORY,
|
|
10
|
-
SWAP,
|
|
11
|
-
NUM_OF_SCREENS,
|
|
12
|
-
UPTIME,
|
|
13
|
-
OUTPUT,
|
|
14
|
-
WHITE,
|
|
15
|
-
)
|
|
16
|
-
import asyncio
|
|
17
|
-
import os
|
|
18
|
-
import logging
|
|
19
|
-
|
|
20
|
-
from luma.core.interface.serial import i2c
|
|
21
|
-
from luma.core.render import canvas
|
|
22
|
-
from luma.oled.device import sh1106
|
|
23
|
-
from PIL import ImageFont
|
|
24
|
-
|
|
25
|
-
_LOGGER = logging.getLogger(__name__)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def make_font(name, size):
|
|
29
|
-
font_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "fonts", name))
|
|
30
|
-
return ImageFont.truetype(font_path, size)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
fontBig = make_font("DejaVuSans.ttf", 12)
|
|
34
|
-
fontSmall = make_font("DejaVuSans.ttf", 10)
|
|
35
|
-
|
|
36
|
-
screen_order = [UPTIME, NETWORK, CPU, DISK, MEMORY, SWAP, OUTPUT]
|
|
37
|
-
lines = [17, 32, 47]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class Oled:
|
|
41
|
-
"""Oled display class."""
|
|
42
|
-
|
|
43
|
-
def __init__(self, host_data: HostData) -> None:
|
|
44
|
-
"""Initialize OLED screen."""
|
|
45
|
-
self._screen_no = 0
|
|
46
|
-
self._loop = asyncio.get_running_loop()
|
|
47
|
-
self._host_data = host_data
|
|
48
|
-
configure_pin(OLED_PIN)
|
|
49
|
-
setup_input(OLED_PIN)
|
|
50
|
-
edge_detect(OLED_PIN, callback=self._handle_press, bounce=50)
|
|
51
|
-
serial = i2c(port=2, address=0x3C)
|
|
52
|
-
self._device = sh1106(serial)
|
|
53
|
-
_LOGGER.debug("Configuring OLED screen.")
|
|
54
|
-
|
|
55
|
-
def get_task(self) -> list:
|
|
56
|
-
"""Periodically check for host statistics."""
|
|
57
|
-
return asyncio.create_task(self.render_display())
|
|
58
|
-
|
|
59
|
-
async def render_display(self):
|
|
60
|
-
while True:
|
|
61
|
-
screen = screen_order[self._screen_no]
|
|
62
|
-
data = self._host_data.get(screen)
|
|
63
|
-
if data:
|
|
64
|
-
with canvas(self._device) as draw:
|
|
65
|
-
draw.text((1, 1), screen, font=fontBig, fill=WHITE)
|
|
66
|
-
i = 0
|
|
67
|
-
for k in data:
|
|
68
|
-
draw.text(
|
|
69
|
-
(3, lines[i]),
|
|
70
|
-
f"{k} {data[k]}",
|
|
71
|
-
font=fontSmall,
|
|
72
|
-
fill=WHITE,
|
|
73
|
-
)
|
|
74
|
-
i += 1
|
|
75
|
-
await asyncio.sleep(0.5)
|
|
76
|
-
|
|
77
|
-
def _handle_press(self, pin: any) -> None:
|
|
78
|
-
"""Handle press of PIN for OLED display."""
|
|
79
|
-
if self._screen_no < NUM_OF_SCREENS:
|
|
80
|
-
self._screen_no += 1
|
|
81
|
-
else:
|
|
82
|
-
self._screen_no = 0
|
|
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
|