boneIO 0.1.0__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/__init__.py +11 -0
- boneio/bonecli.py +165 -0
- boneio/const.py +111 -0
- boneio/example_config/__init__.py +0 -0
- boneio/example_config/adc.yaml +14 -0
- boneio/example_config/config.yaml +25 -0
- boneio/example_config/input.yaml +200 -0
- boneio/example_config/output24x16A.yaml +139 -0
- boneio/example_config/output32x5A.yaml +160 -0
- boneio/fonts/danube__.ttf +0 -0
- boneio/helper/__init__.py +50 -0
- boneio/helper/exceptions.py +13 -0
- boneio/helper/gpio.py +81 -0
- boneio/helper/ha_discovery.py +128 -0
- boneio/helper/mqtt.py +30 -0
- boneio/helper/oled.py +15 -0
- boneio/helper/queue.py +32 -0
- boneio/helper/stats.py +185 -0
- boneio/helper/yaml.py +46 -0
- boneio/input/__init__.py +5 -0
- boneio/input/gpio.py +122 -0
- boneio/manager.py +398 -0
- boneio/modbus.py +107 -0
- boneio/mqtt_client.py +170 -0
- boneio/oled.py +118 -0
- boneio/relay/__init__.py +5 -0
- boneio/relay/basic.py +76 -0
- boneio/relay/gpio.py +44 -0
- boneio/relay/mcp.py +56 -0
- boneio/schema.yaml +310 -0
- boneio/sensor/__init__.py +12 -0
- boneio/sensor/adc.py +38 -0
- boneio/sensor/gpio.py +32 -0
- boneio/sensor/sdm630.py +193 -0
- boneio/sensor/temp/__init__.py +52 -0
- boneio/version.py +2 -0
- boneio-0.1.0.dist-info/LICENSE +674 -0
- boneio-0.1.0.dist-info/METADATA +51 -0
- boneio-0.1.0.dist-info/RECORD +42 -0
- boneio-0.1.0.dist-info/WHEEL +5 -0
- boneio-0.1.0.dist-info/entry_points.txt +3 -0
- boneio-0.1.0.dist-info/top_level.txt +1 -0
boneio/mqtt_client.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Provide an MQTT client for providing BoneIO MQTT broker."""
|
|
2
|
+
import asyncio
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import uuid
|
|
6
|
+
from contextlib import AsyncExitStack
|
|
7
|
+
from typing import Any, Callable, Optional, Set, Union
|
|
8
|
+
|
|
9
|
+
import paho.mqtt.client as mqtt
|
|
10
|
+
from asyncio_mqtt import Client as AsyncioClient
|
|
11
|
+
from asyncio_mqtt import MqttError
|
|
12
|
+
from paho.mqtt.properties import Properties
|
|
13
|
+
from paho.mqtt.subscribeoptions import SubscribeOptions
|
|
14
|
+
|
|
15
|
+
from boneio.const import PAHO
|
|
16
|
+
from boneio.helper import UniqueQueue
|
|
17
|
+
|
|
18
|
+
_LOGGER = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MQTTClient:
|
|
22
|
+
"""Represent an MQTT client."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
host: str,
|
|
27
|
+
port: int = 1883,
|
|
28
|
+
**client_options: Any,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Set up client."""
|
|
31
|
+
self.host = host
|
|
32
|
+
self.port = port
|
|
33
|
+
if "client_id" not in client_options:
|
|
34
|
+
client_options["client_id"] = mqtt.base62(uuid.uuid4().int, padding=22)
|
|
35
|
+
if "logger" not in client_options:
|
|
36
|
+
client_options["logger"] = logging.getLogger(PAHO)
|
|
37
|
+
client_options["clean_session"] = True
|
|
38
|
+
self.client_options = client_options
|
|
39
|
+
self.asyncio_client: AsyncioClient = None
|
|
40
|
+
self.create_client()
|
|
41
|
+
self.reconnect_interval = 1
|
|
42
|
+
self.publish_queue: UniqueQueue = UniqueQueue()
|
|
43
|
+
|
|
44
|
+
def create_client(self) -> None:
|
|
45
|
+
"""Create the asyncio client."""
|
|
46
|
+
self.asyncio_client = AsyncioClient(
|
|
47
|
+
self.host,
|
|
48
|
+
self.port,
|
|
49
|
+
**self.client_options,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
async def publish( # pylint:disable=too-many-arguments
|
|
53
|
+
self,
|
|
54
|
+
topic: str,
|
|
55
|
+
payload: Optional[str] = None,
|
|
56
|
+
qos: int = 0,
|
|
57
|
+
retain: bool = False,
|
|
58
|
+
properties: Optional[Properties] = None,
|
|
59
|
+
timeout: float = 10,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Publish to topic.
|
|
62
|
+
|
|
63
|
+
Can raise asyncio_mqtt.MqttError.
|
|
64
|
+
"""
|
|
65
|
+
params: dict = {"qos": qos, "retain": retain, "timeout": timeout}
|
|
66
|
+
if payload:
|
|
67
|
+
params["payload"] = payload
|
|
68
|
+
if properties:
|
|
69
|
+
params["properties"] = properties
|
|
70
|
+
|
|
71
|
+
_LOGGER.debug("Sending message topic: %s, payload: %s", topic, payload)
|
|
72
|
+
await self.asyncio_client.publish(topic, **params)
|
|
73
|
+
|
|
74
|
+
async def subscribe( # pylint:disable=too-many-arguments
|
|
75
|
+
self,
|
|
76
|
+
topic: str,
|
|
77
|
+
qos: int = 0,
|
|
78
|
+
options: Optional[SubscribeOptions] = None,
|
|
79
|
+
properties: Optional[Properties] = None,
|
|
80
|
+
timeout: float = 10.0,
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Subscribe to topic.
|
|
83
|
+
|
|
84
|
+
Can raise asyncio_mqtt.MqttError.
|
|
85
|
+
"""
|
|
86
|
+
params: dict = {"qos": qos, "timeout": timeout}
|
|
87
|
+
if options:
|
|
88
|
+
params["options"] = options
|
|
89
|
+
if properties:
|
|
90
|
+
params["properties"] = properties
|
|
91
|
+
|
|
92
|
+
await self.asyncio_client.subscribe(topic, **params)
|
|
93
|
+
|
|
94
|
+
async def unsubscribe(
|
|
95
|
+
self, topic: str, properties: Optional[Properties] = None, timeout: float = 10.0
|
|
96
|
+
) -> None:
|
|
97
|
+
"""Unsubscribe from topic.
|
|
98
|
+
|
|
99
|
+
Can raise asyncio_mqtt.MqttError.
|
|
100
|
+
"""
|
|
101
|
+
params: dict = {"timeout": timeout}
|
|
102
|
+
if properties:
|
|
103
|
+
params["properties"] = properties
|
|
104
|
+
|
|
105
|
+
await self.asyncio_client.unsubscribe(topic, **params)
|
|
106
|
+
|
|
107
|
+
def send_message(self, topic: str, payload: Union[str, dict]) -> None:
|
|
108
|
+
"""Send a message from the manager options."""
|
|
109
|
+
to_publish = (topic, json.dumps(payload) if type(payload) == dict else payload)
|
|
110
|
+
self.publish_queue.put_nowait(to_publish)
|
|
111
|
+
|
|
112
|
+
async def _handle_publish(self) -> None:
|
|
113
|
+
"""Publish messages as they are put on the queue."""
|
|
114
|
+
while True:
|
|
115
|
+
to_publish: tuple = await self.publish_queue.get()
|
|
116
|
+
await self.publish(*to_publish)
|
|
117
|
+
self.publish_queue.task_done()
|
|
118
|
+
|
|
119
|
+
async def start_client(self, manager: any) -> None:
|
|
120
|
+
"""Start the client with the manager."""
|
|
121
|
+
# Reconnect automatically until the client is stopped.
|
|
122
|
+
while True:
|
|
123
|
+
try:
|
|
124
|
+
await self._subscribe_manager(manager)
|
|
125
|
+
except MqttError as err:
|
|
126
|
+
self.reconnect_interval = min(self.reconnect_interval * 2, 900)
|
|
127
|
+
_LOGGER.error(
|
|
128
|
+
"MQTT error: %s. Reconnecting in %s seconds",
|
|
129
|
+
err,
|
|
130
|
+
self.reconnect_interval,
|
|
131
|
+
)
|
|
132
|
+
await asyncio.sleep(self.reconnect_interval)
|
|
133
|
+
self.create_client() # reset connect/reconnect futures
|
|
134
|
+
|
|
135
|
+
async def _subscribe_manager(self, manager: any) -> None:
|
|
136
|
+
"""Connect and subscribe to manager topics + host stats."""
|
|
137
|
+
async with AsyncExitStack() as stack:
|
|
138
|
+
tasks: Set[asyncio.Task] = set()
|
|
139
|
+
|
|
140
|
+
# Connect to the MQTT broker.
|
|
141
|
+
await stack.enter_async_context(self.asyncio_client)
|
|
142
|
+
# Reset the reconnect interval after successful connection.
|
|
143
|
+
self.reconnect_interval = 1
|
|
144
|
+
|
|
145
|
+
publish_task = asyncio.create_task(self._handle_publish())
|
|
146
|
+
tasks.add(publish_task)
|
|
147
|
+
|
|
148
|
+
# Messages that doesn't match a filter will get logged and handled here.
|
|
149
|
+
messages = await stack.enter_async_context(
|
|
150
|
+
self.asyncio_client.unfiltered_messages()
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
messages_task = asyncio.create_task(
|
|
154
|
+
handle_messages(messages, manager.receive_message)
|
|
155
|
+
)
|
|
156
|
+
tasks.add(messages_task)
|
|
157
|
+
|
|
158
|
+
topic = f"{manager.relay_topic}"
|
|
159
|
+
await self.subscribe(topic)
|
|
160
|
+
|
|
161
|
+
# Wait for everything to complete (or fail due to, e.g., network errors).
|
|
162
|
+
await asyncio.gather(*tasks)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
async def handle_messages(messages: Any, callback: Callable[[str, str], None]) -> None:
|
|
166
|
+
"""Handle messages with callback."""
|
|
167
|
+
async for message in messages:
|
|
168
|
+
payload = message.payload.decode()
|
|
169
|
+
_LOGGER.debug("Received message topic: %s, payload: %s", message.topic, payload)
|
|
170
|
+
callback(message.topic, payload)
|
boneio/oled.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
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 luma.core.error import DeviceNotFoundError
|
|
9
|
+
from PIL import ImageDraw
|
|
10
|
+
|
|
11
|
+
from boneio.const import CPU, DISK, MEMORY, NETWORK, OLED_PIN, SWAP, UPTIME, WHITE
|
|
12
|
+
from boneio.helper import (
|
|
13
|
+
HostData,
|
|
14
|
+
configure_pin,
|
|
15
|
+
edge_detect,
|
|
16
|
+
make_font,
|
|
17
|
+
setup_input,
|
|
18
|
+
I2CError,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_LOGGER = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
fontBig = make_font("DejaVuSans.ttf", 12)
|
|
25
|
+
fontSmall = make_font("DejaVuSans.ttf", 10)
|
|
26
|
+
fontExtraSmall = make_font("DejaVuSans.ttf", 7)
|
|
27
|
+
danube = make_font("danube__.ttf", 15, local=True)
|
|
28
|
+
|
|
29
|
+
screen_order = [UPTIME, NETWORK, CPU, DISK, MEMORY, SWAP]
|
|
30
|
+
|
|
31
|
+
STANDARD_ROWS = [17, 32, 47]
|
|
32
|
+
UPTIME_ROWS = list(range(25, 50, 10))
|
|
33
|
+
OUTPUT_ROWS = list(range(14, 60, 6))
|
|
34
|
+
OUTPUT_COLS = range(0, 101, 50)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Oled:
|
|
38
|
+
"""Oled display class."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, host_data: HostData, output_groups: List[str]) -> None:
|
|
41
|
+
"""Initialize OLED screen."""
|
|
42
|
+
self._screen_order = cycle(screen_order + output_groups)
|
|
43
|
+
self._output_groups = output_groups
|
|
44
|
+
self._current_screen = next(self._screen_order)
|
|
45
|
+
self._host_data = host_data
|
|
46
|
+
configure_pin(OLED_PIN)
|
|
47
|
+
setup_input(OLED_PIN)
|
|
48
|
+
edge_detect(pin=OLED_PIN, callback=self._handle_press, bounce=80)
|
|
49
|
+
try:
|
|
50
|
+
serial = i2c(port=2, address=0x3C)
|
|
51
|
+
self._device = sh1106(serial)
|
|
52
|
+
except DeviceNotFoundError as err:
|
|
53
|
+
raise I2CError(err)
|
|
54
|
+
_LOGGER.debug("Configuring OLED screen.")
|
|
55
|
+
|
|
56
|
+
def _draw_standard(self, data: dict, draw: ImageDraw) -> None:
|
|
57
|
+
"""Draw standard information about host screen."""
|
|
58
|
+
draw.text((1, 1), self._current_screen, font=fontBig, fill=WHITE)
|
|
59
|
+
i = 0
|
|
60
|
+
for k in data:
|
|
61
|
+
draw.text(
|
|
62
|
+
(3, STANDARD_ROWS[i]),
|
|
63
|
+
f"{k} {data[k]}",
|
|
64
|
+
font=fontSmall,
|
|
65
|
+
fill=WHITE,
|
|
66
|
+
)
|
|
67
|
+
i += 1
|
|
68
|
+
|
|
69
|
+
def _draw_uptime(self, data: dict, draw: ImageDraw) -> None:
|
|
70
|
+
"""Draw uptime screen with boneIO logo."""
|
|
71
|
+
draw.text((3, 5), "bone", font=danube, fill=WHITE)
|
|
72
|
+
draw.text((53, 5), "iO", font=danube, fill=WHITE)
|
|
73
|
+
i = 0
|
|
74
|
+
for k in data:
|
|
75
|
+
draw.text(
|
|
76
|
+
(3, UPTIME_ROWS[i]),
|
|
77
|
+
f"{k}: {data[k]}",
|
|
78
|
+
font=fontSmall,
|
|
79
|
+
fill=WHITE,
|
|
80
|
+
)
|
|
81
|
+
i += 1
|
|
82
|
+
|
|
83
|
+
def _draw_output(self, data: dict, draw: ImageDraw) -> None:
|
|
84
|
+
"Draw outputs of GPIO/MCP relays."
|
|
85
|
+
cols = cycle(OUTPUT_COLS)
|
|
86
|
+
draw.text((1, 1), f"Relay {self._current_screen}", font=fontSmall, fill=WHITE)
|
|
87
|
+
i = 0
|
|
88
|
+
j = next(cols)
|
|
89
|
+
for k in data:
|
|
90
|
+
if len(OUTPUT_ROWS) == i:
|
|
91
|
+
j = next(cols)
|
|
92
|
+
i = 0
|
|
93
|
+
draw.text(
|
|
94
|
+
(j, OUTPUT_ROWS[i]), f"{k} {data[k]}", font=fontExtraSmall, fill=WHITE
|
|
95
|
+
)
|
|
96
|
+
i += 1
|
|
97
|
+
|
|
98
|
+
def render_display(self) -> None:
|
|
99
|
+
"""Render display."""
|
|
100
|
+
data = self._host_data.get(self._current_screen)
|
|
101
|
+
if data:
|
|
102
|
+
with canvas(self._device) as draw:
|
|
103
|
+
if self._current_screen in self._output_groups:
|
|
104
|
+
self._draw_output(data, draw)
|
|
105
|
+
elif self._current_screen == UPTIME:
|
|
106
|
+
self._draw_uptime(data, draw)
|
|
107
|
+
else:
|
|
108
|
+
self._draw_standard(data, draw)
|
|
109
|
+
|
|
110
|
+
def handle_data_update(self, type: str):
|
|
111
|
+
"""Callback to handle new data present into screen."""
|
|
112
|
+
if type == self._current_screen:
|
|
113
|
+
self.render_display()
|
|
114
|
+
|
|
115
|
+
def _handle_press(self, pin: any) -> None:
|
|
116
|
+
"""Handle press of PIN for OLED display."""
|
|
117
|
+
self._current_screen = next(self._screen_order)
|
|
118
|
+
self.render_display()
|
boneio/relay/__init__.py
ADDED
boneio/relay/basic.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Basic Relay module."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
from boneio.const import OFF, ON, RELAY, STATE, SWITCH
|
|
8
|
+
from boneio.helper import BasicMqtt
|
|
9
|
+
|
|
10
|
+
_LOGGER = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BasicRelay(BasicMqtt):
|
|
14
|
+
"""Basic relay class."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self, callback: Callable, id: str = None, ha_type=SWITCH, **kwargs
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Initialize Basic relay."""
|
|
20
|
+
super().__init__(id=id, name=id, topic_type=RELAY, **kwargs)
|
|
21
|
+
self._ha_type = ha_type
|
|
22
|
+
self._state = False
|
|
23
|
+
self._callback = callback
|
|
24
|
+
self._loop = asyncio.get_running_loop()
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def is_mcp_type(self) -> bool:
|
|
28
|
+
"""Check if relay is mcp type."""
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def id(self) -> bool:
|
|
33
|
+
"""Id of the relay.
|
|
34
|
+
Has to be trimmed out of spaces because of MQTT handling in HA."""
|
|
35
|
+
return self._id or self._pin
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def name(self) -> bool:
|
|
39
|
+
"""Not trimmed id."""
|
|
40
|
+
return self._name or self._pin
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def state(self) -> bool:
|
|
44
|
+
"""Is relay active."""
|
|
45
|
+
return self._state
|
|
46
|
+
|
|
47
|
+
def send_state(self) -> None:
|
|
48
|
+
"""Send state to Mqtt on action."""
|
|
49
|
+
state = ON if self.is_active else OFF
|
|
50
|
+
self._state = state
|
|
51
|
+
self._send_message(
|
|
52
|
+
topic=self._send_topic,
|
|
53
|
+
payload={STATE: state},
|
|
54
|
+
)
|
|
55
|
+
self._loop.call_soon_threadsafe(self._callback)
|
|
56
|
+
|
|
57
|
+
def toggle(self) -> None:
|
|
58
|
+
"""Toggle relay."""
|
|
59
|
+
_LOGGER.debug("Toggle relay.")
|
|
60
|
+
if self.is_active:
|
|
61
|
+
self.turn_off()
|
|
62
|
+
else:
|
|
63
|
+
self.turn_on()
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def is_active(self) -> bool:
|
|
67
|
+
"""Is active check."""
|
|
68
|
+
raise NotImplementedError
|
|
69
|
+
|
|
70
|
+
def turn_on(self) -> None:
|
|
71
|
+
"""Call turn on action."""
|
|
72
|
+
raise NotImplementedError
|
|
73
|
+
|
|
74
|
+
def turn_off(self) -> None:
|
|
75
|
+
"""Call turn off action."""
|
|
76
|
+
raise NotImplementedError
|
boneio/relay/gpio.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""GPIO Relay module.
|
|
2
|
+
!!Not used in BoneIO.
|
|
3
|
+
Created just in case.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
from boneio.helper import read_input, setup_output, write_output
|
|
9
|
+
from boneio.relay.basic import BasicRelay
|
|
10
|
+
from boneio.const import HIGH, LOW
|
|
11
|
+
|
|
12
|
+
_LOGGER = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GpioRelay(BasicRelay):
|
|
16
|
+
"""Represents GPIO Relay output"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, pin: str, **kwargs) -> None:
|
|
19
|
+
"""Initialize Gpio relay."""
|
|
20
|
+
super().__init(**kwargs)
|
|
21
|
+
self._pin = pin
|
|
22
|
+
setup_output(self._pin)
|
|
23
|
+
write_output(self.pin, LOW)
|
|
24
|
+
_LOGGER.debug("Setup relay with pin %s", self._pin)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def is_active(self) -> bool:
|
|
28
|
+
"""Is relay active."""
|
|
29
|
+
return read_input(self.pin, on_state=HIGH)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def pin(self) -> str:
|
|
33
|
+
"""PIN of the relay"""
|
|
34
|
+
return self._pin
|
|
35
|
+
|
|
36
|
+
def turn_on(self) -> None:
|
|
37
|
+
"""Call turn on action."""
|
|
38
|
+
write_output(self.pin, HIGH)
|
|
39
|
+
self._loop.call_soon_threadsafe(self.send_state)
|
|
40
|
+
|
|
41
|
+
def turn_off(self) -> None:
|
|
42
|
+
"""Call turn off action."""
|
|
43
|
+
write_output(self.pin, LOW)
|
|
44
|
+
self._loop.call_soon_threadsafe(self.send_state)
|
boneio/relay/mcp.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""MCP23017 Relay module."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from adafruit_mcp230xx.mcp23017 import MCP23017
|
|
5
|
+
from boneio.relay.basic import BasicRelay
|
|
6
|
+
|
|
7
|
+
_LOGGER = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MCPRelay(BasicRelay):
|
|
11
|
+
"""Represents MCP Relay output"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, pin: int, mcp: MCP23017, mcp_id: str, **kwargs) -> None:
|
|
14
|
+
"""Initialize MCP relay."""
|
|
15
|
+
super().__init__(**kwargs)
|
|
16
|
+
self._pin_id = pin
|
|
17
|
+
self._pin = mcp.get_pin(pin)
|
|
18
|
+
self._pin.switch_to_output(value=True)
|
|
19
|
+
self._mcp_id = mcp_id
|
|
20
|
+
self._pin.value = False
|
|
21
|
+
_LOGGER.debug("Setup MCP with pin %s", self._pin_id)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def is_mcp_type(self) -> bool:
|
|
25
|
+
"""Check if relay is mcp type."""
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def mcp_id(self) -> str:
|
|
30
|
+
"""Retrieve parent MCP ID."""
|
|
31
|
+
return self._mcp_id
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def is_active(self) -> bool:
|
|
35
|
+
"""Is relay active."""
|
|
36
|
+
return self.pin.value
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def pin(self) -> str:
|
|
40
|
+
"""PIN of the relay"""
|
|
41
|
+
return self._pin
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def ha_type(self) -> str:
|
|
45
|
+
"""HA type."""
|
|
46
|
+
return self._ha_type
|
|
47
|
+
|
|
48
|
+
def turn_on(self) -> None:
|
|
49
|
+
"""Call turn on action."""
|
|
50
|
+
self.pin.value = True
|
|
51
|
+
self._loop.call_soon_threadsafe(self.send_state)
|
|
52
|
+
|
|
53
|
+
def turn_off(self) -> None:
|
|
54
|
+
"""Call turn off action."""
|
|
55
|
+
self.pin.value = False
|
|
56
|
+
self._loop.call_soon_threadsafe(self.send_state)
|