Compare commits

..

10 Commits

Author SHA1 Message Date
Dlr Rpi
dc349ff68d Justfile and add numpy 2026-01-15 14:11:28 +01:00
caa6ae4432 Fix everything? 2026-01-15 12:52:21 +01:00
Jonas Hahn
460b874651 Added uv and also some comments for the angle calculation 2025-12-20 10:30:25 +01:00
Jonas Hahn
209dca235f Added simple angle script Makefile and a simple shell nix 2025-12-20 10:29:28 +01:00
Dlr Rpi
2c10ddd173 Add uv for dependency management 2025-12-15 17:30:42 +01:00
Dlr Rpi
d64ffda2d6 makefile 2025-11-13 12:33:51 +01:00
Dlr Rpi
2d2befbbfd Debug 2025-11-06 17:11:33 +01:00
Dlr Rpi
5188bf0f25 Update the cables 2025-11-06 17:01:28 +01:00
Dlr Rpi
857af9857b first test 2025-11-06 16:56:20 +01:00
Dlr Rpi
aeeb78bf98 Changed structure 2025-11-06 16:20:44 +01:00
20 changed files with 1113 additions and 462 deletions

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.13

View File

@@ -13,3 +13,35 @@ from adafruit_servokit import ServoKit
kit = ServoKit(channels=16) kit = ServoKit(channels=16)
kit.servo[0].angle = 180 kit.servo[0].angle = 180
``` ```
```text
https://pinout.xyz/ Full pinout for the rpi3
https://components101.com/sites/default/files/component_datasheet/SG90%20Servo%20Motor%20Datasheet.pdf Datasheet for the servomotor used
https://ben.akrin.com/raspberry-pi-servo-jitter/ Blog post how to fix jittering
```
Local address
inet6 fe80::7e2c:ada5:5de7:9a2c/64
## Cables
From right to left
```
..............................
. ---------- 1x .
. 2x .
. 3x .
. xx .
. ooooo 4x .
. ooooo xx .
. ooooo xx .
. xx .
. xx .
. .
1: white
2: purlple
3: yellow
4: black
```

View File

@@ -1,61 +0,0 @@
import pigpio
import RPi.GPIO as GPIO
import time
import os
from motor import Motor # Models the motor
# Constants
SERVO1_PIN = 18
SERVO2_PIN = 19
BUTTON1_FWD = 5
BUTTON1_BWD = 6
BUTTON2_FWD = 17
BUTTON2_BWD = 27
SHUTDOWN_BTN = 26
STEP = 2
LOOP_DELAY = 0.3 # In seconds
# Local pi
pi = pigpio.pi()
if not pi:
os.exit()
# Setup the controls
GPIO.setmode(GPIO.BCM)
for btn in [BUTTON1_FWD, BUTTON1_BWD, BUTTON2_FWD, BUTTON2_BWD, SHUTDOWN_BTN]:
GPIO.setup(btn, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
# Setup motors
m1 = Motor(pi, SERVO1_PIN)
m2 = Motor(pi, SERVO2_PIN)
# Main
try:
while True:
# Inputs shutdown
if GPIO.input(SHUTDOWN_BTN) == GPIO.HIGH:
os.system("sudo shutdown now")
# Motors
if GPIO.input(BUTTON1_FWD):
m1.inc(STEP)
elif GPIO.input(BUTTON1_BWD):
m1.inc(-STEP)
if GPIO.input(BUTTON2_FWD):
m2.inc(STEP)
elif GPIO.input(BUTTON2_BWD):
m2.inc(-STEP)
time.sleep(LOOP_DELAY)
except KeyboardInterrupt:
pass
finally:
del m1
del m2
pi.stop()
GPIO.cleanup()

View File

@@ -1,41 +0,0 @@
# https://learn.adafruit.com/16-channel-pwm-servo-driver/python-circuitpython
import time
from adafruit_servokit import ServoKit
MIN = 550
MAX = 2450
CHANNELS = 16
kit = ServoKit(channels=CHANNELS, frequency=50)
for i in range(CHANNELS):
kit.servo[i].angle = 0
time.sleep(2)
# Testing the accuracy of the lib
num = 14
kit.servo[num].angle = 0
time.sleep(2)
kit.servo[num].angle = 90
time.sleep(2)
kit.servo[num].angle = 30
time.sleep(2)
kit.servo[num].angle = 120
time.sleep(2)
kit.servo[num].angle = 60
time.sleep(2)
kit.servo[num].angle = 150
time.sleep(1.5)
kit.servo[num].angle = 90
time.sleep(1.5)
kit.servo[num].angle = 180

View File

@@ -1,96 +0,0 @@
import time
from adafruit_servokit import ServoKit
class MultiSmoothServoController:
def __init__(self, channels=16, min_pulse=500, max_pulse=2500):
self.kit = ServoKit(channels=channels)
self.channels = channels
self.min_pulse = min_pulse
self.max_pulse = max_pulse
# Track last angle and pulse per servo
self._last_angles = [None] * channels
self._last_pulses = [None] * channels
# Set pulse width range for all channels upfront
for ch in range(channels):
self.kit.servo[ch].set_pulse_width_range(min_pulse, max_pulse)
def angle_to_pulse(self, angle):
"""Convert angle (0-180) to pulse width in microseconds"""
return int(self.min_pulse + (self.max_pulse - self.min_pulse) * angle / 180)
def set_angle(self, channel, angle):
"""Set angle for a single servo channel, avoid jitter"""
if channel < 0 or channel >= self.channels:
raise ValueError(f"Channel must be between 0 and {self.channels - 1}")
if angle < 0 or angle > 180:
raise ValueError("Angle must be between 0 and 180")
pulse = self.angle_to_pulse(angle)
if self._last_angles[channel] != angle or self._last_pulses[channel] != pulse:
self.kit.servo[channel].pulse_width = pulse
self._last_angles[channel] = angle
self._last_pulses[channel] = pulse
def smooth_move(self, channel, start_angle, end_angle, step=1, delay=0.02):
"""Smoothly move one servo from start_angle to end_angle"""
if start_angle < end_angle:
angles = range(start_angle, end_angle + 1, step)
else:
angles = range(start_angle, end_angle - 1, -step)
for angle in angles:
self.set_angle(channel, angle)
time.sleep(delay)
def smooth_move_all(self, start_angles, end_angles, step=1, delay=0.02):
"""
Smoothly move all servos from their respective start_angles to end_angles.
Both start_angles and end_angles should be lists/tuples of length = number of channels.
"""
if len(start_angles) != self.channels or len(end_angles) != self.channels:
raise ValueError("start_angles and end_angles must have length equal to number of channels")
max_steps = 0
# Calculate how many steps each servo needs and track max
steps_per_servo = []
for start, end in zip(start_angles, end_angles):
steps = abs(end - start) // step
steps_per_servo.append(steps)
if steps > max_steps:
max_steps = steps
for i in range(max_steps + 1):
for ch in range(self.channels):
start = start_angles[ch]
end = end_angles[ch]
if start < end:
angle = min(start + i * step, end)
else:
angle = max(start - i * step, end)
self.set_angle(ch, angle)
time.sleep(delay)
def stop_all(self):
"""Stop pulses to all servos (optional)"""
for ch in range(self.channels):
self.kit.servo[ch].pulse_width = 0
self._last_angles[ch] = None
self._last_pulses[ch] = None
controller = MultiSmoothServoController()
# Move servo channel 2 smoothly from 0 to 180 degrees
controller.smooth_move(channel=2, start_angle=0, end_angle=180)
# Smoothly move multiple servos simultaneously:
start_positions = [0] * 16 # all servos at 0°
end_positions = [90, 45, 180, 0, 30, 60, 90, 120, 150, 180, 0, 90, 45, 135, 180, 0]
controller.smooth_move_all(start_positions, end_positions)
# Stop all servos
controller.stop_all()

View File

@@ -1,3 +0,0 @@
Local addresses
inet 169.254.217.237/16
inet6 fe80::d89b:cecc:9c55:e1c3/64

5
justfile Normal file
View File

@@ -0,0 +1,5 @@
sim:
uv run python simulation.py
sync:
rsync -r --exclude=venv ~/solarmotor guest@hahn1.one:

View File

@@ -1,3 +0,0 @@
https://pinout.xyz/ Full pinout for the rpi3
https://components101.com/sites/default/files/component_datasheet/SG90%20Servo%20Motor%20Datasheet.pdf Datasheet for the servomotor used
https://ben.akrin.com/raspberry-pi-servo-jitter/ Blog post how to fix jittering

6
main.py Normal file
View File

@@ -0,0 +1,6 @@
def main():
print("Hello from solarmotor!")
if __name__ == "__main__":
main()

0
objects/__init__.py Normal file
View File

15
objects/board.py Normal file
View File

@@ -0,0 +1,15 @@
from adafruit_servokit import ServoKit
class Board:
MIN = 500
MAX = 2500
def __init__(self, channels=16, frequency=50):
self.channels = channels
self.frequency = frequency
self.kit = ServoKit(channels=channels, frequency=frequency)
for i in range(channels):
self.kit.servo[i].set_pulse_width_range(Board.MIN, Board.MAX)
self.kit.servo[i].actuation_range = 180
self.kit.servo[i].angle = 0

View File

@@ -1,10 +1,7 @@
"""Helpers for building moving mirrors.""" """Helpers for building moving mirrors."""
from adafruit_servokit import ServoKit from objects.board import Board
import time
class Board:
def __init__(self, channels=16, frequency=50):
self.kit = ServoKit(channels=channels, frequency=frequency)
class Motor: class Motor:
"""Model a type of servo motor.""" """Model a type of servo motor."""
@@ -13,42 +10,41 @@ class Motor:
MAX_PULSE = 2500 MAX_PULSE = 2500
MIN_PULSE = 500 MIN_PULSE = 500
COVERAGE = 180 # Total degree of freedom in degrees COVERAGE = 180 # Total degree of freedom in degrees
AREA = MAX_PULSE - MIN_PULSE OFFSET = 0 # In degrees a constant to be added
OFFSET = 2 # In degrees a constant to be added
SCALE = 1 # Scaling SCALE = 1 # Scaling
# Used for ids # Used for ids
count = 0 count = 0
def __init__(self, pi, pin, angle=0): def __init__(self, board: Board, angle=0):
self.id = Motor.count; Motor.count += 1 self.board: Board = board
self.pi = pi # Local pi instance self.id: int = Motor.count
Motor.count += 1
self.pin = pin
self.angle = angle self.angle = angle
self.offset = Motor.OFFSET # Fine grained controls over every motor self.offset = Motor.OFFSET # Fine grained controls over every motor
self.coverage = Motor.COVERAGE
self.scale = Motor.SCALE self.scale = Motor.SCALE
# Initialization # Initialization
self.set() self.set()
def angle_to_pulse(self, angle):
return min(max(Motor.MIN_PULSE, (Motor.MIN_PULSE + Motor.AREA * angle/Motor.COVERAGE + self.offset) * self.scale), Motor.MAX_PULSE)
# Update the motor position on hardware # Update the motor position on hardware
def set(self): def set(self):
self.pi.set_servo_pulsewidth(self.pin, self.angle_to_pulse(self.angle)) self.board.kit.servo[self.id].angle = self.angle * self.scale + self.offset
def safe_set_angle(angle=0, sleep=0.01, offset=1):
self.board.kit.servo[NUM].angle = angle + offset
time.sleep(sleep)
kit.servo[NUM].angle = angle
def set_angle(self, angle): def set_angle(self, angle):
self.angle = angle self.angle = min(self.coverage, max(0, angle)) # Double check bad
self.set() self.set()
def __str__(self): def __str__(self):
return f"Motor {self.id} is set at {self.angle} degrees." return f"Motor {self.id} is set at {self.angle} degrees."
def __del__(self):
self.pi.set_servo_pulsewidth(self.pin, 0)
def inc(self, inc): def inc(self, inc):
self.angle += inc self.angle += inc
self.angle = min(max(self.angle, 0), Motor.COVERAGE) # Clip self.angle = min(max(self.angle, 0), Motor.COVERAGE) # Clip

170
objects/solar.py Normal file
View File

@@ -0,0 +1,170 @@
"""Alle gemessenen Koordinaten der Quelle und der Sonne haben den Ursprung in der linken unteren Ecke des Clusters in einem rechtshaendigen flachen System.
"""
import math
import objects.motor as motor
import numpy as np
# Einheitsvektoren
unit_x = np.array([1, 0, 0])
unit_y = np.array([0, 1, 0])
unit_z = np.array([0, 0, 1])
def get_axis(axis):
"Axis are numbered from 1 to 3 from x to z."
match axis:
case 1:
ax = unit_x
case 2:
ax = unit_y
case 3:
ax = unit_z
case _:
ax = unit_x
return ax
def proj(vec, axis: int =1):
"""Simple vector projection onto an axis."""
ax = get_axis(axis)
return np.dot(vec, ax) * ax
def abs_custom(vec):
l = 0
for i in range(3):
l += vec[i] ** 2
return np.sqrt(l)
def rotate(v, angle=90, axis=1):
"Rotate a vector with an angle around a axis with the right hand rule."
angle = angle/180 * np.pi
k = get_axis(axis)
return (
v * np.cos(angle)
+ np.cross(k, v) * np.sin(angle)
+ k * np.dot(k, v) * (1 - np.cos(angle))
)
def agl(a, b):
"Get the angle between two vectors. This is always between 0 and 180 degree."
return np.round(np.acos(np.dot(a, b)/(abs_custom(a) * abs_custom(b)))/(2 * np.pi) * 360)
def normalize(vec):
l = abs_custom(vec)
return vec/l
def get_angles(source, target):
"""Main function to get the phi and theta angles for a source and a target vector. Both vectors must lie on the front half sphere.
Phi is from 0 to 180 where 0 means left when you look at the mirrors. The hardware is bounded between 45 and 135 degree. Thus the here provided angle needs to be subtracted by 45 and then doubled.
Theta is from 0 to 90 where 0 means up."""
source_planar = source - proj(source, 3)
target_planar = target - proj(target, 3)
source_phi = agl(source_planar, unit_x)
target_phi = agl(target_planar, unit_x)
source_theta = agl(rotate(source, 90 - source_phi, 3), unit_z)
target_theta = agl(rotate(target, 90 - target_phi, 3), unit_z)
phi = None
theta = None
theta_diff = None
phi_diff = agl(source_planar, target_planar)
if source_phi < target_phi:
rota = rotate(source_planar, phi_diff, 3)
theta_diff = agl(rota, target)
phi = source_phi + phi_diff/2
else:
rota = rotate(target_planar, phi_diff, 3)
theta_diff = agl(rota, source)
phi = target_phi + phi_diff/2
if source_theta < target_theta:
theta = target_theta + theta_diff/2
else:
theta = source_theta + theta_diff/2
return (phi, theta)
class MovingEntity:
"""Embedded entity in the world with a position."""
def __init__(self, world):
self.world = world
self.pos = (0.0, 0.0, 0.0) # (x, y, z) in local untilted coordinates
def get_pos_rotated(self):
"""Return position rotated by world's tilt around y-axis."""
return self.world.rotate_point_y(self.pos)
def move(self, dx=0, dy=0, dz=0):
self.pos = (self.pos[0] + dx, self.pos[1] + dy, self.pos[2] + dz)
class Target(MovingEntity):
def __init__(self, world, pos=(0.0, 0.0, 0.0)):
super().__init__(world)
self.pos = pos
class Source(MovingEntity):
def __init__(self, world, pos=(10.0, 10.0, 10.0)):
super().__init__(world)
self.pos = pos
class Mirror:
def __init__(self, world, cluster_x=0, cluster_y=0):
self.world = world
self.cluster_x = cluster_x
self.cluster_y = cluster_y
# Store the motors
self.phi = motor.Motor(self.world.board)
self.theta = motor.Motor(self.world.board)
# Position in un-tilted coordinate system
self.pos = (cluster_x * self.world.grid_size, cluster_y * self.world.grid_size, 0.0)
def get_pos_rotated(self):
return self.world.rotate_point_y(self.pos)
def set_angle_from_source_target(self, source: Source, target: Target):
"Set the angles of a mirror from global source and target vectors."
rot_pos = np.array([self.get_pos_rotated()])
rel_source = np.array([source.pos]) - rot_pos
rel_target = np.array([target.pos]) - rot_pos
phi, theta = get_angles(rel_source, rel_target)
# Update the angles based on the normals in rotated positions
self.phi.set_angle(phi)
self.theta.set_angle(theta)
def get_angles(self):
return self.phi.angle, self.theta.angle
class World:
def __init__(self, board, tilt_deg=0.0):
self.board = board
self.grid_size = 10 # In cm
self.tilt_deg = tilt_deg # Tilt of the grid system around y-axis
self.mirrors = []
def add_mirror(self, mirror):
self.mirrors.append(mirror)
def update_mirrors_from_source_target(self, source: Source, target: Target):
for mirror in self.mirrors:
mirror.set_angle_from_source_target(source, target)
def rotate_point_y(self, point):
"""Rotate a point around the y-axis by the world's tilt angle."""
x, y, z = point
theta = math.radians(self.tilt_deg)
cos_t = math.cos(theta)
sin_t = math.sin(theta)
x_rot = x * cos_t + z * sin_t
y_rot = y
z_rot = -x * sin_t + z * cos_t
return (x_rot, y_rot, z_rot)

49
pyproject.toml Normal file
View File

@@ -0,0 +1,49 @@
[project]
name = "solarmotor"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"adafruit-blinka==8.66.2",
"adafruit-circuitpython-busdevice==5.2.14",
"adafruit-circuitpython-connectionmanager==3.1.6",
"adafruit-circuitpython-motor==3.4.18",
"adafruit-circuitpython-pca9685==3.4.20",
"adafruit-circuitpython-register==1.11.1",
"adafruit-circuitpython-requests==4.1.14",
"adafruit-circuitpython-servokit==1.3.22",
"adafruit-circuitpython-typing==1.12.2",
"adafruit-platformdetect==3.84.1",
"adafruit-pureio==1.1.11",
"attrs==25.4.0",
"binho-host-adapter==0.1.6",
"black==25.12.0",
"cattrs==25.3.0",
"click==8.3.1",
"docstring-to-markdown==0.17",
"importlib-metadata==8.7.0",
"jedi==0.19.2",
"jedi-language-server==0.46.0",
"lsprotocol==2025.0.0",
"mypy-extensions==1.1.0",
"numpy>=2.4.1",
"packaging==25.0",
"parso==0.8.5",
"pathspec==0.12.1",
"platformdirs==4.5.1",
"pluggy==1.6.0",
"pyftdi==0.57.1",
"pygls==2.0.0",
"pyserial==3.5",
"python-lsp-jsonrpc==1.1.2",
"python-lsp-server==1.14.0",
"pytokens==0.3.0",
"pyusb==1.3.1",
"rpi-gpio==0.7.1",
"ruff>=0.14.9",
"sysv-ipc==1.1.0",
"typing-extensions==4.15.0",
"ujson==5.11.0",
"zipp==3.23.0",
]

13
shell.nix Normal file
View File

@@ -0,0 +1,13 @@
{ pkgs ? import <nixpkgs> {} }:
# Simple python shell for all packages
pkgs.mkShell {
buildInputs = with pkgs; [
(pkgs.python313.withPackages (ps: with ps; [
matplotlib
numpy
ty
]))
];
}

107
simple-angle.py Normal file
View File

@@ -0,0 +1,107 @@
import numpy as np
# Einheitsvektoren
unit_x = np.array([1, 0, 0])
unit_y = np.array([0, 1, 0])
unit_z = np.array([0, 0, 1])
def get_axis(axis):
"Axis are numbered from 1 to 3 from x to z."
match axis:
case 1:
ax = unit_x
case 2:
ax = unit_y
case 3:
ax = unit_z
case _:
ax = unit_x
return ax
def proj(vec, axis: int =1):
"""Simple vector projection onto an axis."""
ax = get_axis(axis)
return np.dot(vec, ax) * ax
def abs_custom(vec):
l = 0
for i in range(3):
l += vec[i] ** 2
return np.sqrt(l)
def rotate(v, angle=90, axis=1):
"Rotate a vector with an angle around a axis with the right hand rule."
angle = angle/180 * np.pi
k = get_axis(axis)
return (
v * np.cos(angle)
+ np.cross(k, v) * np.sin(angle)
+ k * np.dot(k, v) * (1 - np.cos(angle))
)
def agl(a, b):
"Get the angle between two vectors. This is always between 0 and 180 degree."
return np.round(np.acos(np.dot(a, b)/(abs_custom(a) * abs_custom(b)))/(2 * np.pi) * 360)
def normalize(vec):
l = abs_custom(vec)
return vec/l
def get_angles(source, target):
"""Main function to get the phi and theta angles for a source and a target vector. Both vectors must lie on the front half sphere.
Phi is from 0 to 180 where 0 means left when you look at the mirrors. The hardware is bounded between 45 and 135 degree. Thus the here provided angle needs to be subtracted by 45 and then doubled.
Theta is from 0 to 90 where 0 means up."""
source_planar = source - proj(source, 3)
target_planar = target - proj(target, 3)
source_phi = agl(source_planar, unit_x)
target_phi = agl(target_planar, unit_x)
source_theta = agl(rotate(source, 90 - source_phi, 3), unit_z)
target_theta = agl(rotate(target, 90 - target_phi, 3), unit_z)
phi = None
theta = None
theta_diff = None
phi_diff = agl(source_planar, target_planar)
if source_phi < target_phi:
rota = rotate(source_planar, phi_diff, 3)
theta_diff = agl(rota, target)
phi = source_phi + phi_diff/2
else:
rota = rotate(target_planar, phi_diff, 3)
theta_diff = agl(rota, source)
phi = target_phi + phi_diff/2
if source_theta < target_theta:
theta = target_theta + theta_diff/2
else:
theta = source_theta + theta_diff/2
return (phi, theta)
GRID_SIZE = 10
# Aufbau der Koordinaten
# Das Zentrum des Spiegels hinten rechts bildet den Ursprung
# Dann geht die x-Achse nach links und die y-Achse nach vorne
# X, Y, Z
source_orig = np.array([0, 20, 0])
target_orig = np.array([0, 20, 0])
# Strategie des Programms
# 1. Iteration ueber jeden Spiegel
# 2. Berechnung des Quellvektors und des Targetvektors fuer die Position des Spiegels
# 3. Berechne
for x in range(4):
for y in range(2):
x_size = x * GRID_SIZE
y_size = y * GRID_SIZE
phi, theta = get_angles(source_orig - unit_x * x_size - unit_y * y_size, target_orig - unit_x * x_size - unit_y * y_size)
print(f"For grid ({x}, {y}), phi = {phi} and theta = {theta}.")

View File

@@ -1,107 +1,56 @@
import pigpio
import RPi.GPIO as GPIO
import time import time
import math
# Solar module for simulation of world # Solar module for simulation of world
import solar # Modeling of the world import objects.solar as solar # Modeling of the world
from motor import Motor # Small helper functions and constants
# Constants from objects.motor import Motor # Small helper functions and constants
SERVO1_PIN = 18 from objects.board import Board
SERVO2_PIN = 19
INIT_PULSE = 0
STEP = 10 STEP = 10
LOOP_DELAY = 0.01 # In seconds LOOP_DELAY = 0.005 # In seconds
pi = pigpio.pi()
if not pi.connected:
print("Cannot connect to pigpio daemon!")
exit()
angle1 = init_motor(SERVO1_PIN)
angle2 = init_motor(SERVO2_PIN)
# Testing embedding the mirrors in the world # Testing embedding the mirrors in the world
world = solar.World(tilt_deg=15) # The world is tilted 15 degrees around y-axis board = Board()
world = solar.World(board, tilt_deg=0)
HEIGHT = 30 HEIGHT = 30
source = solar.Source(world, pos=(0, 0, 30)) source = solar.Source(world, pos=(0, 50, 0))
target = solar.Target(world, pos=(20, 0, 30)) target = solar.Target(world, pos=(0, 50, 0))
# Create mirrors in a 9x9 grid # Create mirrors in a 3x2 grid
for x in range(3): for x in range(4):
for y in range(3): for y in range(2):
mirror = solar.Mirror(world, cluster_x=x, cluster_y=y) mirror = solar.Mirror(world, cluster_x=x, cluster_y=y)
world.add_mirror(mirror) world.add_mirror(mirror)
world.update_mirrors_from_source_target(source, target) world.update_mirrors_from_source_target(source, target)
for i, mirror in enumerate(world.mirrors): def print_status():
pitch, yaw = mirror.get_angles() for i, mirror in enumerate(world.mirrors):
print(f"Mirror {i} ({mirror.cluster_x}, {mirror.cluster_y}) angles -> pitch: {pitch:.2f}°, yaw: {yaw:.2f}°") pitch, yaw = mirror.get_angles()
print(f"Mirror {i} ({mirror.cluster_x}, {mirror.cluster_y}) angles -> pitch: {pitch:.2f}°, yaw: {yaw:.2f}°")
def sm1(a): # Set motor 1
pi.set_servo_pulsewidth(SERVO1_PIN, angle_to_pulse(a))
time.sleep(2)
angle = 150
time.sleep(10)
sm1(150)
sm1(90)
sm1(0)
sm1(180)
sm1(30)
sm1(120)
sm1(60)
sm1(30)
sm1(180)
sm1(120)
sm1(30)
sm1(150)
sm1(3)
print_status()
a = 1
t = time.time()
# Main # Main
try: try:
while True: while True:
# Shutdown source.move(0, 0, 0.1)
if GPIO.input(SHUTDOWN_BTN) == GPIO.HIGH: #source.move(10 * math.sin(a * t), 10 * math.cos(a * t))
os.system("sudo shutdown now") print(source.pos)
print(target.pos)
pulse1 = angle_to_pulse(angle) world.update_mirrors_from_source_target(source, target)
time.sleep(3) print_status()
angle += 10
# Motor 1
target1 = pulse1
if GPIO.input(BUTTON1_FWD):
target1 = min(MAX_PULSE, pulse1 + STEP)
elif GPIO.input(BUTTON1_BWD):
target1 = max(MIN_PULSE, pulse1 - STEP)
pulse1 = move_servo(pulse1, target1)
pi.set_servo_pulsewidth(SERVO1_PIN, pulse1)
# Motor 2
target2 = pulse2
if GPIO.input(BUTTON2_FWD):
target2 = min(MAX_PULSE, pulse2 + STEP)
elif GPIO.input(BUTTON2_BWD):
target2 = max(MIN_PULSE, pulse2 - STEP)
pulse2 = move_servo(pulse2, target2)
pi.set_servo_pulsewidth(SERVO2_PIN, pulse2)
time.sleep(LOOP_DELAY) time.sleep(LOOP_DELAY)
t = time.time()
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
finally:
pi.set_servo_pulsewidth(SERVO1_PIN, 0)
pi.set_servo_pulsewidth(SERVO2_PIN, 0)
pi.stop()
GPIO.cleanup()

107
solar.py
View File

@@ -1,107 +0,0 @@
"""Alle gemessenen Koordinaten der Quelle und der Sonne haben den Ursprung in der linken unteren Ecke des Clusters in einem rechtshaendigen flachen System.
"""
import math
class MovingEntity:
"""Embedded entity in the world with a position."""
def __init__(self, world):
self.world = world
self.pos = (0.0, 0.0, 0.0) # (x, y, z) in local untilted coordinates
def get_pos_rotated(self):
"""Return position rotated by world's tilt around y-axis."""
return self.world.rotate_point_y(self.pos)
class Target(MovingEntity):
def __init__(self, world, pos=(0.0, 0.0, 0.0)):
super().__init__(world)
self.pos = pos
class Source(MovingEntity):
def __init__(self, world, pos=(10.0, 10.0, 10.0)):
super().__init__(world)
self.pos = pos
class Mirror:
def __init__(self, world, pitch_pin, yaw_pin, cluster_x=0, cluster_y=0):
self.world = world
self.cluster_x = cluster_x
self.cluster_y = cluster_y
# Store the motors
self.yaw = motor.Motor(self.world.pi, yaw_pin)
self.pitch = motor.Motor(self.world.pi, pitch_pin)
# Position in un-tilted coordinate system
self.pos = (cluster_x * self.world.grid_size, cluster_y * self.world.grid_size, 0.0)
def get_pos_rotated(self):
return self.world.rotate_point_y(self.pos)
def set_angle_from_source_target(self, source: Source, target: Target):
# Get rotated positions
pos_mirror = self.get_pos_rotated()
pos_source = source.get_pos_rotated()
pos_target = target.get_pos_rotated()
v_source = (
pos_source[0] - pos_mirror[0],
pos_source[1] - pos_mirror[1],
pos_source[2] - pos_mirror[2],
)
v_target = (
pos_target[0] - pos_mirror[0],
pos_target[1] - pos_mirror[1],
pos_target[2] - pos_mirror[2],
)
def normalize(v):
length = math.sqrt(v[0] ** 2 + v[1] ** 2 + v[2] ** 2)
if length == 0:
return (0, 0, 0)
return (v[0] / length, v[1] / length, v[2] / length)
v_source_n = normalize(v_source)
v_target_n = normalize(v_target)
mirror_normal = (
v_source_n[0] + v_target_n[0],
v_source_n[1] + v_target_n[1],
v_source_n[2] + v_target_n[2],
)
mirror_normal = normalize(mirror_normal)
# Update the angles based on the normals in rotated positions
self.yaw.set_angle(math.degrees(math.atan2(mirror_normal[0], mirror_normal[2])))
self.pitch.set_angle(math.degrees(math.atan2(mirror_normal[1], mirror_normal[2])))
def get_angles(self):
return self.yaw.angle, self.pitch.angle
class World:
def __init__(self, pi, tilt_deg=0.0):
self.pi = pi
self.grid_size = 10 # In cm
self.tilt_deg = tilt_deg # Tilt of the grid system around y-axis
self.mirrors = []
def add_mirror(self, mirror):
self.mirrors.append(mirror)
def update_mirrors_from_source_target(self, source: Source, target: Target):
for mirror in self.mirrors:
mirror.set_angle_from_source_target(source, target)
def rotate_point_y(self, point):
"""Rotate a point around the y-axis by the world's tilt angle."""
x, y, z = point
theta = math.radians(self.tilt_deg)
cos_t = math.cos(theta)
sin_t = math.sin(theta)
x_rot = x * cos_t + z * sin_t
y_rot = y
z_rot = -x * sin_t + z * cos_t
return (x_rot, y_rot, z_rot)

54
test.py
View File

@@ -1,54 +0,0 @@
import time
from adafruit_servokit import ServoKit
# This is set to zero for the max range
OFFSET = -10
MIN = 500 + OFFSET
MAX = 2500 - OFFSET
CHANNELS = 16
RANGE = 180
INIT = 0
FREQUENCY = 50
# Setup the kit
kit = ServoKit(channels=CHANNELS, frequency=FREQUENCY)
for i in range(CHANNELS):
kit.servo[i].set_pulse_width_range(MIN, MAX)
kit.servo[i].actuation_range = RANGE
kit.servo[i].angle = INIT
time.sleep(2)
# Testing the accuracy of the lib
NUM = 8
OFFSET = 5
# Set the angle
def sa(angle=0, sleep=1.5):
print(f"Set angle of pin {NUM+1} to {angle}.")
kit.servo[NUM].angle = angle + OFFSET
time.sleep(0.01)
kit.servo[NUM].angle = angle
kit.servo[NUM].angle = angle - OFFSET
time.sleep(0.01)
kit.servo[NUM].angle = angle
time.sleep(sleep)
sa(80)
exit()
#for i in range(180):
#sa(i, 0)
#time.sleep(0.1)
# Run the actual testing
sa(10)
sa(90)
sa(30)
sa(170)
sa(60)
sa(150)
sa(90, 0)

673
uv.lock generated Normal file
View File

@@ -0,0 +1,673 @@
version = 1
revision = 3
requires-python = ">=3.13"
[[package]]
name = "adafruit-blinka"
version = "8.66.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-circuitpython-typing" },
{ name = "adafruit-platformdetect" },
{ name = "adafruit-pureio" },
{ name = "binho-host-adapter" },
{ name = "pyftdi" },
{ name = "sysv-ipc", marker = "platform_machine != 'mips' and sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/35/2d/7469c26dbeade72709096922db34a75fecb47c6953dba0cd4c14ee2b3b45/adafruit_blinka-8.66.2.tar.gz", hash = "sha256:35f72589aa45d8738922367753f5588700b043b35611510d8efbe818d1a355fd", size = 268839, upload-time = "2025-09-27T20:53:57.753Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/c8/49ab498e87d04977a0c706af25825c5051daf2553b41c5b10eb7e9e8b19e/adafruit_blinka-8.66.2-py3-none-any.whl", hash = "sha256:93cfd6df6c523bc1312b731bdb4d51711d3ec6e468f18c8d40e09b7339bb2574", size = 399931, upload-time = "2025-09-27T20:53:56.014Z" },
]
[[package]]
name = "adafruit-circuitpython-busdevice"
version = "5.2.14"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-typing" },
]
sdist = { url = "https://files.pythonhosted.org/packages/99/70/2bb69cf3e79fc1bda7b111bcf9f90ff25ac705960eff6bd1375ffad754ff/adafruit_circuitpython_busdevice-5.2.14.tar.gz", hash = "sha256:377f4d9c6b7ac256a0c686fe3d88bb2d10873cec992ff7805c8719a474e92127", size = 24468, upload-time = "2025-10-20T20:37:13.497Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f6/50/3fe9da5d3a66a05130ee5db64b5451cd2973f7f5981f80a72ce349ea1bdc/adafruit_circuitpython_busdevice-5.2.14-py3-none-any.whl", hash = "sha256:e7d049a2dc6a3fc9245e154d66725efeb190a61e7d192fbc8f96f3b76fe1fc77", size = 7501, upload-time = "2025-10-20T20:37:12.675Z" },
]
[[package]]
name = "adafruit-circuitpython-connectionmanager"
version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
]
sdist = { url = "https://files.pythonhosted.org/packages/bc/82/69ca5ef604b0513a5f85a583d255b2d0c48042592842dfbc955b610cf3b6/adafruit_circuitpython_connectionmanager-3.1.6.tar.gz", hash = "sha256:daa41f7675533fbb451c0f522c894911dd003f67b26f0e7be832f2e240ba28b4", size = 33798, upload-time = "2025-10-20T20:38:47.369Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/9e/174bff10a4ff6f74973ee6da43e7b715b07e34426cf4e71d625a7f96e1e5/adafruit_circuitpython_connectionmanager-3.1.6-py3-none-any.whl", hash = "sha256:83c7eafa294fc6e889076d1ba8a7ff902e29d062977d3602078b1594868b7042", size = 7768, upload-time = "2025-10-20T20:38:45.959Z" },
]
[[package]]
name = "adafruit-circuitpython-motor"
version = "3.4.18"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-typing" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3e/ae/03ecf070598d85027711307dabf1489c15c20f7c889e8e6f2fbcb60a2957/adafruit_circuitpython_motor-3.4.18.tar.gz", hash = "sha256:ab33975e7180cb89640debed431c2c86ba8496c4d5438e2f8bee5779549101ca", size = 31017, upload-time = "2025-10-20T20:40:27.433Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/0d/b1ac24da0e14be5906ecf580e3717bb034aa1a68389e22dc95567eb7983e/adafruit_circuitpython_motor-3.4.18-py3-none-any.whl", hash = "sha256:4b50e36b8508bc392269d258c8079ec49e768e13a61223f4cb9fb0139c5a0fdb", size = 10944, upload-time = "2025-10-20T20:40:26.473Z" },
]
[[package]]
name = "adafruit-circuitpython-pca9685"
version = "3.4.20"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-busdevice" },
{ name = "adafruit-circuitpython-register" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4c/28/d21ba2d5c5d60f810c7b6562d96e0e7725db8f3ad8157683a428df3f2f65/adafruit_circuitpython_pca9685-3.4.20.tar.gz", hash = "sha256:afeab2a15689e3961876d76e98b3608ae5157c553efe88ace982350323d0374a", size = 24970, upload-time = "2025-10-20T20:07:14.69Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/b9/40f8bdfbbb7668cf9c13301b596d9895063d60e0e252ccda835da8e258c7/adafruit_circuitpython_pca9685-3.4.20-py3-none-any.whl", hash = "sha256:3f2adf9e7a25b111bcf1f58ae2b546d9c8dbc6a13e8e963e1b12fc70b81cbf1c", size = 6029, upload-time = "2025-10-20T20:07:13.539Z" },
]
[[package]]
name = "adafruit-circuitpython-register"
version = "1.11.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-busdevice" },
{ name = "adafruit-circuitpython-typing" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f2/14/dde5accb99a72460f750037373b9df9a06d9f6915c0891433ca09b26a1a5/adafruit_circuitpython_register-1.11.1.tar.gz", hash = "sha256:45c6b6de5e65efe584375ea1bd6964866dab0d5e5b52ede49e906abd28876989", size = 31668, upload-time = "2025-10-20T20:46:55.559Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/b0/a847f66cbd60c9222fb2b16ebb6277d9e48e1cd5f3282835e4e84785ea28/adafruit_circuitpython_register-1.11.1-py3-none-any.whl", hash = "sha256:40b792f149a28f047c46a7c280313ad8384d75439354737ab1e88f107a2251f6", size = 19015, upload-time = "2025-10-20T20:46:54.671Z" },
]
[[package]]
name = "adafruit-circuitpython-requests"
version = "4.1.14"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-connectionmanager" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5e/0c/b61ba2eb109e973c10a08845ad1ea7479d779ad605d8ef70bfeef87a548c/adafruit_circuitpython_requests-4.1.14.tar.gz", hash = "sha256:36431be6eb1471bc6c281c8b48cc50eea08f3b7b22d82619e2a0e99d53d40fee", size = 67539, upload-time = "2025-10-20T20:47:22.25Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/2c/3d98d269353c0cbfabdc37d812f1cf3a554eea807ac25fdb1982e826af46/adafruit_circuitpython_requests-4.1.14-py3-none-any.whl", hash = "sha256:3f5b2a3856ae9205bb022bdbb9989c617b928432ca7bf8045e719bf25ec9c35d", size = 10884, upload-time = "2025-10-20T20:47:20.943Z" },
]
[[package]]
name = "adafruit-circuitpython-servokit"
version = "1.3.22"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-busdevice" },
{ name = "adafruit-circuitpython-motor" },
{ name = "adafruit-circuitpython-pca9685" },
{ name = "adafruit-circuitpython-register" },
]
sdist = { url = "https://files.pythonhosted.org/packages/94/0d/41b3dbf33eacb8aa46f762c223f7374565e6ea29c607a8072ec119564783/adafruit_circuitpython_servokit-1.3.22.tar.gz", hash = "sha256:626d5b6b98502b78ab50f582034bc84549e9a1939a76476d8d7df08aac86f76b", size = 23811, upload-time = "2025-10-20T20:44:56.542Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/0b/e781837b50d5750e631235673c3f259df522b2ca7af43895e22f03d7056a/adafruit_circuitpython_servokit-1.3.22-py3-none-any.whl", hash = "sha256:14245e9cd5edd32dd23d43d74a4f2ac74980033ecc517410232fb79dfc3e04ca", size = 5663, upload-time = "2025-10-20T20:44:55.664Z" },
]
[[package]]
name = "adafruit-circuitpython-typing"
version = "1.12.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-busdevice" },
{ name = "adafruit-circuitpython-requests" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/31/12/cd335c0adfdbf836cd6cfc0658238fe1e056481cc9d83d1960acd4af6b0b/adafruit_circuitpython_typing-1.12.2.tar.gz", hash = "sha256:8a38ad064a665f84ce7a89454f0f37fd072f77d1b5b1a207bc54181cdd0d7e26", size = 25358, upload-time = "2025-09-08T19:52:43.56Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/9b/87d9f26652b10250d427f50116c00fa90c80d21c9c6cc361b69692cc29ca/adafruit_circuitpython_typing-1.12.2-py3-none-any.whl", hash = "sha256:d03e20cb86c5daaadd5297b7d729bd9280fae540a86cbe9fdfa687ca5f4b1f45", size = 11014, upload-time = "2025-09-08T19:52:42.414Z" },
]
[[package]]
name = "adafruit-platformdetect"
version = "3.84.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1e/52/9641f5de40e9a85e69c01873eaee3e992e3d7dad32cd14fa7a8542994b1f/adafruit_platformdetect-3.84.1.tar.gz", hash = "sha256:2ed292fd292e3487db53c261e2879ae1831c607a8eb947beea20df6b5d24e42b", size = 49166, upload-time = "2025-10-14T18:33:15.526Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a6/78/9f5cd6031452f95b3e159e625a61d20148173db7f18817c52ab67aad3594/adafruit_platformdetect-3.84.1-py3-none-any.whl", hash = "sha256:53a9e34f32c48d7cd4b6a4497a0e4e07e05d5101cea055d60240ba8a3a0f06d8", size = 26628, upload-time = "2025-10-14T18:33:14.091Z" },
]
[[package]]
name = "adafruit-pureio"
version = "1.1.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e5/b7/f1672435116822079bbdab42163f9e6424769b7db778873d95d18c085230/Adafruit_PureIO-1.1.11.tar.gz", hash = "sha256:c4cfbb365731942d1f1092a116f47dfdae0aef18c5b27f1072b5824ad5ea8c7c", size = 35511, upload-time = "2023-05-25T19:01:34.654Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/19/9d/28e9d12f36e13c5f2acba3098187b0e931290ecd1d8df924391b5ad2db19/Adafruit_PureIO-1.1.11-py3-none-any.whl", hash = "sha256:281ab2099372cc0decc26326918996cbf21b8eed694ec4764d51eefa029d324e", size = 10678, upload-time = "2023-05-25T19:01:32.397Z" },
]
[[package]]
name = "attrs"
version = "25.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" },
]
[[package]]
name = "binho-host-adapter"
version = "0.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyserial" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/36/29b7b896e83e195fac6d64ccff95c0f24a18ee86e7437a22e60e0331d90a/binho-host-adapter-0.1.6.tar.gz", hash = "sha256:1e6da7a84e208c13b5f489066f05774bff1d593d0f5bf1ca149c2b8e83eae856", size = 10068, upload-time = "2020-06-04T19:38:11.789Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7b/6b/0f13486003aea3eb349c2946b7ec9753e7558b78e35d22c938062a96959c/binho_host_adapter-0.1.6-py3-none-any.whl", hash = "sha256:f71ca176c1e2fc1a5dce128beb286da217555c6c7c805f2ed282a6f3507ec277", size = 10540, upload-time = "2020-06-04T19:38:10.612Z" },
]
[[package]]
name = "black"
version = "25.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "mypy-extensions" },
{ name = "packaging" },
{ name = "pathspec" },
{ name = "platformdirs" },
{ name = "pytokens" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c4/d9/07b458a3f1c525ac392b5edc6b191ff140b596f9d77092429417a54e249d/black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7", size = 659264, upload-time = "2025-12-08T01:40:52.501Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/52/c551e36bc95495d2aa1a37d50566267aa47608c81a53f91daa809e03293f/black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5", size = 1923809, upload-time = "2025-12-08T01:46:55.126Z" },
{ url = "https://files.pythonhosted.org/packages/a0/f7/aac9b014140ee56d247e707af8db0aae2e9efc28d4a8aba92d0abd7ae9d1/black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f", size = 1742384, upload-time = "2025-12-08T01:49:37.022Z" },
{ url = "https://files.pythonhosted.org/packages/74/98/38aaa018b2ab06a863974c12b14a6266badc192b20603a81b738c47e902e/black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf", size = 1798761, upload-time = "2025-12-08T01:46:05.386Z" },
{ url = "https://files.pythonhosted.org/packages/16/3a/a8ac542125f61574a3f015b521ca83b47321ed19bb63fe6d7560f348bfe1/black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d", size = 1429180, upload-time = "2025-12-08T01:45:34.903Z" },
{ url = "https://files.pythonhosted.org/packages/e6/2d/bdc466a3db9145e946762d52cd55b1385509d9f9004fec1c97bdc8debbfb/black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce", size = 1239350, upload-time = "2025-12-08T01:46:09.458Z" },
{ url = "https://files.pythonhosted.org/packages/35/46/1d8f2542210c502e2ae1060b2e09e47af6a5e5963cb78e22ec1a11170b28/black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5", size = 1917015, upload-time = "2025-12-08T01:53:27.987Z" },
{ url = "https://files.pythonhosted.org/packages/41/37/68accadf977672beb8e2c64e080f568c74159c1aaa6414b4cd2aef2d7906/black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f", size = 1741830, upload-time = "2025-12-08T01:54:36.861Z" },
{ url = "https://files.pythonhosted.org/packages/ac/76/03608a9d8f0faad47a3af3a3c8c53af3367f6c0dd2d23a84710456c7ac56/black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f", size = 1791450, upload-time = "2025-12-08T01:44:52.581Z" },
{ url = "https://files.pythonhosted.org/packages/06/99/b2a4bd7dfaea7964974f947e1c76d6886d65fe5d24f687df2d85406b2609/black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83", size = 1452042, upload-time = "2025-12-08T01:46:13.188Z" },
{ url = "https://files.pythonhosted.org/packages/b2/7c/d9825de75ae5dd7795d007681b752275ea85a1c5d83269b4b9c754c2aaab/black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b", size = 1267446, upload-time = "2025-12-08T01:46:14.497Z" },
{ url = "https://files.pythonhosted.org/packages/68/11/21331aed19145a952ad28fca2756a1433ee9308079bd03bd898e903a2e53/black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828", size = 206191, upload-time = "2025-12-08T01:40:50.963Z" },
]
[[package]]
name = "cattrs"
version = "25.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6e/00/2432bb2d445b39b5407f0a90e01b9a271475eea7caf913d7a86bcb956385/cattrs-25.3.0.tar.gz", hash = "sha256:1ac88d9e5eda10436c4517e390a4142d88638fe682c436c93db7ce4a277b884a", size = 509321, upload-time = "2025-10-07T12:26:08.737Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d8/2b/a40e1488fdfa02d3f9a653a61a5935ea08b3c2225ee818db6a76c7ba9695/cattrs-25.3.0-py3-none-any.whl", hash = "sha256:9896e84e0a5bf723bc7b4b68f4481785367ce07a8a02e7e9ee6eb2819bc306ff", size = 70738, upload-time = "2025-10-07T12:26:06.603Z" },
]
[[package]]
name = "click"
version = "8.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "docstring-to-markdown"
version = "0.17"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/d8/8abe80d62c5dce1075578031bcfde07e735bcf0afe2886dd48b470162ab4/docstring_to_markdown-0.17.tar.gz", hash = "sha256:df72a112294c7492487c9da2451cae0faeee06e86008245c188c5761c9590ca3", size = 32260, upload-time = "2025-05-02T15:09:07.932Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/56/7b/af3d0da15bed3a8665419bb3a630585756920f4ad67abfdfef26240ebcc0/docstring_to_markdown-0.17-py3-none-any.whl", hash = "sha256:fd7d5094aa83943bf5f9e1a13701866b7c452eac19765380dead666e36d3711c", size = 23479, upload-time = "2025-05-02T15:09:06.676Z" },
]
[[package]]
name = "importlib-metadata"
version = "8.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" },
]
[[package]]
name = "jedi"
version = "0.19.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "parso" },
]
sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" },
]
[[package]]
name = "jedi-language-server"
version = "0.46.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cattrs" },
{ name = "docstring-to-markdown" },
{ name = "jedi" },
{ name = "lsprotocol" },
{ name = "pygls" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6e/63/2ae4466db0d5f723bb141990487a46f1add8f6449995172c7d45e6d37c66/jedi_language_server-0.46.0.tar.gz", hash = "sha256:b9ee7cce6cb2bd6fce11115906db41549d7e97b358e0818a23dca164ed0b961a", size = 34602, upload-time = "2025-10-25T20:45:40.647Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/06/c29fe15b67db3170e057376386a7643228b6bcea35df68e884475d8a7897/jedi_language_server-0.46.0-py3-none-any.whl", hash = "sha256:c8003c33a36de9998ac1c6e13ebe3d99b11d06046ae6307d75f54c73e9358ea7", size = 33769, upload-time = "2025-10-25T20:45:39.411Z" },
]
[[package]]
name = "lsprotocol"
version = "2025.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "cattrs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e9/26/67b84e6ec1402f0e6764ef3d2a0aaf9a79522cc1d37738f4e5bb0b21521a/lsprotocol-2025.0.0.tar.gz", hash = "sha256:e879da2b9301e82cfc3e60d805630487ac2f7ab17492f4f5ba5aaba94fe56c29", size = 74896, upload-time = "2025-06-17T21:30:18.156Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7b/f0/92f2d609d6642b5f30cb50a885d2bf1483301c69d5786286500d15651ef2/lsprotocol-2025.0.0-py3-none-any.whl", hash = "sha256:f9d78f25221f2a60eaa4a96d3b4ffae011b107537facee61d3da3313880995c7", size = 76250, upload-time = "2025-06-17T21:30:19.455Z" },
]
[[package]]
name = "mypy-extensions"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
]
[[package]]
name = "numpy"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/68/732d4b7811c00775f3bd522a21e8dd5a23f77eb11acdeb663e4a4ebf0ef4/numpy-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d797454e37570cfd61143b73b8debd623c3c0952959adb817dd310a483d58a1b", size = 16652495, upload-time = "2026-01-10T06:43:06.283Z" },
{ url = "https://files.pythonhosted.org/packages/20/ca/857722353421a27f1465652b2c66813eeeccea9d76d5f7b74b99f298e60e/numpy-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c55962006156aeef1629b953fd359064aa47e4d82cfc8e67f0918f7da3344f", size = 12368657, upload-time = "2026-01-10T06:43:09.094Z" },
{ url = "https://files.pythonhosted.org/packages/81/0d/2377c917513449cc6240031a79d30eb9a163d32a91e79e0da47c43f2c0c8/numpy-2.4.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:71abbea030f2cfc3092a0ff9f8c8fdefdc5e0bf7d9d9c99663538bb0ecdac0b9", size = 5197256, upload-time = "2026-01-10T06:43:13.634Z" },
{ url = "https://files.pythonhosted.org/packages/17/39/569452228de3f5de9064ac75137082c6214be1f5c532016549a7923ab4b5/numpy-2.4.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b55aa56165b17aaf15520beb9cbd33c9039810e0d9643dd4379e44294c7303e", size = 6545212, upload-time = "2026-01-10T06:43:15.661Z" },
{ url = "https://files.pythonhosted.org/packages/8c/a4/77333f4d1e4dac4395385482557aeecf4826e6ff517e32ca48e1dafbe42a/numpy-2.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0faba4a331195bfa96f93dd9dfaa10b2c7aa8cda3a02b7fd635e588fe821bf5", size = 14402871, upload-time = "2026-01-10T06:43:17.324Z" },
{ url = "https://files.pythonhosted.org/packages/ba/87/d341e519956273b39d8d47969dd1eaa1af740615394fe67d06f1efa68773/numpy-2.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e3087f53e2b4428766b54932644d148613c5a595150533ae7f00dab2f319a8", size = 16359305, upload-time = "2026-01-10T06:43:19.376Z" },
{ url = "https://files.pythonhosted.org/packages/32/91/789132c6666288eaa20ae8066bb99eba1939362e8f1a534949a215246e97/numpy-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:49e792ec351315e16da54b543db06ca8a86985ab682602d90c60ef4ff4db2a9c", size = 16181909, upload-time = "2026-01-10T06:43:21.808Z" },
{ url = "https://files.pythonhosted.org/packages/cf/b8/090b8bd27b82a844bb22ff8fdf7935cb1980b48d6e439ae116f53cdc2143/numpy-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e9e06c4c2379db47f3f6fc7a8652e7498251789bf8ff5bd43bf478ef314ca2", size = 18284380, upload-time = "2026-01-10T06:43:23.957Z" },
{ url = "https://files.pythonhosted.org/packages/67/78/722b62bd31842ff029412271556a1a27a98f45359dea78b1548a3a9996aa/numpy-2.4.1-cp313-cp313-win32.whl", hash = "sha256:3d1a100e48cb266090a031397863ff8a30050ceefd798f686ff92c67a486753d", size = 5957089, upload-time = "2026-01-10T06:43:27.535Z" },
{ url = "https://files.pythonhosted.org/packages/da/a6/cf32198b0b6e18d4fbfa9a21a992a7fca535b9bb2b0cdd217d4a3445b5ca/numpy-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:92a0e65272fd60bfa0d9278e0484c2f52fe03b97aedc02b357f33fe752c52ffb", size = 12307230, upload-time = "2026-01-10T06:43:29.298Z" },
{ url = "https://files.pythonhosted.org/packages/44/6c/534d692bfb7d0afe30611320c5fb713659dcb5104d7cc182aff2aea092f5/numpy-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:20d4649c773f66cc2fc36f663e091f57c3b7655f936a4c681b4250855d1da8f5", size = 10313125, upload-time = "2026-01-10T06:43:31.782Z" },
{ url = "https://files.pythonhosted.org/packages/da/a1/354583ac5c4caa566de6ddfbc42744409b515039e085fab6e0ff942e0df5/numpy-2.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f93bc6892fe7b0663e5ffa83b61aab510aacffd58c16e012bb9352d489d90cb7", size = 12496156, upload-time = "2026-01-10T06:43:34.237Z" },
{ url = "https://files.pythonhosted.org/packages/51/b0/42807c6e8cce58c00127b1dc24d365305189991f2a7917aa694a109c8d7d/numpy-2.4.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:178de8f87948163d98a4c9ab5bee4ce6519ca918926ec8df195af582de28544d", size = 5324663, upload-time = "2026-01-10T06:43:36.211Z" },
{ url = "https://files.pythonhosted.org/packages/fe/55/7a621694010d92375ed82f312b2f28017694ed784775269115323e37f5e2/numpy-2.4.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:98b35775e03ab7f868908b524fc0a84d38932d8daf7b7e1c3c3a1b6c7a2c9f15", size = 6645224, upload-time = "2026-01-10T06:43:37.884Z" },
{ url = "https://files.pythonhosted.org/packages/50/96/9fa8635ed9d7c847d87e30c834f7109fac5e88549d79ef3324ab5c20919f/numpy-2.4.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941c2a93313d030f219f3a71fd3d91a728b82979a5e8034eb2e60d394a2b83f9", size = 14462352, upload-time = "2026-01-10T06:43:39.479Z" },
{ url = "https://files.pythonhosted.org/packages/03/d1/8cf62d8bb2062da4fb82dd5d49e47c923f9c0738032f054e0a75342faba7/numpy-2.4.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529050522e983e00a6c1c6b67411083630de8b57f65e853d7b03d9281b8694d2", size = 16407279, upload-time = "2026-01-10T06:43:41.93Z" },
{ url = "https://files.pythonhosted.org/packages/86/1c/95c86e17c6b0b31ce6ef219da00f71113b220bcb14938c8d9a05cee0ff53/numpy-2.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2302dc0224c1cbc49bb94f7064f3f923a971bfae45c33870dcbff63a2a550505", size = 16248316, upload-time = "2026-01-10T06:43:44.121Z" },
{ url = "https://files.pythonhosted.org/packages/30/b4/e7f5ff8697274c9d0fa82398b6a372a27e5cef069b37df6355ccb1f1db1a/numpy-2.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9171a42fcad32dcf3fa86f0a4faa5e9f8facefdb276f54b8b390d90447cff4e2", size = 18329884, upload-time = "2026-01-10T06:43:46.613Z" },
{ url = "https://files.pythonhosted.org/packages/37/a4/b073f3e9d77f9aec8debe8ca7f9f6a09e888ad1ba7488f0c3b36a94c03ac/numpy-2.4.1-cp313-cp313t-win32.whl", hash = "sha256:382ad67d99ef49024f11d1ce5dcb5ad8432446e4246a4b014418ba3a1175a1f4", size = 6081138, upload-time = "2026-01-10T06:43:48.854Z" },
{ url = "https://files.pythonhosted.org/packages/16/16/af42337b53844e67752a092481ab869c0523bc95c4e5c98e4dac4e9581ac/numpy-2.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:62fea415f83ad8fdb6c20840578e5fbaf5ddd65e0ec6c3c47eda0f69da172510", size = 12447478, upload-time = "2026-01-10T06:43:50.476Z" },
{ url = "https://files.pythonhosted.org/packages/6c/f8/fa85b2eac68ec631d0b631abc448552cb17d39afd17ec53dcbcc3537681a/numpy-2.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a7870e8c5fc11aef57d6fea4b4085e537a3a60ad2cdd14322ed531fdca68d261", size = 10382981, upload-time = "2026-01-10T06:43:52.575Z" },
{ url = "https://files.pythonhosted.org/packages/1b/a7/ef08d25698e0e4b4efbad8d55251d20fe2a15f6d9aa7c9b30cd03c165e6f/numpy-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3869ea1ee1a1edc16c29bbe3a2f2a4e515cc3a44d43903ad41e0cacdbaf733dc", size = 16652046, upload-time = "2026-01-10T06:43:54.797Z" },
{ url = "https://files.pythonhosted.org/packages/8f/39/e378b3e3ca13477e5ac70293ec027c438d1927f18637e396fe90b1addd72/numpy-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e867df947d427cdd7a60e3e271729090b0f0df80f5f10ab7dd436f40811699c3", size = 12378858, upload-time = "2026-01-10T06:43:57.099Z" },
{ url = "https://files.pythonhosted.org/packages/c3/74/7ec6154f0006910ed1fdbb7591cf4432307033102b8a22041599935f8969/numpy-2.4.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e3bd2cb07841166420d2fa7146c96ce00cb3410664cbc1a6be028e456c4ee220", size = 5207417, upload-time = "2026-01-10T06:43:59.037Z" },
{ url = "https://files.pythonhosted.org/packages/f7/b7/053ac11820d84e42f8feea5cb81cc4fcd1091499b45b1ed8c7415b1bf831/numpy-2.4.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:f0a90aba7d521e6954670550e561a4cb925713bd944445dbe9e729b71f6cabee", size = 6542643, upload-time = "2026-01-10T06:44:01.852Z" },
{ url = "https://files.pythonhosted.org/packages/c0/c4/2e7908915c0e32ca636b92e4e4a3bdec4cb1e7eb0f8aedf1ed3c68a0d8cd/numpy-2.4.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d558123217a83b2d1ba316b986e9248a1ed1971ad495963d555ccd75dcb1556", size = 14418963, upload-time = "2026-01-10T06:44:04.047Z" },
{ url = "https://files.pythonhosted.org/packages/eb/c0/3ed5083d94e7ffd7c404e54619c088e11f2e1939a9544f5397f4adb1b8ba/numpy-2.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f44de05659b67d20499cbc96d49f2650769afcb398b79b324bb6e297bfe3844", size = 16363811, upload-time = "2026-01-10T06:44:06.207Z" },
{ url = "https://files.pythonhosted.org/packages/0e/68/42b66f1852bf525050a67315a4fb94586ab7e9eaa541b1bef530fab0c5dd/numpy-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:69e7419c9012c4aaf695109564e3387f1259f001b4326dfa55907b098af082d3", size = 16197643, upload-time = "2026-01-10T06:44:08.33Z" },
{ url = "https://files.pythonhosted.org/packages/d2/40/e8714fc933d85f82c6bfc7b998a0649ad9769a32f3494ba86598aaf18a48/numpy-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd257026eb1b34352e749d7cc1678b5eeec3e329ad8c9965a797e08ccba205", size = 18289601, upload-time = "2026-01-10T06:44:10.841Z" },
{ url = "https://files.pythonhosted.org/packages/80/9a/0d44b468cad50315127e884802351723daca7cf1c98d102929468c81d439/numpy-2.4.1-cp314-cp314-win32.whl", hash = "sha256:727c6c3275ddefa0dc078524a85e064c057b4f4e71ca5ca29a19163c607be745", size = 6005722, upload-time = "2026-01-10T06:44:13.332Z" },
{ url = "https://files.pythonhosted.org/packages/7e/bb/c6513edcce5a831810e2dddc0d3452ce84d208af92405a0c2e58fd8e7881/numpy-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:7d5d7999df434a038d75a748275cd6c0094b0ecdb0837342b332a82defc4dc4d", size = 12438590, upload-time = "2026-01-10T06:44:15.006Z" },
{ url = "https://files.pythonhosted.org/packages/e9/da/a598d5cb260780cf4d255102deba35c1d072dc028c4547832f45dd3323a8/numpy-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:ce9ce141a505053b3c7bce3216071f3bf5c182b8b28930f14cd24d43932cd2df", size = 10596180, upload-time = "2026-01-10T06:44:17.386Z" },
{ url = "https://files.pythonhosted.org/packages/de/bc/ea3f2c96fcb382311827231f911723aeff596364eb6e1b6d1d91128aa29b/numpy-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e53170557d37ae404bf8d542ca5b7c629d6efa1117dac6a83e394142ea0a43f", size = 12498774, upload-time = "2026-01-10T06:44:19.467Z" },
{ url = "https://files.pythonhosted.org/packages/aa/ab/ef9d939fe4a812648c7a712610b2ca6140b0853c5efea361301006c02ae5/numpy-2.4.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:a73044b752f5d34d4232f25f18160a1cc418ea4507f5f11e299d8ac36875f8a0", size = 5327274, upload-time = "2026-01-10T06:44:23.189Z" },
{ url = "https://files.pythonhosted.org/packages/bd/31/d381368e2a95c3b08b8cf7faac6004849e960f4a042d920337f71cef0cae/numpy-2.4.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:fb1461c99de4d040666ca0444057b06541e5642f800b71c56e6ea92d6a853a0c", size = 6648306, upload-time = "2026-01-10T06:44:25.012Z" },
{ url = "https://files.pythonhosted.org/packages/c8/e5/0989b44ade47430be6323d05c23207636d67d7362a1796ccbccac6773dd2/numpy-2.4.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423797bdab2eeefbe608d7c1ec7b2b4fd3c58d51460f1ee26c7500a1d9c9ee93", size = 14464653, upload-time = "2026-01-10T06:44:26.706Z" },
{ url = "https://files.pythonhosted.org/packages/10/a7/cfbe475c35371cae1358e61f20c5f075badc18c4797ab4354140e1d283cf/numpy-2.4.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52b5f61bdb323b566b528899cc7db2ba5d1015bda7ea811a8bcf3c89c331fa42", size = 16405144, upload-time = "2026-01-10T06:44:29.378Z" },
{ url = "https://files.pythonhosted.org/packages/f8/a3/0c63fe66b534888fa5177cc7cef061541064dbe2b4b60dcc60ffaf0d2157/numpy-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42d7dd5fa36d16d52a84f821eb96031836fd405ee6955dd732f2023724d0aa01", size = 16247425, upload-time = "2026-01-10T06:44:31.721Z" },
{ url = "https://files.pythonhosted.org/packages/6b/2b/55d980cfa2c93bd40ff4c290bf824d792bd41d2fe3487b07707559071760/numpy-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7b6b5e28bbd47b7532698e5db2fe1db693d84b58c254e4389d99a27bb9b8f6b", size = 18330053, upload-time = "2026-01-10T06:44:34.617Z" },
{ url = "https://files.pythonhosted.org/packages/23/12/8b5fc6b9c487a09a7957188e0943c9ff08432c65e34567cabc1623b03a51/numpy-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:5de60946f14ebe15e713a6f22850c2372fa72f4ff9a432ab44aa90edcadaa65a", size = 6152482, upload-time = "2026-01-10T06:44:36.798Z" },
{ url = "https://files.pythonhosted.org/packages/00/a5/9f8ca5856b8940492fc24fbe13c1bc34d65ddf4079097cf9e53164d094e1/numpy-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f085da926c0d491ffff3096f91078cc97ea67e7e6b65e490bc8dcda65663be2", size = 12627117, upload-time = "2026-01-10T06:44:38.828Z" },
{ url = "https://files.pythonhosted.org/packages/ad/0d/eca3d962f9eef265f01a8e0d20085c6dd1f443cbffc11b6dede81fd82356/numpy-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6436cffb4f2bf26c974344439439c95e152c9a527013f26b3577be6c2ca64295", size = 10667121, upload-time = "2026-01-10T06:44:41.644Z" },
]
[[package]]
name = "packaging"
version = "25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "parso"
version = "0.8.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" },
]
[[package]]
name = "pathspec"
version = "0.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
]
[[package]]
name = "platformdirs"
version = "4.5.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
]
[[package]]
name = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "pyftdi"
version = "0.57.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyserial" },
{ name = "pyusb" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/16/cd/0731490946e037e954ef83719f07c7672cf32bc90dd9c75201c40b827664/pyftdi-0.57.1-py3-none-any.whl", hash = "sha256:efd3f5a7d43202dc883ff261a7b1cb4dcbbe65b19628f8603a8b1183a7bc2841", size = 146180, upload-time = "2025-08-14T15:59:16.164Z" },
]
[[package]]
name = "pygls"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "cattrs" },
{ name = "lsprotocol" },
]
sdist = { url = "https://files.pythonhosted.org/packages/87/50/2bfc32f3acbc8941042919b59c9f592291127b55d7331b72e67ce7b62f08/pygls-2.0.0.tar.gz", hash = "sha256:99accd03de1ca76fe1e7e317f0968ebccf7b9955afed6e2e3e188606a20b4f07", size = 55796, upload-time = "2025-10-17T19:22:47.925Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/09/14feafc13bebb9c85b29b374889c1549d3700cb572f2d43a1bb940d70315/pygls-2.0.0-py3-none-any.whl", hash = "sha256:b4e54bba806f76781017ded8fd07463b98670f959042c44170cd362088b200cc", size = 69533, upload-time = "2025-10-17T19:22:46.63Z" },
]
[[package]]
name = "pyserial"
version = "3.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
]
[[package]]
name = "python-lsp-jsonrpc"
version = "1.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ujson" },
]
sdist = { url = "https://files.pythonhosted.org/packages/48/b6/fd92e2ea4635d88966bb42c20198df1a981340f07843b5e3c6694ba3557b/python-lsp-jsonrpc-1.1.2.tar.gz", hash = "sha256:4688e453eef55cd952bff762c705cedefa12055c0aec17a06f595bcc002cc912", size = 15298, upload-time = "2023-09-23T17:48:30.451Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/d9/656659d5b5d5f402b2b174cd0ba9bc827e07ce3c0bf88da65424baf64af8/python_lsp_jsonrpc-1.1.2-py3-none-any.whl", hash = "sha256:7339c2e9630ae98903fdaea1ace8c47fba0484983794d6aafd0bd8989be2b03c", size = 8805, upload-time = "2023-09-23T17:48:28.804Z" },
]
[[package]]
name = "python-lsp-server"
version = "1.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "black" },
{ name = "docstring-to-markdown" },
{ name = "jedi" },
{ name = "pluggy" },
{ name = "python-lsp-jsonrpc" },
{ name = "ujson" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b4/b5/b989d41c63390dfc2bf63275ab543b82fed076723d912055e77ccbae1422/python_lsp_server-1.14.0.tar.gz", hash = "sha256:509c445fc667f41ffd3191cb7512a497bf7dd76c14ceb1ee2f6c13ebe71f9a6b", size = 121536, upload-time = "2025-12-06T16:12:20.86Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/cf/587f913335e3855e0ddca2aee7c3f9d5de2d75a1e23434891e9f74783bcd/python_lsp_server-1.14.0-py3-none-any.whl", hash = "sha256:a71a917464effc48f4c70363f90b8520e5e3ba8201428da80b97a7ceb259e32a", size = 77060, upload-time = "2025-12-06T16:12:19.46Z" },
]
[[package]]
name = "pytokens"
version = "0.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" },
]
[[package]]
name = "pyusb"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/00/6b/ce3727395e52b7b76dfcf0c665e37d223b680b9becc60710d4bc08b7b7cb/pyusb-1.3.1.tar.gz", hash = "sha256:3af070b607467c1c164f49d5b0caabe8ac78dbed9298d703a8dbf9df4052d17e", size = 77281, upload-time = "2025-01-08T23:45:01.866Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/28/b8/27e6312e86408a44fe16bd28ee12dd98608b39f7e7e57884a24e8f29b573/pyusb-1.3.1-py3-none-any.whl", hash = "sha256:bf9b754557af4717fe80c2b07cc2b923a9151f5c08d17bdb5345dac09d6a0430", size = 58465, upload-time = "2025-01-08T23:45:00.029Z" },
]
[[package]]
name = "rpi-gpio"
version = "0.7.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c4/0f/10b524a12b3445af1c607c27b2f5ed122ef55756e29942900e5c950735f2/RPi.GPIO-0.7.1.tar.gz", hash = "sha256:cd61c4b03c37b62bba4a5acfea9862749c33c618e0295e7e90aa4713fb373b70", size = 29090, upload-time = "2022-02-06T15:15:06.022Z" }
[[package]]
name = "ruff"
version = "0.14.9"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f6/1b/ab712a9d5044435be8e9a2beb17cbfa4c241aa9b5e4413febac2a8b79ef2/ruff-0.14.9.tar.gz", hash = "sha256:35f85b25dd586381c0cc053f48826109384c81c00ad7ef1bd977bfcc28119d5b", size = 5809165, upload-time = "2025-12-11T21:39:47.381Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/1c/d1b1bba22cffec02351c78ab9ed4f7d7391876e12720298448b29b7229c1/ruff-0.14.9-py3-none-linux_armv6l.whl", hash = "sha256:f1ec5de1ce150ca6e43691f4a9ef5c04574ad9ca35c8b3b0e18877314aba7e75", size = 13576541, upload-time = "2025-12-11T21:39:14.806Z" },
{ url = "https://files.pythonhosted.org/packages/94/ab/ffe580e6ea1fca67f6337b0af59fc7e683344a43642d2d55d251ff83ceae/ruff-0.14.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ed9d7417a299fc6030b4f26333bf1117ed82a61ea91238558c0268c14e00d0c2", size = 13779363, upload-time = "2025-12-11T21:39:20.29Z" },
{ url = "https://files.pythonhosted.org/packages/7d/f8/2be49047f929d6965401855461e697ab185e1a6a683d914c5c19c7962d9e/ruff-0.14.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d5dc3473c3f0e4a1008d0ef1d75cee24a48e254c8bed3a7afdd2b4392657ed2c", size = 12925292, upload-time = "2025-12-11T21:39:38.757Z" },
{ url = "https://files.pythonhosted.org/packages/9e/e9/08840ff5127916bb989c86f18924fd568938b06f58b60e206176f327c0fe/ruff-0.14.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84bf7c698fc8f3cb8278830fb6b5a47f9bcc1ed8cb4f689b9dd02698fa840697", size = 13362894, upload-time = "2025-12-11T21:39:02.524Z" },
{ url = "https://files.pythonhosted.org/packages/31/1c/5b4e8e7750613ef43390bb58658eaf1d862c0cc3352d139cd718a2cea164/ruff-0.14.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa733093d1f9d88a5d98988d8834ef5d6f9828d03743bf5e338bf980a19fce27", size = 13311482, upload-time = "2025-12-11T21:39:17.51Z" },
{ url = "https://files.pythonhosted.org/packages/5b/3a/459dce7a8cb35ba1ea3e9c88f19077667a7977234f3b5ab197fad240b404/ruff-0.14.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a1cfb04eda979b20c8c19550c8b5f498df64ff8da151283311ce3199e8b3648", size = 14016100, upload-time = "2025-12-11T21:39:41.948Z" },
{ url = "https://files.pythonhosted.org/packages/a6/31/f064f4ec32524f9956a0890fc6a944e5cf06c63c554e39957d208c0ffc45/ruff-0.14.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1e5cb521e5ccf0008bd74d5595a4580313844a42b9103b7388eca5a12c970743", size = 15477729, upload-time = "2025-12-11T21:39:23.279Z" },
{ url = "https://files.pythonhosted.org/packages/7a/6d/f364252aad36ccd443494bc5f02e41bf677f964b58902a17c0b16c53d890/ruff-0.14.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd429a8926be6bba4befa8cdcf3f4dd2591c413ea5066b1e99155ed245ae42bb", size = 15122386, upload-time = "2025-12-11T21:39:33.125Z" },
{ url = "https://files.pythonhosted.org/packages/20/02/e848787912d16209aba2799a4d5a1775660b6a3d0ab3944a4ccc13e64a02/ruff-0.14.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab208c1b7a492e37caeaf290b1378148f75e13c2225af5d44628b95fd7834273", size = 14497124, upload-time = "2025-12-11T21:38:59.33Z" },
{ url = "https://files.pythonhosted.org/packages/f3/51/0489a6a5595b7760b5dbac0dd82852b510326e7d88d51dbffcd2e07e3ff3/ruff-0.14.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72034534e5b11e8a593f517b2f2f2b273eb68a30978c6a2d40473ad0aaa4cb4a", size = 14195343, upload-time = "2025-12-11T21:39:44.866Z" },
{ url = "https://files.pythonhosted.org/packages/f6/53/3bb8d2fa73e4c2f80acc65213ee0830fa0c49c6479313f7a68a00f39e208/ruff-0.14.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:712ff04f44663f1b90a1195f51525836e3413c8a773574a7b7775554269c30ed", size = 14346425, upload-time = "2025-12-11T21:39:05.927Z" },
{ url = "https://files.pythonhosted.org/packages/ad/04/bdb1d0ab876372da3e983896481760867fc84f969c5c09d428e8f01b557f/ruff-0.14.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a111fee1db6f1d5d5810245295527cda1d367c5aa8f42e0fca9a78ede9b4498b", size = 13258768, upload-time = "2025-12-11T21:39:08.691Z" },
{ url = "https://files.pythonhosted.org/packages/40/d9/8bf8e1e41a311afd2abc8ad12be1b6c6c8b925506d9069b67bb5e9a04af3/ruff-0.14.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8769efc71558fecc25eb295ddec7d1030d41a51e9dcf127cbd63ec517f22d567", size = 13326939, upload-time = "2025-12-11T21:39:53.842Z" },
{ url = "https://files.pythonhosted.org/packages/f4/56/a213fa9edb6dd849f1cfbc236206ead10913693c72a67fb7ddc1833bf95d/ruff-0.14.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:347e3bf16197e8a2de17940cd75fd6491e25c0aa7edf7d61aa03f146a1aa885a", size = 13578888, upload-time = "2025-12-11T21:39:35.988Z" },
{ url = "https://files.pythonhosted.org/packages/33/09/6a4a67ffa4abae6bf44c972a4521337ffce9cbc7808faadede754ef7a79c/ruff-0.14.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7715d14e5bccf5b660f54516558aa94781d3eb0838f8e706fb60e3ff6eff03a8", size = 14314473, upload-time = "2025-12-11T21:39:50.78Z" },
{ url = "https://files.pythonhosted.org/packages/12/0d/15cc82da5d83f27a3c6b04f3a232d61bc8c50d38a6cd8da79228e5f8b8d6/ruff-0.14.9-py3-none-win32.whl", hash = "sha256:df0937f30aaabe83da172adaf8937003ff28172f59ca9f17883b4213783df197", size = 13202651, upload-time = "2025-12-11T21:39:26.628Z" },
{ url = "https://files.pythonhosted.org/packages/32/f7/c78b060388eefe0304d9d42e68fab8cffd049128ec466456cef9b8d4f06f/ruff-0.14.9-py3-none-win_amd64.whl", hash = "sha256:c0b53a10e61df15a42ed711ec0bda0c582039cf6c754c49c020084c55b5b0bc2", size = 14702079, upload-time = "2025-12-11T21:39:11.954Z" },
{ url = "https://files.pythonhosted.org/packages/26/09/7a9520315decd2334afa65ed258fed438f070e31f05a2e43dd480a5e5911/ruff-0.14.9-py3-none-win_arm64.whl", hash = "sha256:8e821c366517a074046d92f0e9213ed1c13dbc5b37a7fc20b07f79b64d62cc84", size = 13744730, upload-time = "2025-12-11T21:39:29.659Z" },
]
[[package]]
name = "solarmotor"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "adafruit-blinka" },
{ name = "adafruit-circuitpython-busdevice" },
{ name = "adafruit-circuitpython-connectionmanager" },
{ name = "adafruit-circuitpython-motor" },
{ name = "adafruit-circuitpython-pca9685" },
{ name = "adafruit-circuitpython-register" },
{ name = "adafruit-circuitpython-requests" },
{ name = "adafruit-circuitpython-servokit" },
{ name = "adafruit-circuitpython-typing" },
{ name = "adafruit-platformdetect" },
{ name = "adafruit-pureio" },
{ name = "attrs" },
{ name = "binho-host-adapter" },
{ name = "black" },
{ name = "cattrs" },
{ name = "click" },
{ name = "docstring-to-markdown" },
{ name = "importlib-metadata" },
{ name = "jedi" },
{ name = "jedi-language-server" },
{ name = "lsprotocol" },
{ name = "mypy-extensions" },
{ name = "numpy" },
{ name = "packaging" },
{ name = "parso" },
{ name = "pathspec" },
{ name = "platformdirs" },
{ name = "pluggy" },
{ name = "pyftdi" },
{ name = "pygls" },
{ name = "pyserial" },
{ name = "python-lsp-jsonrpc" },
{ name = "python-lsp-server" },
{ name = "pytokens" },
{ name = "pyusb" },
{ name = "rpi-gpio" },
{ name = "ruff" },
{ name = "sysv-ipc" },
{ name = "typing-extensions" },
{ name = "ujson" },
{ name = "zipp" },
]
[package.metadata]
requires-dist = [
{ name = "adafruit-blinka", specifier = "==8.66.2" },
{ name = "adafruit-circuitpython-busdevice", specifier = "==5.2.14" },
{ name = "adafruit-circuitpython-connectionmanager", specifier = "==3.1.6" },
{ name = "adafruit-circuitpython-motor", specifier = "==3.4.18" },
{ name = "adafruit-circuitpython-pca9685", specifier = "==3.4.20" },
{ name = "adafruit-circuitpython-register", specifier = "==1.11.1" },
{ name = "adafruit-circuitpython-requests", specifier = "==4.1.14" },
{ name = "adafruit-circuitpython-servokit", specifier = "==1.3.22" },
{ name = "adafruit-circuitpython-typing", specifier = "==1.12.2" },
{ name = "adafruit-platformdetect", specifier = "==3.84.1" },
{ name = "adafruit-pureio", specifier = "==1.1.11" },
{ name = "attrs", specifier = "==25.4.0" },
{ name = "binho-host-adapter", specifier = "==0.1.6" },
{ name = "black", specifier = "==25.12.0" },
{ name = "cattrs", specifier = "==25.3.0" },
{ name = "click", specifier = "==8.3.1" },
{ name = "docstring-to-markdown", specifier = "==0.17" },
{ name = "importlib-metadata", specifier = "==8.7.0" },
{ name = "jedi", specifier = "==0.19.2" },
{ name = "jedi-language-server", specifier = "==0.46.0" },
{ name = "lsprotocol", specifier = "==2025.0.0" },
{ name = "mypy-extensions", specifier = "==1.1.0" },
{ name = "numpy", specifier = ">=2.4.1" },
{ name = "packaging", specifier = "==25.0" },
{ name = "parso", specifier = "==0.8.5" },
{ name = "pathspec", specifier = "==0.12.1" },
{ name = "platformdirs", specifier = "==4.5.1" },
{ name = "pluggy", specifier = "==1.6.0" },
{ name = "pyftdi", specifier = "==0.57.1" },
{ name = "pygls", specifier = "==2.0.0" },
{ name = "pyserial", specifier = "==3.5" },
{ name = "python-lsp-jsonrpc", specifier = "==1.1.2" },
{ name = "python-lsp-server", specifier = "==1.14.0" },
{ name = "pytokens", specifier = "==0.3.0" },
{ name = "pyusb", specifier = "==1.3.1" },
{ name = "rpi-gpio", specifier = "==0.7.1" },
{ name = "ruff", specifier = ">=0.14.9" },
{ name = "sysv-ipc", specifier = "==1.1.0" },
{ name = "typing-extensions", specifier = "==4.15.0" },
{ name = "ujson", specifier = "==5.11.0" },
{ name = "zipp", specifier = "==3.23.0" },
]
[[package]]
name = "sysv-ipc"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0c/d7/5d2f861155e9749f981e6c58f2a482d3ab458bf8c35ae24d4b4d5899ebf9/sysv_ipc-1.1.0.tar.gz", hash = "sha256:0f063cbd36ec232032e425769ebc871f195a7d183b9af32f9901589ea7129ac3", size = 99448, upload-time = "2021-01-17T19:07:44.217Z" }
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "ujson"
version = "5.11.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/d9/3f17e3c5773fb4941c68d9a37a47b1a79c9649d6c56aefbed87cc409d18a/ujson-5.11.0.tar.gz", hash = "sha256:e204ae6f909f099ba6b6b942131cee359ddda2b6e4ea39c12eb8b991fe2010e0", size = 7156583, upload-time = "2025-08-20T11:57:02.452Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1c/ec/2de9dd371d52c377abc05d2b725645326c4562fc87296a8907c7bcdf2db7/ujson-5.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:109f59885041b14ee9569bf0bb3f98579c3fa0652317b355669939e5fc5ede53", size = 55435, upload-time = "2025-08-20T11:55:50.243Z" },
{ url = "https://files.pythonhosted.org/packages/5b/a4/f611f816eac3a581d8a4372f6967c3ed41eddbae4008d1d77f223f1a4e0a/ujson-5.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a31c6b8004438e8c20fc55ac1c0e07dad42941db24176fe9acf2815971f8e752", size = 53193, upload-time = "2025-08-20T11:55:51.373Z" },
{ url = "https://files.pythonhosted.org/packages/e9/c5/c161940967184de96f5cbbbcce45b562a4bf851d60f4c677704b1770136d/ujson-5.11.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c684fb21255b9b90320ba7e199780f653e03f6c2528663768965f4126a5b50", size = 57603, upload-time = "2025-08-20T11:55:52.583Z" },
{ url = "https://files.pythonhosted.org/packages/2b/d6/c7b2444238f5b2e2d0e3dab300b9ddc3606e4b1f0e4bed5a48157cebc792/ujson-5.11.0-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:4c9f5d6a27d035dd90a146f7761c2272cf7103de5127c9ab9c4cd39ea61e878a", size = 59794, upload-time = "2025-08-20T11:55:53.69Z" },
{ url = "https://files.pythonhosted.org/packages/fe/a3/292551f936d3d02d9af148f53e1bc04306b00a7cf1fcbb86fa0d1c887242/ujson-5.11.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:837da4d27fed5fdc1b630bd18f519744b23a0b5ada1bbde1a36ba463f2900c03", size = 57363, upload-time = "2025-08-20T11:55:54.843Z" },
{ url = "https://files.pythonhosted.org/packages/90/a6/82cfa70448831b1a9e73f882225980b5c689bf539ec6400b31656a60ea46/ujson-5.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:787aff4a84da301b7f3bac09bc696e2e5670df829c6f8ecf39916b4e7e24e701", size = 1036311, upload-time = "2025-08-20T11:55:56.197Z" },
{ url = "https://files.pythonhosted.org/packages/84/5c/96e2266be50f21e9b27acaee8ca8f23ea0b85cb998c33d4f53147687839b/ujson-5.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6dd703c3e86dc6f7044c5ac0b3ae079ed96bf297974598116aa5fb7f655c3a60", size = 1195783, upload-time = "2025-08-20T11:55:58.081Z" },
{ url = "https://files.pythonhosted.org/packages/8d/20/78abe3d808cf3bb3e76f71fca46cd208317bf461c905d79f0d26b9df20f1/ujson-5.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3772e4fe6b0c1e025ba3c50841a0ca4786825a4894c8411bf8d3afe3a8061328", size = 1088822, upload-time = "2025-08-20T11:55:59.469Z" },
{ url = "https://files.pythonhosted.org/packages/d8/50/8856e24bec5e2fc7f775d867aeb7a3f137359356200ac44658f1f2c834b2/ujson-5.11.0-cp313-cp313-win32.whl", hash = "sha256:8fa2af7c1459204b7a42e98263b069bd535ea0cd978b4d6982f35af5a04a4241", size = 39753, upload-time = "2025-08-20T11:56:01.345Z" },
{ url = "https://files.pythonhosted.org/packages/5b/d8/1baee0f4179a4d0f5ce086832147b6cc9b7731c24ca08e14a3fdb8d39c32/ujson-5.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:34032aeca4510a7c7102bd5933f59a37f63891f30a0706fb46487ab6f0edf8f0", size = 43866, upload-time = "2025-08-20T11:56:02.552Z" },
{ url = "https://files.pythonhosted.org/packages/a9/8c/6d85ef5be82c6d66adced3ec5ef23353ed710a11f70b0b6a836878396334/ujson-5.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:ce076f2df2e1aa62b685086fbad67f2b1d3048369664b4cdccc50707325401f9", size = 38363, upload-time = "2025-08-20T11:56:03.688Z" },
{ url = "https://files.pythonhosted.org/packages/28/08/4518146f4984d112764b1dfa6fb7bad691c44a401adadaa5e23ccd930053/ujson-5.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65724738c73645db88f70ba1f2e6fb678f913281804d5da2fd02c8c5839af302", size = 55462, upload-time = "2025-08-20T11:56:04.873Z" },
{ url = "https://files.pythonhosted.org/packages/29/37/2107b9a62168867a692654d8766b81bd2fd1e1ba13e2ec90555861e02b0c/ujson-5.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29113c003ca33ab71b1b480bde952fbab2a0b6b03a4ee4c3d71687cdcbd1a29d", size = 53246, upload-time = "2025-08-20T11:56:06.054Z" },
{ url = "https://files.pythonhosted.org/packages/9b/f8/25583c70f83788edbe3ca62ce6c1b79eff465d78dec5eb2b2b56b3e98b33/ujson-5.11.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c44c703842024d796b4c78542a6fcd5c3cb948b9fc2a73ee65b9c86a22ee3638", size = 57631, upload-time = "2025-08-20T11:56:07.374Z" },
{ url = "https://files.pythonhosted.org/packages/ed/ca/19b3a632933a09d696f10dc1b0dfa1d692e65ad507d12340116ce4f67967/ujson-5.11.0-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:e750c436fb90edf85585f5c62a35b35082502383840962c6983403d1bd96a02c", size = 59877, upload-time = "2025-08-20T11:56:08.534Z" },
{ url = "https://files.pythonhosted.org/packages/55/7a/4572af5324ad4b2bfdd2321e898a527050290147b4ea337a79a0e4e87ec7/ujson-5.11.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f278b31a7c52eb0947b2db55a5133fbc46b6f0ef49972cd1a80843b72e135aba", size = 57363, upload-time = "2025-08-20T11:56:09.758Z" },
{ url = "https://files.pythonhosted.org/packages/7b/71/a2b8c19cf4e1efe53cf439cdf7198ac60ae15471d2f1040b490c1f0f831f/ujson-5.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ab2cb8351d976e788669c8281465d44d4e94413718af497b4e7342d7b2f78018", size = 1036394, upload-time = "2025-08-20T11:56:11.168Z" },
{ url = "https://files.pythonhosted.org/packages/7a/3e/7b98668cba3bb3735929c31b999b374ebc02c19dfa98dfebaeeb5c8597ca/ujson-5.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:090b4d11b380ae25453100b722d0609d5051ffe98f80ec52853ccf8249dfd840", size = 1195837, upload-time = "2025-08-20T11:56:12.6Z" },
{ url = "https://files.pythonhosted.org/packages/a1/ea/8870f208c20b43571a5c409ebb2fe9b9dba5f494e9e60f9314ac01ea8f78/ujson-5.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:80017e870d882d5517d28995b62e4e518a894f932f1e242cbc802a2fd64d365c", size = 1088837, upload-time = "2025-08-20T11:56:14.15Z" },
{ url = "https://files.pythonhosted.org/packages/63/b6/c0e6607e37fa47929920a685a968c6b990a802dec65e9c5181e97845985d/ujson-5.11.0-cp314-cp314-win32.whl", hash = "sha256:1d663b96eb34c93392e9caae19c099ec4133ba21654b081956613327f0e973ac", size = 41022, upload-time = "2025-08-20T11:56:15.509Z" },
{ url = "https://files.pythonhosted.org/packages/4e/56/f4fe86b4c9000affd63e9219e59b222dc48b01c534533093e798bf617a7e/ujson-5.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:849e65b696f0d242833f1df4182096cedc50d414215d1371fca85c541fbff629", size = 45111, upload-time = "2025-08-20T11:56:16.597Z" },
{ url = "https://files.pythonhosted.org/packages/0a/f3/669437f0280308db4783b12a6d88c00730b394327d8334cc7a32ef218e64/ujson-5.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:e73df8648c9470af2b6a6bf5250d4744ad2cf3d774dcf8c6e31f018bdd04d764", size = 39682, upload-time = "2025-08-20T11:56:17.763Z" },
{ url = "https://files.pythonhosted.org/packages/6e/cd/e9809b064a89fe5c4184649adeb13c1b98652db3f8518980b04227358574/ujson-5.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:de6e88f62796372fba1de973c11138f197d3e0e1d80bcb2b8aae1e826096d433", size = 55759, upload-time = "2025-08-20T11:56:18.882Z" },
{ url = "https://files.pythonhosted.org/packages/1b/be/ae26a6321179ebbb3a2e2685b9007c71bcda41ad7a77bbbe164005e956fc/ujson-5.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e56ef8066f11b80d620985ae36869a3ff7e4b74c3b6129182ec5d1df0255f3", size = 53634, upload-time = "2025-08-20T11:56:20.012Z" },
{ url = "https://files.pythonhosted.org/packages/ae/e9/fb4a220ee6939db099f4cfeeae796ecb91e7584ad4d445d4ca7f994a9135/ujson-5.11.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a325fd2c3a056cf6c8e023f74a0c478dd282a93141356ae7f16d5309f5ff823", size = 58547, upload-time = "2025-08-20T11:56:21.175Z" },
{ url = "https://files.pythonhosted.org/packages/bd/f8/fc4b952b8f5fea09ea3397a0bd0ad019e474b204cabcb947cead5d4d1ffc/ujson-5.11.0-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:a0af6574fc1d9d53f4ff371f58c96673e6d988ed2b5bf666a6143c782fa007e9", size = 60489, upload-time = "2025-08-20T11:56:22.342Z" },
{ url = "https://files.pythonhosted.org/packages/2e/e5/af5491dfda4f8b77e24cf3da68ee0d1552f99a13e5c622f4cef1380925c3/ujson-5.11.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10f29e71ecf4ecd93a6610bd8efa8e7b6467454a363c3d6416db65de883eb076", size = 58035, upload-time = "2025-08-20T11:56:23.92Z" },
{ url = "https://files.pythonhosted.org/packages/c4/09/0945349dd41f25cc8c38d78ace49f14c5052c5bbb7257d2f466fa7bdb533/ujson-5.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a0a9b76a89827a592656fe12e000cf4f12da9692f51a841a4a07aa4c7ecc41c", size = 1037212, upload-time = "2025-08-20T11:56:25.274Z" },
{ url = "https://files.pythonhosted.org/packages/49/44/8e04496acb3d5a1cbee3a54828d9652f67a37523efa3d3b18a347339680a/ujson-5.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b16930f6a0753cdc7d637b33b4e8f10d5e351e1fb83872ba6375f1e87be39746", size = 1196500, upload-time = "2025-08-20T11:56:27.517Z" },
{ url = "https://files.pythonhosted.org/packages/64/ae/4bc825860d679a0f208a19af2f39206dfd804ace2403330fdc3170334a2f/ujson-5.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04c41afc195fd477a59db3a84d5b83a871bd648ef371cf8c6f43072d89144eef", size = 1089487, upload-time = "2025-08-20T11:56:29.07Z" },
{ url = "https://files.pythonhosted.org/packages/30/ed/5a057199fb0a5deabe0957073a1c1c1c02a3e99476cd03daee98ea21fa57/ujson-5.11.0-cp314-cp314t-win32.whl", hash = "sha256:aa6d7a5e09217ff93234e050e3e380da62b084e26b9f2e277d2606406a2fc2e5", size = 41859, upload-time = "2025-08-20T11:56:30.495Z" },
{ url = "https://files.pythonhosted.org/packages/aa/03/b19c6176bdf1dc13ed84b886e99677a52764861b6cc023d5e7b6ebda249d/ujson-5.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:48055e1061c1bb1f79e75b4ac39e821f3f35a9b82de17fce92c3140149009bec", size = 46183, upload-time = "2025-08-20T11:56:31.574Z" },
{ url = "https://files.pythonhosted.org/packages/5d/ca/a0413a3874b2dc1708b8796ca895bf363292f9c70b2e8ca482b7dbc0259d/ujson-5.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1194b943e951092db611011cb8dbdb6cf94a3b816ed07906e14d3bc6ce0e90ab", size = 40264, upload-time = "2025-08-20T11:56:32.773Z" },
]
[[package]]
name = "zipp"
version = "3.23.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
]