Skip to content

Conversation

@Daniel0813
Copy link
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings November 15, 2025 00:51
@sgbaird sgbaird mentioned this pull request Nov 15, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds Python scripts for controlling a desk lifter via Raspberry Pi GPIO pins, including test scripts, calibration utilities, and height positioning functionality.

Key Changes:

  • Added GPIO control scripts for desk up/down movement
  • Implemented calibration-based height positioning logic
  • Documented calibration measurements and movement rates

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
scripts/test_down.py Test script for verifying DOWN button control via GPIO pin 27
scripts/reset_to_lowest.py Utility to reset desk to lowest position using 56-second calibrated duration
scripts/move_to_height.py Main positioning script that resets to lowest then moves to target height based on calibration
scripts/lifter_calibration.txt Documents measured calibration data (height range and movement rates)

Highest height: 54.5 inches

Down movement rate: 0.55 inches/second (measured from 54.5" to 49" in 10s)
Up movement rate: 0.55 inches/second (measured from 23.7" to 29.1" in 10s)
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent calibration data documentation: Line 7 states "Up movement rate: 0.55 inches/second" but move_to_height.py uses UP_RATE = 0.54. Additionally, line 6 states "Down movement rate: 0.55 inches/second" but this constant is not defined in move_to_height.py where it's used (line 34 hardcodes 56.0 seconds). Ensure calibration values are consistent across all files.

Suggested change
Up movement rate: 0.55 inches/second (measured from 23.7" to 29.1" in 10s)
Up movement rate: 0.54 inches/second (measured from 23.7" to 29.1" in 10s)

Copilot uses AI. Check for mistakes.
Comment on lines 48 to 55
if __name__ == "__main__":
try:
target = float(input(f"Enter target height in inches ({LOWEST_HEIGHT}-{HIGHEST_HEIGHT}): "))
move_to_height(target)
finally:
release_up()
release_down()
GPIO.cleanup()
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Script violates custom guideline against if __name__ == "__main__" patterns. According to the project's coding guidelines, this should be avoided unless explicitly requested. The code should be left as a top-level script instead.

Copilot generated this review using guidance from repository custom instructions.
# Calibration data
LOWEST_HEIGHT = 23.7 # inches
HIGHEST_HEIGHT = 54.5 # inches
UP_RATE = 0.54 # inches per second (from calibration)
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent calibration data: The calibration file shows Up movement rate: 0.55 inches/second but this script uses UP_RATE = 0.54. This mismatch will cause incorrect positioning calculations.

Suggested change
UP_RATE = 0.54 # inches per second (from calibration)
UP_RATE = 0.55 # inches per second (from calibration)

Copilot uses AI. Check for mistakes.

def move_to_height(target_height):
if not (LOWEST_HEIGHT <= target_height <= HIGHEST_HEIGHT):
raise ValueError(f"Target height {target_height}'' is out of range.")
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message incorrectly displays the target height using double-quote notation ({target_height}'') instead of inch notation ({target_height}"). The double single-quote is confusing and non-standard.

Suggested change
raise ValueError(f"Target height {target_height}'' is out of range.")
raise ValueError(f'Target height {target_height}" is out of range.')

Copilot uses AI. Check for mistakes.
Comment on lines 28 to 50
raise ValueError(f"Target height {target_height}'' is out of range.")
# Step 1: Reset to lowest
print(f"Resetting to lowest position ({LOWEST_HEIGHT})...")
release_down()
input("Press Enter to move all the way DOWN (about 56 seconds from max height)...")
press_down()
time.sleep(56.0)
release_down()
print("At lowest position.")
# Step 2: Move up for calculated time
delta = target_height - LOWEST_HEIGHT
up_time = delta / UP_RATE
print(f"Moving UP for {up_time:.1f} seconds to reach {target_height}''...")
release_up()
input("Press Enter to move UP...")
press_up()
time.sleep(up_time)
release_up()
print(f"Arrived at {target_height}'' (approximate).")

if __name__ == "__main__":
try:
target = float(input(f"Enter target height in inches ({LOWEST_HEIGHT}-{HIGHEST_HEIGHT}): "))
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent inch notation throughout the function: Lines 28, 30, 40, 46, and 50 use different notations for inches (double single-quotes '', no notation, or inconsistent formatting). Standardize to use the proper inch symbol (") throughout for clarity and consistency.

Copilot uses AI. Check for mistakes.
# Step 1: Reset to lowest
print(f"Resetting to lowest position ({LOWEST_HEIGHT})...")
release_down()
input("Press Enter to move all the way DOWN (about 56 seconds from max height)...")
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misleading prompt message: The message says "about 56 seconds from max height" but this function is called during reset which should start from the current position (unknown), not necessarily from max height. This could confuse users if the desk isn't at max height.

Suggested change
input("Press Enter to move all the way DOWN (about 56 seconds from max height)...")
input("Press Enter to move all the way DOWN (this will take up to 56 seconds)...")

Copilot uses AI. Check for mistakes.
release_down()
input("Press Enter to move all the way DOWN (about 56 seconds from max height)...")
press_down()
time.sleep(56.0)
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded magic number: The 56.0 second duration is duplicated from reset_to_lowest.py without explanation. Consider extracting this as a named constant (e.g., MAX_TO_MIN_TIME = 56.0) with a comment explaining its derivation, or better yet, calculate it from (HIGHEST_HEIGHT - LOWEST_HEIGHT) / DOWN_RATE where DOWN_RATE = 0.55.

Copilot uses AI. Check for mistakes.
@sgbaird sgbaird self-requested a review November 15, 2025 04:15
@sgbaird
Copy link
Member

sgbaird commented Nov 18, 2025

@copilot

See requirements below:

  1. someone sets up all the hardware and RPi software per the instructions at https://progressive-automations-python.readthedocs.io/en/latest/
  2. they run pip install progressive-automations-python
  3. they run prefect cloud login -k <api-key> and whatever other commands are needed to get the workpools/deployments/etc. started (i.e., probably prefect worker start ...)
  4. they should also be able to run progressive_automations_python --test <UP or DOWN> or similar for some simple testing
  5. they should be able to call run_deployment(...) externally and have the raspberry pi pick that up and execute the command
  6. the move command triggered externally via run_deployment should be asynchronous, but there should also be a way to poll the position (i.e., see if it's in its final state), keeping in mind the duty cycle limits and the potential error codes that might occur. Other equipment will have this @flow as a dependency, where it needs to reach the desired position before the next piece of equipment can do its job

Related to last point, including a snippet from context7 chat below:

In Prefect v3, normally I'd run ```from prefect.deployments import run_deployment  run_deployment(     name="custom-movements-flow/custom-movements",     parameters={"position_inches": 35.5} )```, but this is blocking. I want to be able to send a command asynchronously and poll its status for later (similar to baking an oven, preheat to 350 degrees, not sitting in front of the oven the entire time)

I'll help you find the async solution for Prefect v3. Let me fetch the documentation on deployments and async execution.


resolve-library-id
libraryName:
Prefect
Now let me fetch the Prefect documentation focused on async execution and non-blocking deployment runs:


get-library-docs
context7CompatibleLibraryID:
/prefecthq/prefect
topic:
async deployment run_deployment non-blocking
Perfect! I found exactly what you need. Here's how to run a deployment asynchronously and poll its status later in Prefect v3:

Async Non-Blocking Deployment Trigger
Instead of blocking with run_deployment(), you can use await run_deployment() in an async context:

import asyncio
from prefect.deployments import run_deployment

async def trigger_deployment():
    flow_run = await run_deployment(
        name="custom-movements-flow/custom-movements",
        parameters={"position_inches": 35.5}
    )
    return flow_run

# Trigger it without waiting
flow_run = asyncio.run(trigger_deployment())
print(f"Flow run ID: {flow_run.id}")
Return Immediately Without Waiting
If you want to trigger and return immediately (set the oven to 350° and walk away), use timeout=0:

from prefect.deployments import run_deployment

flow_run = run_deployment(
    name="custom-movements-flow/custom-movements",
    parameters={"position_inches": 35.5},
    timeout=0  # Returns immediately
)
print(f"Flow run ID: {flow_run.id}")
Polling the Status Later
Once you have the flow_run object, you can poll its status later:

from prefect.deployments import run_deployment
from prefect import get_client
import asyncio

# Trigger without waiting
flow_run = run_deployment(
    name="custom-movements-flow/custom-movements",
    parameters={"position_inches": 35.5},
    timeout=0
)

# Later... poll the status
async def check_status(flow_run_id):
    async with get_client() as client:
        flow_run = await client.read_flow_run(flow_run_id)
        print(f"Status: {flow_run.state.type}")
        print(f"Result: {flow_run.state.result if flow_run.state_type == 'COMPLETED' else 'Still running'}")
        return flow_run

# Check status anytime
flow_run_status = asyncio.run(check_status(flow_run.id))
Key Parameters:

timeout=0: Returns immediately without waiting for completion
timeout=300: Waits up to 5 minutes for completion
await run_deployment(): Non-blocking async version
This gives you exactly what you need—send the command (preheat to 350°), get back the flow run ID immediately, then check on it whenever you want!

Copy link
Contributor

Copilot AI commented Nov 18, 2025

@sgbaird I've opened a new pull request, #4, to work on those changes. Once the pull request is ready, I'll request review from you.

- Updated duty_cycle.py to enhance movement validation against duty cycle limits.
- Introduced generate_movements.py for generating movement configurations based on current duty cycle status.
- Modified lifter_state.json to reflect updated usage periods and total up time.
- Enhanced main.py to support new commands for deploying immediate and custom movements.
- Created movement_configs.json to store generated movement configurations.
- Updated prefect_flows.py to simplify flow definitions and integrate custom movements execution.
- Added duty cycle monitoring flow for scheduled checks and recommendations based on capacity.
@sgbaird sgbaird merged commit 7dc674f into main Nov 19, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Programmatic control of LG-07 progressive automation modular lifting column for rail / arm combo

3 participants