remla 0.1.0__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.
- remla-0.1.0/PKG-INFO +28 -0
- remla-0.1.0/README.md +0 -0
- remla-0.1.0/pyproject.toml +32 -0
- remla-0.1.0/remla/__init__.py +0 -0
- remla-0.1.0/remla/customvalidators.py +68 -0
- remla-0.1.0/remla/i2ccmd.py +252 -0
- remla-0.1.0/remla/labcontrol/Controllers.py +1970 -0
- remla-0.1.0/remla/labcontrol/Experiment.py +258 -0
- remla-0.1.0/remla/labcontrol/__init__.py +2 -0
- remla-0.1.0/remla/labcontrol/__pycache__/Controllers.cpython-311.pyc +0 -0
- remla-0.1.0/remla/labcontrol/__pycache__/Experiment.cpython-311.pyc +0 -0
- remla-0.1.0/remla/labcontrol/__pycache__/__init__.cpython-311.pyc +0 -0
- remla-0.1.0/remla/main.py +646 -0
- remla-0.1.0/remla/settings.py +37 -0
- remla-0.1.0/remla/setup/finalInfoTemplate.md +17 -0
- remla-0.1.0/remla/setup/hello.txt +1 -0
- remla-0.1.0/remla/setup/i2c-output.png +0 -0
- remla-0.1.0/remla/setup/index.html +209 -0
- remla-0.1.0/remla/setup/localhost.conf +39 -0
- remla-0.1.0/remla/setup/mediaMTXGetFeed.js +53 -0
- remla-0.1.0/remla/setup/mediamtx.service +6 -0
- remla-0.1.0/remla/setup/mediamtx.yml +625 -0
- remla-0.1.0/remla/setup/reader.js +486 -0
- remla-0.1.0/remla/setup/remlaSocket.js +39 -0
- remla-0.1.0/remla/setupcmd.py +216 -0
- remla-0.1.0/remla/systemHelpers.py +273 -0
- remla-0.1.0/remla/typerHelpers.py +33 -0
- remla-0.1.0/remla/yaml.py +105 -0
remla-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: remla
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Zak Espley
|
|
6
|
+
Author-email: zespley@gmail.com
|
|
7
|
+
Requires-Python: >=3.11,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Requires-Dist: adafruit-circuitpython-motorkit (>=1.6.14,<2.0.0)
|
|
13
|
+
Requires-Dist: dlipower (>=1.0.176,<2.0.0)
|
|
14
|
+
Requires-Dist: pathvalidate (>=3.2.0,<4.0.0)
|
|
15
|
+
Requires-Dist: pigpio (>=1.78,<2.0)
|
|
16
|
+
Requires-Dist: python-tplink-smarthome (>=0.1.3,<0.2.0)
|
|
17
|
+
Requires-Dist: pyvisa (>=1.14.1,<2.0.0)
|
|
18
|
+
Requires-Dist: pyvisa-py (>=0.7.2,<0.8.0)
|
|
19
|
+
Requires-Dist: rpistepper (>=0.3a1,<0.4)
|
|
20
|
+
Requires-Dist: ruamel-yaml (>=0.18.6,<0.19.0)
|
|
21
|
+
Requires-Dist: setuptools (>=70.0.0,<71.0.0)
|
|
22
|
+
Requires-Dist: typer[all] (>=0.12.1,<0.13.0)
|
|
23
|
+
Requires-Dist: validators (>=0.28.3,<0.29.0)
|
|
24
|
+
Requires-Dist: websockets (>=12.0,<13.0)
|
|
25
|
+
Requires-Dist: wheel (>=0.43.0,<0.44.0)
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
|
remla-0.1.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "remla"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = ["Zak Espley <zespley@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
include = ["setup/*/**"]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.scripts]
|
|
10
|
+
remla = "remla.main:app"
|
|
11
|
+
|
|
12
|
+
[tool.poetry.dependencies]
|
|
13
|
+
python = "^3.11"
|
|
14
|
+
typer = {extras = ["all"], version = "^0.12.1"}
|
|
15
|
+
ruamel-yaml = "^0.18.6"
|
|
16
|
+
rpistepper = "^0.3a1"
|
|
17
|
+
pyvisa = "^1.14.1"
|
|
18
|
+
pyvisa-py = "^0.7.2"
|
|
19
|
+
adafruit-circuitpython-motorkit = "^1.6.14"
|
|
20
|
+
python-tplink-smarthome = "^0.1.3"
|
|
21
|
+
dlipower = "^1.0.176"
|
|
22
|
+
websockets = "^12.0"
|
|
23
|
+
validators = "^0.28.3"
|
|
24
|
+
pigpio = "^1.78"
|
|
25
|
+
wheel = "^0.43.0"
|
|
26
|
+
setuptools = "^70.0.0"
|
|
27
|
+
pathvalidate = "^3.2.0"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["poetry-core"]
|
|
32
|
+
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from .typerHelpers import *
|
|
2
|
+
import typer
|
|
3
|
+
from click import style
|
|
4
|
+
import validators
|
|
5
|
+
from remla.typerHelpers import *
|
|
6
|
+
from typing import Callable, Any
|
|
7
|
+
|
|
8
|
+
def sensorValidator(selection: str) -> int:
|
|
9
|
+
validMessage = "Please input a number between 1 and 5."
|
|
10
|
+
try:
|
|
11
|
+
value = int(selection)
|
|
12
|
+
except ValueError:
|
|
13
|
+
raise RemlaBadParameter(validMessage)
|
|
14
|
+
|
|
15
|
+
if value-1 in range(6):
|
|
16
|
+
return value-1
|
|
17
|
+
else:
|
|
18
|
+
raise RemlaBadParameter(validMessage)
|
|
19
|
+
|
|
20
|
+
def domainOrHostnameValidtor(domainOrHostname:str, alertUser:bool=True) -> bool:
|
|
21
|
+
|
|
22
|
+
if validators.domain(domainOrHostname):
|
|
23
|
+
return True
|
|
24
|
+
|
|
25
|
+
if validators.hostname(domainOrHostname):
|
|
26
|
+
return True
|
|
27
|
+
if alertUser:
|
|
28
|
+
alert("Invalid Domain, Hostname, or IP Address")
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
def portValidator(port:int, alertUser:bool=True)->bool:
|
|
32
|
+
validPort = validators.between(port, min_val=0, max_val=65535)
|
|
33
|
+
definedPort = validators.between(port, min_val=0, max_val=1023)
|
|
34
|
+
if not validPort:
|
|
35
|
+
if alertUser:
|
|
36
|
+
alert("Ports must be in the range 0-65535")
|
|
37
|
+
return False
|
|
38
|
+
elif definedPort:
|
|
39
|
+
warning("Ports in the range 0-1023 are typically reserved. Make sure you know what you are doing.")
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
def urlValidator(url:str, alertUser:bool=True)->bool:
|
|
43
|
+
if validators.url(url):
|
|
44
|
+
return True
|
|
45
|
+
if alertUser:
|
|
46
|
+
alert("You did not provide a valid URL")
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
def uniqueValidator(items:list, invalidMsg:tuple[str,Callable[[str],None]]|None=None,
|
|
50
|
+
processMethod:Callable[[...,Any],list]|None=None, abort:bool=True) -> bool:
|
|
51
|
+
if processMethod is not None:
|
|
52
|
+
items = list(map(processMethod, items))
|
|
53
|
+
itemSet = set(items)
|
|
54
|
+
if len(itemSet) != len(items):
|
|
55
|
+
if invalidMsg is not None:
|
|
56
|
+
msgType = invalidMsg[1]
|
|
57
|
+
msg = invalidMsg[0]
|
|
58
|
+
msgType(msg)
|
|
59
|
+
if abort:
|
|
60
|
+
raise typer.Abort()
|
|
61
|
+
return False
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
class RemlaBadParameter(typer.BadParameter):
|
|
65
|
+
|
|
66
|
+
def __init__(self, message, *args, **kwargs):
|
|
67
|
+
super().__init__(style(f"⚠️ {message} ⚠️ ", fg="yellow"), *args, **kwargs)
|
|
68
|
+
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
from remla.labcontrol.Controllers import gpio
|
|
2
|
+
import typer
|
|
3
|
+
from remla.systemHelpers import *
|
|
4
|
+
from rich.prompt import Prompt
|
|
5
|
+
from remla.settings import *
|
|
6
|
+
from remla.typerHelpers import *
|
|
7
|
+
from remla.systemHelpers import *
|
|
8
|
+
from typing_extensions import Annotated
|
|
9
|
+
from typing import Union
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(no_args_is_help=False)
|
|
13
|
+
|
|
14
|
+
addOverlayCmd = "sudo dtoverlay i2c-gpio bus=<bus> i2c_gpio_sda=<sda> i2c_gpio_scl=<scl>"
|
|
15
|
+
removeOverlayCmd = "sudo dtoverlay -r <index>"
|
|
16
|
+
armI2CCmd = "sudo dtparam i2c_arm=<state>"
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def new(
|
|
20
|
+
bus: Annotated[Union[str, None], typer.Option("--bus", "-b", help="The bus number you want." )]=None,
|
|
21
|
+
sda: Annotated[Union[str,None], typer.Option("--sda", "-d", help="SDA Data Line GPIO number in BCM.")]=None,
|
|
22
|
+
scl: Annotated[Union[str,None], typer.Option("--scl", "-c", help="SCL Clock Line GPIO Number in BCM")]=None,
|
|
23
|
+
arm: Annotated[Union[bool,None], typer.Option("--arm", "-a", help="Turns on the builtin I2C on GPIO 2 & 3")]=False
|
|
24
|
+
):
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
MUST BE RUN AS ROOT. (use sudo)
|
|
28
|
+
|
|
29
|
+
This command sets up a new i2c bus by writing right to the /boot/firmware/config.txt file.
|
|
30
|
+
It will then try to load an overlay so that you can use it before reboot.
|
|
31
|
+
|
|
32
|
+
It works in two methods you can just run `sudo remla i2c new` to get an interactive prompt for how to add a new i2c bus to the
|
|
33
|
+
system (this is the preferred method). Additionally you can use the the -b, -d, -s flags to provide
|
|
34
|
+
your own bus, sda, and scl respectively. Just put the flag followed by a number, i.e. -b 11 for bus 11
|
|
35
|
+
|
|
36
|
+
You must provide a bus if you do it this way. You must also provide both an sda and scl pin or provide neither. If neither is provided
|
|
37
|
+
then the defaults of GPIO 23 and 24 are selected.
|
|
38
|
+
|
|
39
|
+
Finally, you can use the -a flag to turn on the default i2c-1 bus if it is not on.
|
|
40
|
+
|
|
41
|
+
Note: All pin numbers should use the BCM convention.
|
|
42
|
+
"""
|
|
43
|
+
####### Verify that we are running as sudo ######
|
|
44
|
+
if os.geteuid() != 0:
|
|
45
|
+
alert("This script must be run as root.")
|
|
46
|
+
typer.echo("Try running with sudo")
|
|
47
|
+
raise typer.Abort()
|
|
48
|
+
# Check if running interactive mode
|
|
49
|
+
if bus is None and sda is None and scl is None and not arm:
|
|
50
|
+
_i2cInteractive()
|
|
51
|
+
elif arm:
|
|
52
|
+
config = bootConfigPath.read_text()
|
|
53
|
+
arm_line = "dtparam=i2c_arm=<state>"
|
|
54
|
+
i2c_arm, i2c_arm_line_no, i2c_overalys = getI2COverlays()
|
|
55
|
+
if i2c_arm:
|
|
56
|
+
warning("I2C arm is already in config.txt. If you don't see it when you think you should try a reboot")
|
|
57
|
+
return
|
|
58
|
+
if i2c_arm_line_no < 0:
|
|
59
|
+
bootConfigPath.write_text(config + "\n" + arm_line.replace("<state>", "on"))
|
|
60
|
+
else:
|
|
61
|
+
bootConfigPath.write_text( config.replace(arm_line.replace("<state>", "off"), arm_line.replace("<state>", "on")) )
|
|
62
|
+
success("Added i2c_arm to config.txt. It will now be automatically loaded on reboot")
|
|
63
|
+
cmd = armI2CCmd.replace("<state>", "on")
|
|
64
|
+
try:
|
|
65
|
+
subprocess.run(cmd.split(), check=True)
|
|
66
|
+
success("Also loaded in i2c_arm right now, so you can use it before reboot.\nTo test it out, run `i2cdetect -y 1`")
|
|
67
|
+
except subprocess.CalledProcessError as e:
|
|
68
|
+
warning(f"We tried to load I2C right now but something went wrong. You likely need to reboot to have access to I2C. The error message was\n\n{e}")
|
|
69
|
+
|
|
70
|
+
else:
|
|
71
|
+
#Check if bus is given
|
|
72
|
+
try:
|
|
73
|
+
int(bus)
|
|
74
|
+
if bus is None or int(bus) <= 2:
|
|
75
|
+
raise ValueError
|
|
76
|
+
except ValueError:
|
|
77
|
+
alert("Bus must be an integer greater than or equal to 2, and probably less than 16")
|
|
78
|
+
raise typer.Abort()
|
|
79
|
+
except TypeError:
|
|
80
|
+
alert("You must provide a bus argument with `-b` flag followed by an integer from 2-16")
|
|
81
|
+
raise typer.Abort()
|
|
82
|
+
|
|
83
|
+
# Check if SDA and SCL are both none, or provided.
|
|
84
|
+
if not bothOrNoneAssigned(sda, scl):
|
|
85
|
+
alert("You need to do one of the following:\n\
|
|
86
|
+
1. Assign both SDA and SCL to numbers on your representing GPIO pins in BCM format\n\
|
|
87
|
+
2. Not assign either SDA or SCL in which case BCM pins 23, and 24 will be used.")
|
|
88
|
+
raise typer.Abort()
|
|
89
|
+
|
|
90
|
+
sda = sda or "23"
|
|
91
|
+
scl = scl or "24"
|
|
92
|
+
|
|
93
|
+
if sda==scl:
|
|
94
|
+
alert("SDA and SCL must be different GPIO pins")
|
|
95
|
+
raise typer.Abort()
|
|
96
|
+
|
|
97
|
+
i2c_arm, i2c_arm_line_no, i2c_overlays = getI2COverlays()
|
|
98
|
+
forbidden_gpios = {0,1,2,3}
|
|
99
|
+
preferred_gpios = {17,27,22,23,24,25,5,6,16,26}
|
|
100
|
+
|
|
101
|
+
if {int(sda), int(scl)} & forbidden_gpios:
|
|
102
|
+
alert(f"You can't use any of the GPIOs in the the following {forbidden_gpios}")
|
|
103
|
+
raise typer.Abort()
|
|
104
|
+
|
|
105
|
+
if not ({int(sda), int(scl)} & preferred_gpios):
|
|
106
|
+
warning(f"If you can, try to use one of the following GPIO's as they have no other uses:\n{preferred_gpios}")
|
|
107
|
+
|
|
108
|
+
conflicts = []
|
|
109
|
+
for overlay in i2c_overlays:
|
|
110
|
+
in_use = f""
|
|
111
|
+
if overlay["bus"] == bus:
|
|
112
|
+
in_use += f"BUS: {bus}\t"
|
|
113
|
+
#alert(f"Bus {bus} is already in use. Consider using a different bus number or using `remla i2c update` to update that bus")
|
|
114
|
+
if overlay["sda"] == sda:
|
|
115
|
+
in_use += f"GPIO_SDA: {sda}\t"
|
|
116
|
+
if overlay["scl"] == scl:
|
|
117
|
+
in_use += f"GPIO_SCL: {scl}"
|
|
118
|
+
if in_use != "":
|
|
119
|
+
line = _createI2CLine(overlay['bus'], overlay['sda'], overlay['scl'])
|
|
120
|
+
conflicts.append(line)
|
|
121
|
+
|
|
122
|
+
if len(conflicts) != 0:
|
|
123
|
+
message = "\nYou should run `remla i2c delete` to remove conflicting i2c conditions"
|
|
124
|
+
message += "\n".join(conflicts)
|
|
125
|
+
alert(message)
|
|
126
|
+
raise typer.Abort()
|
|
127
|
+
line = _createI2CLine(bus, sda, scl)
|
|
128
|
+
with open(bootConfigPath, "a") as config:
|
|
129
|
+
config.write("\n"+line)
|
|
130
|
+
success(f"Updated {bootConfigPath} with new overlay:\n\t{line}\n Overlay will automatically be activated on next reboot. Loading in overlay right now as well...")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
cmd = addOverlayCmd.replace("<bus>", bus).replace("<sda>", sda).replace("<scl>", scl).strip()
|
|
134
|
+
try:
|
|
135
|
+
subprocess.run(cmd.split(), check=True)
|
|
136
|
+
success(f"Added new i2c bus. You can check by running the command `i2cdetect -y {bus}`")
|
|
137
|
+
except subprocess.CalledProcessError as e:
|
|
138
|
+
alert(f"There was an error loading the overlay. Error read:\n {e}")
|
|
139
|
+
|
|
140
|
+
@app.command(help="The will list out busses that are avaialble and then remove you selected one from the /boot/firmware/config.txt.\
|
|
141
|
+
A restart is required for you changes to take effect.\n\
|
|
142
|
+
This can also be used to turn off the default i2c-1 bus if needed.")
|
|
143
|
+
def delete():
|
|
144
|
+
i2c_arm, i2c_arm_line_no, i2c_overlays = getI2COverlays()
|
|
145
|
+
choice_string = ""
|
|
146
|
+
choices = len(i2c_overlays)
|
|
147
|
+
if not i2c_arm and choices == 0:
|
|
148
|
+
warning("No I2C enteries to delete. Exiting...")
|
|
149
|
+
return
|
|
150
|
+
for index, overlay in enumerate(i2c_overlays):
|
|
151
|
+
choice_string += f" {index+1}. {_createI2CLine(overlay['bus'], overlay['sda'], overlay['scl'])}\n"
|
|
152
|
+
if i2c_arm:
|
|
153
|
+
choice_string += f" {len(i2c_overlays) + 1}. dtparam=i2c_arm=on"
|
|
154
|
+
choices += 1
|
|
155
|
+
deletionChoice = IntPrompt.ask(f"Which overlay do you want to delete?\n{choice_string}\n", choices=[str(i) for i in range(1, choices+1)])
|
|
156
|
+
if deletionChoice != choices or not i2c_arm:
|
|
157
|
+
config = bootConfigPath.read_text().splitlines()
|
|
158
|
+
del config[i2c_overlays[deletionChoice-1]["line_no"]]
|
|
159
|
+
bootConfigPath.write_text("\n".join(config) + "\n")
|
|
160
|
+
else:
|
|
161
|
+
config = bootConfigPath.read_text()
|
|
162
|
+
config = config.replace("dtparam=i2c_arm=on", "dtparam=i2c_arm=off")
|
|
163
|
+
bootConfigPath.write_text(config)
|
|
164
|
+
success("Successfully updated config.txt.")
|
|
165
|
+
|
|
166
|
+
def getI2COverlays():
|
|
167
|
+
i2c_overlays = []
|
|
168
|
+
i2c_arm = False
|
|
169
|
+
i2c_arm_line_no = -1
|
|
170
|
+
with open(bootConfigPath, "r") as config:
|
|
171
|
+
for index, line in enumerate(config):
|
|
172
|
+
if line.startswith("dtoverlay=i2c-gpio"):
|
|
173
|
+
i2c_overlays.append(_extractI2CInfo(line.strip()))
|
|
174
|
+
i2c_overlays[-1]["line_no"] = index
|
|
175
|
+
elif line.startswith("dtparam=i2c_arm="):
|
|
176
|
+
if line.strip().split("=")[-1] == "on":
|
|
177
|
+
i2c_arm=True
|
|
178
|
+
i2c_arm_line_no = index
|
|
179
|
+
|
|
180
|
+
return (i2c_arm, i2c_arm_line_no, i2c_overlays)
|
|
181
|
+
|
|
182
|
+
# Function to find matches and extract information with improved flexibility
|
|
183
|
+
def _extractI2CInfo(line):
|
|
184
|
+
# Use `findall` to capture all matching parts, regardless of order
|
|
185
|
+
matches = re.findall(r"(bus=(\d+)|i2c_gpio_sda=(\d+)|i2c_gpio_scl=(\d+))", line)
|
|
186
|
+
result = {"bus": None, "sda": None, "scl": None}
|
|
187
|
+
|
|
188
|
+
for match in matches:
|
|
189
|
+
if match[1]: # `bus`
|
|
190
|
+
result["bus"] = match[1]
|
|
191
|
+
elif match[2]: # `sda`
|
|
192
|
+
result["sda"] = match[2]
|
|
193
|
+
elif match[3]: # `scl`
|
|
194
|
+
result["scl"] = match[3]
|
|
195
|
+
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
def _createI2CLine(bus, sda, scl):
|
|
199
|
+
line = f"dtoverlay=i2c-gpio"
|
|
200
|
+
if bus:
|
|
201
|
+
line += f",bus={bus}"
|
|
202
|
+
if sda:
|
|
203
|
+
line += f",i2c_gpio_sda={sda}"
|
|
204
|
+
if scl:
|
|
205
|
+
line += f",i2c_gpio_scl={scl}"
|
|
206
|
+
return line
|
|
207
|
+
|
|
208
|
+
def _i2cInteractive():
|
|
209
|
+
i2c_arm, i2c_arm_line_no, i2c_overlays = getI2COverlays()
|
|
210
|
+
preferred_gpios = {"17","27","22","23","24","25","5","6","16","26"}
|
|
211
|
+
other_gpios = {"4", "14", "15", "18", "9", "10", "11", "7", "8", "13", "19", "20", "21"}
|
|
212
|
+
allowed_buses = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}
|
|
213
|
+
|
|
214
|
+
for overlay in i2c_overlays:
|
|
215
|
+
preferred_gpios.discard(overlay["sda"])
|
|
216
|
+
other_gpios.discard(overlay["sda"])
|
|
217
|
+
preferred_gpios.discard(overlay["scl"])
|
|
218
|
+
other_gpios.discard(overlay["scl"])
|
|
219
|
+
allowed_buses.discard(overlay["bus"])
|
|
220
|
+
|
|
221
|
+
allowed_gpios = list(preferred_gpios | other_gpios)
|
|
222
|
+
allowed_gpios.sort(key=lambda x: int(x))
|
|
223
|
+
allowed_buses = list(allowed_buses)
|
|
224
|
+
allowed_buses.sort(key = lambda x: int(x))
|
|
225
|
+
available_preferred_gpios = list(preferred_gpios)
|
|
226
|
+
available_preferred_gpios.sort(key=lambda x: int(x))
|
|
227
|
+
bus_num = Prompt.ask("What bus number do you want to use?\n", choices=allowed_buses)
|
|
228
|
+
gpio_prompt = f"""Which GPIO pin do you want to use for your <type>?\n
|
|
229
|
+
Note: You are not required to choose from this list, but the preferred list
|
|
230
|
+
below are GPIO's that aren't used by any builtin software\n
|
|
231
|
+
Preferred List: {available_preferred_gpios}\n
|
|
232
|
+
"""
|
|
233
|
+
print(allowed_gpios)
|
|
234
|
+
sda_num = Prompt.ask(gpio_prompt.replace("<type>", "SDA"), choices=allowed_gpios)
|
|
235
|
+
allowed_gpios.remove(sda_num)
|
|
236
|
+
scl_num = Prompt.ask(gpio_prompt.replace("<type>", "SCL"), choices=allowed_gpios)
|
|
237
|
+
|
|
238
|
+
with open(bootConfigPath, "a") as config:
|
|
239
|
+
config.write("\n" + _createI2CLine(bus_num, sda_num, scl_num))
|
|
240
|
+
|
|
241
|
+
success("Added i2c_arm to config.txt. It will now be automatically loaded on reboot")
|
|
242
|
+
cmd = addOverlayCmd.replace("<bus>", bus_num).replace("<sda>", sda_num).replace("<scl>", scl_num).strip()
|
|
243
|
+
try:
|
|
244
|
+
subprocess.run(cmd.split(), check=True)
|
|
245
|
+
success(f"Also loaded in i2c_arm right now, so you can use it before reboot.\nTo test it out, run `i2cdetect -y {bus_num}`")
|
|
246
|
+
except subprocess.CalledProcessError as e:
|
|
247
|
+
warning(f"We tried to load I2C right now but something went wrong. You likely need to reboot to have access to I2C. The error message was\n\n{e}")
|
|
248
|
+
|
|
249
|
+
if __name__ == "__main__":
|
|
250
|
+
results = getI2COverlays()
|
|
251
|
+
print(results)
|
|
252
|
+
|