Skip to content

Commit fb083c8

Browse files
committed
Update and Lint MacroPad RPC Home Assistant code to work with settings.toml
1 parent 61c29b5 commit fb083c8

File tree

4 files changed

+114
-78
lines changed

4 files changed

+114
-78
lines changed

MacroPad_RPC_Home_Assistant/boot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
import usb_cdc
66

7-
usb_cdc.enable(console=True, data=True)
7+
usb_cdc.enable(console=True, data=True)

MacroPad_RPC_Home_Assistant/code.py

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
"""
55
Home Assistant Remote Procedure Call for MacroPad.
66
"""
7+
import os
78
import time
89
import displayio
910
import terminalio
1011
from adafruit_display_shapes.rect import Rect
11-
from rpc import RpcClient, RpcError
1212
from adafruit_display_text import label
1313
from adafruit_macropad import MacroPad
14-
from secrets import secrets
14+
from rpc import RpcClient, RpcError
1515

1616
macropad = MacroPad()
1717
rpc = RpcClient()
@@ -22,27 +22,40 @@
2222
KEY_LABELS = ("Demo", "Office")
2323
UPDATE_DELAY = 0.25
2424
NEOPIXEL_COLORS = {
25-
"OFF": 0xff0000,
26-
"ON": 0x00ff00,
25+
"OFF": 0xFF0000,
26+
"ON": 0x00FF00,
2727
}
2828

2929
class MqttError(Exception):
3030
"""For MQTT Specific Errors"""
31-
pass
31+
3232
# Set up displayio group with all the labels
3333
group = displayio.Group()
3434
for key_index in range(12):
3535
x = key_index % 3
3636
y = key_index // 3
37-
group.append(label.Label(terminalio.FONT, text=(str(KEY_LABELS[key_index]) if key_index < len(KEY_LABELS) else ''), color=0xFFFFFF,
38-
anchored_position=((macropad.display.width - 1) * x / 2,
39-
macropad.display.height - 1 -
40-
(3 - y) * 12),
41-
anchor_point=(x / 2, 1.0)))
37+
group.append(
38+
label.Label(
39+
terminalio.FONT,
40+
text=(str(KEY_LABELS[key_index]) if key_index < len(KEY_LABELS) else ""),
41+
color=0xFFFFFF,
42+
anchored_position=(
43+
(macropad.display.width - 1) * x / 2,
44+
macropad.display.height - 1 - (3 - y) * 12,
45+
),
46+
anchor_point=(x / 2, 1.0),
47+
)
48+
)
4249
group.append(Rect(0, 0, macropad.display.width, 12, fill=0xFFFFFF))
43-
group.append(label.Label(terminalio.FONT, text='Home Assistant', color=0x000000,
44-
anchored_position=(macropad.display.width//2, -2),
45-
anchor_point=(0.5, 0.0)))
50+
group.append(
51+
label.Label(
52+
terminalio.FONT,
53+
text="Home Assistant",
54+
color=0x000000,
55+
anchored_position=(macropad.display.width // 2, -2),
56+
anchor_point=(0.5, 0.0),
57+
)
58+
)
4659
macropad.display.show(group)
4760

4861
def rpc_call(function, *args, **kwargs):
@@ -54,16 +67,22 @@ def rpc_call(function, *args, **kwargs):
5467
return response["return_val"]
5568

5669
def mqtt_init():
57-
rpc_call("mqtt_init", secrets["mqtt_broker"], username=secrets["mqtt_username"], password=secrets["mqtt_password"], port=secrets["mqtt_port"])
70+
rpc_call(
71+
"mqtt_init",
72+
os.getenv("MQTT_BROKER"),
73+
username=os.getenv("MQTT_USERNAME"),
74+
password=os.getenv("MQTT_PASSWORD"),
75+
port=os.getenv("MQTT_PORT"),
76+
)
5877
rpc_call("mqtt_connect")
5978

60-
def update_key(key_number):
61-
if key_number < len(SUBSCRIBE_TOPICS):
62-
switch_state = rpc_call("mqtt_get_last_value", SUBSCRIBE_TOPICS[key_number])
79+
def update_key(key_id):
80+
if key_id < len(SUBSCRIBE_TOPICS):
81+
switch_state = rpc_call("mqtt_get_last_value", SUBSCRIBE_TOPICS[key_id])
6382
if switch_state is not None:
64-
macropad.pixels[key_number] = NEOPIXEL_COLORS[switch_state]
83+
macropad.pixels[key_id] = NEOPIXEL_COLORS[switch_state]
6584
else:
66-
macropad.pixels[key_number] = 0
85+
macropad.pixels[key_id] = 0
6786

6887
server_is_running = False
6988
print("Waiting for server...")
@@ -93,7 +112,11 @@ def update_key(key_number):
93112
last_macropad_encoder_value = macropad.encoder
94113

95114
macropad.encoder_switch_debounced.update()
96-
if macropad.encoder_switch_debounced.pressed and "key_number" not in output and ENCODER_ITEM is not None:
115+
if (
116+
macropad.encoder_switch_debounced.pressed
117+
and "key_number" not in output
118+
and ENCODER_ITEM is not None
119+
):
97120
output["key_number"] = ENCODER_ITEM
98121

99122
if output:

MacroPad_RPC_Home_Assistant/rpc.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,42 @@
77

88
import time
99
import json
10+
1011
try:
1112
import serial
1213
import adafruit_board_toolkit.circuitpython_serial
14+
1315
json_decode_exception = json.decoder.JSONDecodeError
1416
except ImportError:
1517
import usb_cdc as serial
18+
1619
json_decode_exception = ValueError
1720

18-
RESPONSE_TIMEOUT=5
19-
DATA_TIMEOUT=0.5
21+
RESPONSE_TIMEOUT = 5
22+
DATA_TIMEOUT = 0.5
23+
2024

2125
class RpcError(Exception):
2226
"""For RPC Specific Errors"""
23-
pass
2427

2528
class _Rpc:
2629
def __init__(self):
2730
self._serial = None
2831

2932
@staticmethod
30-
def create_response_packet(error=False, error_type="RPC", message=None, return_val=None):
33+
def create_response_packet(
34+
error=False, error_type="RPC", message=None, return_val=None
35+
):
3136
return {
3237
"error": error,
3338
"error_type": error_type if error else None,
3439
"message": message,
35-
"return_val": return_val
40+
"return_val": return_val,
3641
}
3742

3843
@staticmethod
39-
def create_request_packet(function, args=[], kwargs={}):
40-
return {
41-
"function": function,
42-
"args": args,
43-
"kwargs": kwargs
44-
}
44+
def create_request_packet(function, args=[], kwargs={}): # pylint: disable=dangerous-default-value
45+
return {"function": function, "args": args, "kwargs": kwargs}
4546

4647
def _wait_for_packet(self, timeout=None):
4748
incoming_packet = b""
@@ -51,11 +52,16 @@ def _wait_for_packet(self, timeout=None):
5152
if incoming_packet:
5253
data_start_time = time.monotonic()
5354
while not self._serial.in_waiting:
54-
if incoming_packet and (time.monotonic() - data_start_time) >= DATA_TIMEOUT:
55+
if (
56+
incoming_packet
57+
and (time.monotonic() - data_start_time) >= DATA_TIMEOUT
58+
):
5559
incoming_packet = b""
5660
if not incoming_packet and timeout is not None:
5761
if (time.monotonic() - response_start_time) >= timeout:
58-
return self.create_response_packet(error=True, message="Timed out waiting for response")
62+
return self.create_response_packet(
63+
error=True, message="Timed out waiting for response"
64+
)
5965
time.sleep(0.001)
6066
data = self._serial.read(self._serial.in_waiting)
6167
if data:
@@ -66,13 +72,13 @@ def _wait_for_packet(self, timeout=None):
6672
if sorted(tuple(packet.keys())) == sorted(self._packet_format()):
6773
return packet
6874
except json_decode_exception:
69-
pass # Incomplete packet
75+
pass # Incomplete packet
7076

7177
class RpcClient(_Rpc):
7278
def __init__(self):
7379
super().__init__()
7480
self._serial = serial.data
75-
81+
7682
def _packet_format(self):
7783
return self.create_response_packet().keys()
7884

@@ -97,13 +103,14 @@ def init_serial(self, baudrate):
97103
return serial.Serial(
98104
port,
99105
baudrate,
100-
parity='N',
106+
parity="N",
101107
rtscts=False,
102108
xonxoff=False,
103109
exclusive=True,
104110
)
105111

106-
def detect_port(self):
112+
@staticmethod
113+
def detect_port():
107114
"""
108115
Detect the port automatically
109116
"""
@@ -113,14 +120,16 @@ def detect_port(self):
113120
if len(ports) > 1:
114121
print("Multiple devices detected, using the first detected port.")
115122
return ports[0]
116-
raise RuntimeError("Unable to find any CircuitPython Devices with the CDC Data port enabled.")
123+
raise RuntimeError(
124+
"Unable to find any CircuitPython Devices with the CDC Data port enabled."
125+
)
117126

118127
def loop(self, timeout=None):
119128
packet = self._wait_for_packet(timeout)
120129
if "error" not in packet:
121130
response_packet = self._handler(packet)
122131
self._serial.write(bytes(json.dumps(response_packet), "utf-8"))
123-
132+
124133
def close_serial(self):
125134
if self._serial is not None:
126-
self._serial.close()
135+
self._serial.close()

MacroPad_RPC_Home_Assistant/rpc_ha_server.py

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,30 @@
99
import adafruit_minimqtt.adafruit_minimqtt as MQTT
1010
from rpc import RpcServer
1111

12-
mqtt_client = None
13-
mqtt_connected = False
12+
mqtt_status = {
13+
"connected": False,
14+
"client": None,
15+
}
1416
last_mqtt_messages = {}
1517

1618
# For program flow purposes, we do not want these functions to be called remotely
1719
PROTECTED_FUNCTIONS = ["main", "handle_rpc"]
1820

19-
def connect(mqtt_client, userdata, flags, rc):
20-
global mqtt_connected
21-
mqtt_connected = True
21+
def connect(_mqtt_client, _userdata, _flags, _rc):
22+
mqtt_status["connected"] = True
2223

23-
def disconnect(mqtt_client, userdata, rc):
24-
global mqtt_connected
25-
mqtt_connected = False
24+
def disconnect(_mqtt_client, _userdata, _rc):
25+
mqtt_status["connected"] = False
2626

27-
def message(client, topic, message):
28-
last_mqtt_messages[topic] = message
27+
def message(_client, topic, payload):
28+
last_mqtt_messages[topic] = payload
2929

3030
class MqttError(Exception):
3131
"""For MQTT Specific Errors"""
32-
pass
3332

34-
# Default to 1883 as SSL on CPython is not currently supported
33+
# Default to 1883 since SSL on CPython is not currently supported
3534
def mqtt_init(broker, port=1883, username=None, password=None):
36-
global mqtt_client, mqtt_connect_info
37-
mqtt_client = MQTT.MQTT(
35+
mqtt_status["client"] = MQTT.MQTT(
3836
broker=broker,
3937
port=port,
4038
username=username,
@@ -43,28 +41,28 @@ def mqtt_init(broker, port=1883, username=None, password=None):
4341
ssl_context=ssl.create_default_context(),
4442
)
4543

46-
mqtt_client.on_connect = connect
47-
mqtt_client.on_disconnect = disconnect
48-
mqtt_client.on_message = message
44+
mqtt_status["client"].on_connect = connect
45+
mqtt_status["client"].on_disconnect = disconnect
46+
mqtt_status["client"].on_message = message
4947

5048
def mqtt_connect():
51-
mqtt_client.connect()
49+
mqtt_status["client"].connect()
5250

5351
def mqtt_publish(topic, payload):
54-
if mqtt_client is None:
52+
if mqtt_status["client"] is None:
5553
raise MqttError("MQTT is not initialized")
5654
try:
57-
return_val = mqtt_client.publish(topic, json.dumps(payload))
55+
return_val = mqtt_status["client"].publish(topic, json.dumps(payload))
5856
except BrokenPipeError:
5957
time.sleep(0.5)
60-
mqtt_client.connect()
61-
return_val = mqtt_client.publish(topic, json.dumps(payload))
58+
mqtt_status["client"].connect()
59+
return_val = mqtt_status["client"].publish(topic, json.dumps(payload))
6260
return return_val
6361

6462
def mqtt_subscribe(topic):
65-
if mqtt_client is None:
63+
if mqtt_status["client"] is None:
6664
raise MqttError("MQTT is not initialized")
67-
return mqtt_client.subscribe(topic)
65+
return mqtt_status["client"].subscribe(topic)
6866

6967
def mqtt_get_last_value(topic):
7068
"""Return the last value we have received regarding a topic"""
@@ -80,36 +78,42 @@ def handle_rpc(packet):
8078
call the method with parameters, and generate a response
8179
packet as the return value"""
8280
print("Received packet")
83-
func_name = packet['function']
81+
func_name = packet["function"]
8482
if func_name in PROTECTED_FUNCTIONS:
85-
return rpc.create_response_packet(error=True, message=f"{func_name}'() is a protected function and can not be called.")
83+
return rpc.create_response_packet(
84+
error=True,
85+
message=f"{func_name}'() is a protected function and can not be called.",
86+
)
8687
if func_name not in globals():
87-
return rpc.create_response_packet(error=True, message=f"Function {func_name}() not found")
88+
return rpc.create_response_packet(
89+
error=True, message=f"Function {func_name}() not found"
90+
)
8891
try:
89-
return_val = globals()[func_name](*packet['args'], **packet['kwargs'])
92+
return_val = globals()[func_name](*packet["args"], **packet["kwargs"])
9093
except MqttError as err:
91-
return rpc.create_response_packet(error=True, error_type="MQTT", message=str(err))
94+
return rpc.create_response_packet(
95+
error=True, error_type="MQTT", message=str(err)
96+
)
9297

9398
packet = rpc.create_response_packet(return_val=return_val)
9499
return packet
95100

96101
def main():
97102
"""Command line, entry point"""
98-
global mqtt_connected
99103
while True:
100104
rpc.loop(0.25)
101-
if mqtt_connected and mqtt_client is not None:
105+
if mqtt_status["connected"] and mqtt_status["client"] is not None:
102106
try:
103-
mqtt_client.loop(0.5)
107+
mqtt_status["client"].loop(0.5)
104108
except AttributeError:
105-
mqtt_connected = False
109+
mqtt_status["connected"] = False
106110

107-
if __name__ == '__main__':
111+
if __name__ == "__main__":
108112
rpc = RpcServer(handle_rpc)
109113
try:
110-
print(f"Listening for RPC Calls, to stop press \"CTRL+C\"")
114+
print('Listening for RPC Calls, to stop press "CTRL+C"')
111115
main()
112116
except KeyboardInterrupt:
113117
print("")
114-
print(f"Caught interrupt, exiting...")
115-
rpc.close_serial()
118+
print("Caught interrupt, exiting...")
119+
rpc.close_serial()

0 commit comments

Comments
 (0)