Skip to content

Conversation

PayalLakra
Copy link
Member

@PayalLakra PayalLakra commented Jul 5, 2025

I have made the following changes:

  • Added Docstrings and comments in files
  • Now, the code is refactored fully
  • Creating chordspy Python Package

Summary by CodeRabbit

  • New Features

    • Introduced automated release workflow with version validation, build, and publishing to PyPI and GitHub.
    • Added a new unified connection manager supporting USB, WiFi, and BLE with real-time streaming and recording.
    • Added a web-based interface for device connection, data streaming, and app launching with launch buttons.
  • Bug Fixes

    • Fixed resource paths for images and assets to ensure correct loading.
  • Documentation

    • Enhanced code and HTML templates with detailed comments and docstrings.
    • Clarified and improved README with updated installation and usage instructions.
  • Refactor

    • Modularized application entry points for improved maintainability.
    • Replaced legacy scripts with new maintainable modules.
    • Removed legacy connection and device interface modules and replaced with updated implementations.
  • Chores

    • Added project configuration files for packaging and release automation.
    • Updated and cleaned requirements and manifest files.
    • Removed deprecated files and consolidated dependencies.

Copy link

coderabbitai bot commented Jul 5, 2025

Walkthrough

This update restructures the project by modularizing code into the chordspy package, introducing a robust multi-protocol Connection class, and refactoring the Flask web interface. It adds packaging and release automation with Poetry and GitHub Actions, updates resource paths, improves documentation, and standardizes entry points for application scripts.

Changes

File(s) / Path(s) Change Summary
.github/workflows/release.yaml, pyproject.toml, MANIFEST.in Added release automation workflow, project packaging configuration, and manifest for resource inclusion.
README.md, requirements.txt Improved documentation, installation/usage instructions, and minor formatting fix in requirements.
app.py, connection.py, app_requirements.txt, chords_requirements.txt, chords_serial.py Removed old monolithic Flask app, connection manager, requirements files, and serial handler.
chordspy/__init__.py Added package initializer importing main app and connection class.
chordspy/app.py New Flask web interface module with device management, streaming, and app launching routes.
chordspy/connection.py New modular Connection class for USB, WiFi, BLE with LSL and CSV support, and CLI entry point.
chordspy/chords_serial.py Added modular USB serial handler class for hardware detection and streaming.
chordspy/chords_ble.py, chordspy/chords_wifi.py Enhanced BLE and WiFi handlers with detailed docstrings, comments, and improved structure.
chordspy/beetle.py, chordspy/keystroke.py Updated resource paths for image loading to reflect new package structure.
chordspy/game.py Reduced minimum EEG sample size for data processing.
chordspy/static/script.js, chordspy/templates/index.html Added app launch button, refactored JS event handling, and expanded inline HTML documentation.
chordspy/csvplotter.py, chordspy/emgenvelope.py, chordspy/eog.py, chordspy/ffteeg.py, chordspy/gui.py, chordspy/heartbeat_ecg.py Refactored script entry points into main() functions for modularity.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant WebUI (Flask)
    participant ConnectionManager
    participant Device (USB/WiFi/BLE)
    participant LSL/CSV

    User->>WebUI (Flask): Connect/Scan/Record/Launch App
    WebUI (Flask)->>ConnectionManager: connect_device(protocol, params)
    ConnectionManager->>Device: Establish connection (USB/WiFi/BLE)
    Device-->>ConnectionManager: Data packets
    ConnectionManager->>LSL/CSV: Stream data, optionally record
    User->>WebUI (Flask): Stop/Disconnect/Stop Recording
    WebUI (Flask)->>ConnectionManager: cleanup()
    ConnectionManager->>Device: Close connection
    ConnectionManager->>LSL/CSV: Stop streams/recording
Loading

Possibly related PRs

  • #40: Refactors Flask app to use a centralized Connection class for USB, WiFi, BLE, and adds backend routes for device management and recording—strongly related to the modularization and new Flask interface in this PR.
  • #43: Updates the old connection.py with new threaded handlers and cleanup logic; related as this PR removes and replaces connection.py with a new modular implementation.
  • #38: Extends the original Flask app with NPG-LITE BLE support and related routes; related as this PR replaces the entire Flask app with a new implementation.

Poem

🐇
The code now leaps with modular might,
Connections handled left and right.
Flask serves apps and streams anew,
With BLE, WiFi, USB in view!
Release flows hop with Poetry’s tune—
This rabbit’s home is tidy soon!
🥕

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

🧹 Nitpick comments (20)
chordspy/gui.py (1)

125-131: PEP-8 spacing & tighter main() interface

Flake8 rightly flags the missing blank lines.
Also, plot_lsl_data() returns the app object but the return value is ignored; relying on a global adds coupling.

@@
 
+
+
 def main():
     app = plot_lsl_data()      # capture the returned QApplication
-    if inlet:
-        sys.exit(app.exec_())
+    if inlet:
+        sys.exit(app.exec_())
 
 
 if __name__ == "__main__":
     main()

Two blank lines before main() and before the guard satisfy E302/E305 and the explicit app variable reduces hidden globals.

chordspy/csvplotter.py (1)

102-108: Minor PEP-8 spacing deviation

Insert two blank lines before main() and before the if __name__ … guard to silence E302/E305.

@@
 
+
+
 def main():
     root = tk.Tk()               # Create the main Tkinter root window
     CSVPlotterApp(root)          # Create an instance of the CSVPlotterApp class
     root.mainloop()              # Start the Tkinter main loop
 
+
 if __name__ == "__main__":
     main()
chordspy/eog.py (1)

186-194: Consistent spacing around new main()

Same PEP-8 blank-line issue as elsewhere; quick fix:

@@
 
+
+
 def main():
     app = QApplication(sys.argv)
     window = EOGMonitor()
     print("Note: There will be a 2s calibration delay before peak detection starts.")
     window.show()
     sys.exit(app.exec_())
 
+
 if __name__ == "__main__":
     main()
chordspy/ffteeg.py (1)

421-428: Good refactoring for modularity, but fix PEP8 formatting issues.

The extraction of main execution logic into a dedicated main() function improves code modularity and follows the consistent pattern applied across the project. However, the static analysis correctly identifies PEP8 formatting violations.

Apply this diff to fix the formatting issues:

-
+

 def main():
     app = QApplication(sys.argv)
     window = EEGMonitor()  
     window.show()
     sys.exit(app.exec_())

+
 if __name__ == "__main__":
     main()
chordspy/emgenvelope.py (1)

139-146: Consistent refactoring pattern, but address formatting issues.

The main function extraction follows the same beneficial pattern as other GUI modules in the project, improving modularity and consistency.

Apply this diff to fix PEP8 formatting:

         self.close()
-                  
+


 def main():
     app = QApplication(sys.argv)
     window = EMGMonitor()  
     window.show()
     sys.exit(app.exec_())

+
 if __name__ == "__main__":
     main()
chordspy/heartbeat_ecg.py (1)

163-170: Maintain consistency with other modules, fix formatting.

The refactoring maintains consistency with the other GUI modules and improves code organization.

Apply this diff to address PEP8 formatting:

         self.r_peak_curve.setData(r_peak_times, r_peak_values)  # Plot R-peaks as red dots

+
 def main():
     app = QApplication(sys.argv)
     window = ECGMonitor()  
     window.show()
     sys.exit(app.exec_())    

+
 if __name__ == "__main__":
     main()   
chordspy/__init__.py (1)

1-2: Add __all__ to clarify intended public API

The static analysis warnings about unused imports are false positives - these imports are intended to expose the key components (main and Connection) at the package level for external use. Consider adding an __all__ declaration to make the intended public API explicit:

+__all__ = ['main', 'Connection']
+
 from chordspy.app import main
 from chordspy.connection import Connection

This will clarify that these imports are intentional for the package's public interface and may resolve the static analysis warnings.

chordspy/static/script.js (1)

951-968: Consider consolidating initialization calls

The DOMContentLoaded handler has several initialization calls that could be organized better:

 document.addEventListener('DOMContentLoaded', () => {
-    initializeApplication();
-    checkStreamStatus();
-    setInterval(checkStreamStatus, 1000);
-    startTimestampUpdater();
+    // Initialize core application functionality
+    initializeApplication();
+    startTimestampUpdater();
+    
+    // Set up periodic status checking
+    checkStreamStatus();
+    setInterval(checkStreamStatus, 1000);
     
     // Error handling
     window.onerror = function(message, source, lineno, colno, error) {

This groups related functionality and removes the duplicate checkStreamStatus() call (since initializeApplication() likely calls it via startAppStatusChecker()).

README.md (1)

3-3: Fix grammatical issues for better clarity

Address the following grammatical improvements flagged by LanguageTool:

-Chords- Python is an open-source bag of tools designed to interface with Micro-controller development boards running [Chords Arduino Firmware](https://github.com/upsidedownlabs/Chords-Arduino-Firmware). Use Upside Down Labs bio-potential amplifiers to read data, visualize it, record data in CSV Files, and stream it via Lab Streaming Layer.
+Chords-Python is an open-source bag of tools designed to interface with microcontroller development boards running [Chords Arduino Firmware](https://github.com/upsidedownlabs/Chords-Arduino-Firmware). Use Upside-Down Labs' bio-potential amplifiers to read data, visualize it, record data in CSV files, and stream it via the Lab Streaming Layer.

Changes made:

  • Fixed spacing in "Chords-Python"
  • Added possessive apostrophe for "Upside-Down Labs'"
  • Added hyphen to "Upside-Down"
  • Added "the" before "Lab Streaming Layer"
  • Capitalization consistency for "CSV files"
.github/workflows/release.yaml (1)

133-133: Add newline at end of file.

YAML files should end with a newline character for proper formatting.

Add a newline character after the last line.

chordspy/chords_serial.py (3)

108-108: Remove unused variable assignment.

The sampling_rate variable is assigned but never used.

-                    sampling_rate = self.supported_boards[self.board]["sampling_rate"]

If this value is needed for future use, consider storing it as an instance attribute or using it in logging.


16-17: Add blank line before class definition.

PEP 8 requires two blank lines before class definitions.

 import threading
 
+
 class Chords_USB:

276-277: Add blank line after class definition.

PEP 8 requires two blank lines after class definitions.

         last_ten_minute_time = time.time()
 
+
 if __name__ == "__main__":
chordspy/app.py (2)

59-60: Remove unused exception variable.

The exception variable e is not used in the except block.

-    except Exception as e:
+    except Exception:
         return jsonify({'status': 'error', 'message': 'Logging failed'}), 500

200-200: Add missing whitespace after comma.

-        return jsonify({'status': 'error', 'message': f'{module_name} is already running','code': 'ALREADY_RUNNING'}), 400
+        return jsonify({'status': 'error', 'message': f'{module_name} is already running', 'code': 'ALREADY_RUNNING'}), 400
chordspy/chords_ble.py (2)

107-111: Remove or use the extracted channel data.

The channels variable is extracted but never used. If this is for debugging, consider adding a comment or removing it.

-        # Extract channel data (2 bytes per channel, big-endian, signed)
-        channels = [int.from_bytes(sample_data[i:i+2], byteorder='big', signed=True) 
-            for i in range(1, len(sample_data), 2)]
+        # Extract channel data (2 bytes per channel, big-endian, signed)
+        # channels = [int.from_bytes(sample_data[i:i+2], byteorder='big', signed=True) 
+        #     for i in range(1, len(sample_data), 2)]
+        # TODO: Process channel data when needed

28-28: Add spaces before inline comment.

-    CONTROL_CHAR_UUID = "0000ff01-0000-1000-8000-00805f9b34fb" # UUID for control characteristic
+    CONTROL_CHAR_UUID = "0000ff01-0000-1000-8000-00805f9b34fb"  # UUID for control characteristic
chordspy/connection.py (3)

2-2: Fix typo in documentation header.

Remove the trailing dash in the documentation header.

-CHORDS Data Connection-
+CHORDS Data Connection

628-628: Add blank lines for PEP 8 compliance.

Add two blank lines before the function definition.

        self.cleanup()

+
def main():

680-681: Add blank line for module-level code separation.

Add a blank line after the function definition.

if __name__ == '__main__':
    main()
+
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c18ad73 and 187e58b.

⛔ Files ignored due to path filters (14)
  • chordspy/media/Beetle1.png is excluded by !**/*.png
  • chordspy/media/Beetle10.png is excluded by !**/*.png
  • chordspy/media/Beetle2.png is excluded by !**/*.png
  • chordspy/media/Beetle3.png is excluded by !**/*.png
  • chordspy/media/Beetle4.png is excluded by !**/*.png
  • chordspy/media/Beetle5.png is excluded by !**/*.png
  • chordspy/media/Beetle6.png is excluded by !**/*.png
  • chordspy/media/Beetle7.png is excluded by !**/*.png
  • chordspy/media/Beetle8.png is excluded by !**/*.png
  • chordspy/media/Beetle9.png is excluded by !**/*.png
  • chordspy/media/Interface.png is excluded by !**/*.png
  • chordspy/media/Webinterface.png is excluded by !**/*.png
  • chordspy/media/brass-fanfare-with-timpani-and-winchimes-reverberated-146260.wav is excluded by !**/*.wav
  • chordspy/media/icons8-eye-30.png is excluded by !**/*.png
📒 Files selected for processing (27)
  • .github/workflows/release.yaml (1 hunks)
  • MANIFEST.in (1 hunks)
  • README.md (1 hunks)
  • app.py (0 hunks)
  • app_requirements.txt (0 hunks)
  • chords_requirements.txt (0 hunks)
  • chords_serial.py (0 hunks)
  • chordspy/__init__.py (1 hunks)
  • chordspy/app.py (1 hunks)
  • chordspy/beetle.py (1 hunks)
  • chordspy/chords_ble.py (10 hunks)
  • chordspy/chords_serial.py (1 hunks)
  • chordspy/chords_wifi.py (6 hunks)
  • chordspy/connection.py (1 hunks)
  • chordspy/csvplotter.py (1 hunks)
  • chordspy/emgenvelope.py (1 hunks)
  • chordspy/eog.py (1 hunks)
  • chordspy/ffteeg.py (1 hunks)
  • chordspy/game.py (1 hunks)
  • chordspy/gui.py (1 hunks)
  • chordspy/heartbeat_ecg.py (1 hunks)
  • chordspy/keystroke.py (1 hunks)
  • chordspy/static/script.js (3 hunks)
  • chordspy/templates/index.html (5 hunks)
  • connection.py (0 hunks)
  • pyproject.toml (1 hunks)
  • requirements.txt (0 hunks)
💤 Files with no reviewable changes (6)
  • requirements.txt
  • chords_requirements.txt
  • app_requirements.txt
  • chords_serial.py
  • app.py
  • connection.py
🧰 Additional context used
🧬 Code Graph Analysis (6)
chordspy/__init__.py (2)
chordspy/app.py (1)
  • main (357-366)
chordspy/connection.py (2)
  • main (628-678)
  • Connection (36-626)
chordspy/emgenvelope.py (3)
chordspy/eog.py (1)
  • main (186-191)
chordspy/heartbeat_ecg.py (1)
  • main (163-167)
chordspy/ffteeg.py (1)
  • main (421-425)
chordspy/chords_serial.py (5)
chordspy/static/script.js (10)
  • data (177-177)
  • data (194-194)
  • data (573-573)
  • data (692-692)
  • response (34-34)
  • response (129-133)
  • response (171-171)
  • response (191-191)
  • response (563-567)
  • response (691-691)
chordspy/test/new_parser.py (7)
  • connect_hardware (58-122)
  • detect_hardware (125-133)
  • send_command (50-56)
  • cleanup (275-310)
  • start_timer (192-198)
  • log_one_second_data (201-206)
  • log_ten_minute_data (209-219)
chordspy/chords_ble.py (1)
  • cleanup (204-213)
chordspy/chords_wifi.py (1)
  • cleanup (181-193)
chordspy/connection.py (1)
  • cleanup (558-620)
chordspy/connection.py (3)
chordspy/chords_serial.py (5)
  • Chords_USB (17-275)
  • read_data (161-200)
  • detect_hardware (121-141)
  • send_command (143-159)
  • cleanup (222-231)
chordspy/chords_wifi.py (3)
  • Chords_WIFI (13-199)
  • connect (61-75)
  • cleanup (181-193)
chordspy/chords_ble.py (6)
  • Chords_BLE (13-238)
  • scan_devices (55-69)
  • notification_handler (113-129)
  • connect (215-231)
  • cleanup (204-213)
  • stop (233-238)
chordspy/app.py (5)
chordspy/connection.py (8)
  • Connection (36-626)
  • cleanup (558-620)
  • connect_usb (524-556)
  • connect_wifi (460-522)
  • connect_ble (399-458)
  • start_csv_recording (140-171)
  • stop_csv_recording (173-195)
  • main (628-678)
chordspy/static/script.js (6)
  • config (40-40)
  • data (177-177)
  • data (194-194)
  • data (573-573)
  • data (692-692)
  • filename (776-776)
chordspy/chords_ble.py (1)
  • cleanup (204-213)
chordspy/chords_wifi.py (1)
  • cleanup (181-193)
chordspy/chords_serial.py (1)
  • cleanup (222-231)
chordspy/chords_wifi.py (3)
chordspy/chords_ble.py (1)
  • cleanup (204-213)
chordspy/chords_serial.py (1)
  • cleanup (222-231)
chordspy/connection.py (1)
  • cleanup (558-620)
🪛 Flake8 (7.2.0)
chordspy/__init__.py

[error] 1-1: 'chordspy.app.main' imported but unused

(F401)


[error] 2-2: 'chordspy.connection.Connection' imported but unused

(F401)

chordspy/eog.py

[error] 186-186: expected 2 blank lines, found 1

(E302)


[error] 193-193: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/heartbeat_ecg.py

[error] 163-163: expected 2 blank lines, found 1

(E302)


[error] 169-169: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/csvplotter.py

[error] 102-102: expected 2 blank lines, found 1

(E302)


[error] 107-107: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/gui.py

[error] 125-125: expected 2 blank lines, found 1

(E302)


[error] 130-130: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/emgenvelope.py

[error] 139-139: expected 2 blank lines, found 1

(E302)


[error] 145-145: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/ffteeg.py

[error] 421-421: expected 2 blank lines, found 1

(E302)


[error] 427-427: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/chords_serial.py

[error] 17-17: expected 2 blank lines, found 1

(E302)


[error] 70-70: at least two spaces before inline comment

(E261)


[error] 108-108: local variable 'sampling_rate' is assigned to but never used

(F841)


[error] 256-256: global missing_samples is unused: name is never assigned in scope

(F824)


[error] 265-265: global supported_boards is unused: name is never assigned in scope

(F824)


[error] 265-265: global board is unused: name is never assigned in scope

(F824)


[error] 277-277: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/chords_ble.py

[error] 28-28: at least two spaces before inline comment

(E261)


[error] 108-108: local variable 'channels' is assigned to but never used

(F841)


[error] 109-109: continuation line under-indented for visual indent

(E128)


[error] 240-240: expected 2 blank lines, found 1

(E302)

chordspy/connection.py

[error] 36-36: expected 2 blank lines, found 1

(E302)


[error] 264-264: local variable 'estimated_rate' is assigned to but never used

(F841)


[error] 264-264: at least two spaces before inline comment

(E261)


[error] 428-428: continuation line under-indented for visual indent

(E128)


[error] 628-628: expected 2 blank lines, found 1

(E302)


[error] 680-680: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/app.py

[error] 25-25: redefinition of unused 'logging' from line 17

(F811)


[error] 41-41: expected 2 blank lines, found 1

(E302)


[error] 59-59: local variable 'e' is assigned to but never used

(F841)


[error] 63-63: expected 2 blank lines, found 1

(E302)


[error] 81-81: expected 2 blank lines, found 1

(E302)


[error] 87-87: expected 2 blank lines, found 1

(E302)


[error] 95-95: at least two spaces before inline comment

(E261)


[error] 110-110: expected 2 blank lines, found 1

(E302)


[error] 122-122: continuation line under-indented for visual indent

(E128)


[error] 129-129: expected 2 blank lines, found 1

(E302)


[error] 140-140: expected 2 blank lines, found 1

(E302)


[error] 152-152: expected 2 blank lines, found 1

(E302)


[error] 166-166: expected 2 blank lines, found 1

(E302)


[error] 182-182: expected 2 blank lines, found 1

(E302)


[error] 200-200: missing whitespace after ','

(E231)


[error] 216-216: expected 2 blank lines, found 1

(E302)


[error] 234-234: expected 2 blank lines, found 1

(E302)


[error] 293-293: expected 2 blank lines, found 1

(E302)


[error] 300-300: global connection_manager is unused: name is never assigned in scope

(F824)


[error] 309-309: expected 2 blank lines, found 1

(E302)


[error] 316-316: global connection_manager is unused: name is never assigned in scope

(F824)


[error] 337-337: expected 2 blank lines, found 1

(E302)


[error] 344-344: global connection_manager is unused: name is never assigned in scope

(F824)


[error] 357-357: expected 2 blank lines, found 1

(E302)


[error] 368-368: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/chords_wifi.py

[error] 11-11: 'scipy.signal.butter' imported but unused

(F401)


[error] 11-11: 'scipy.signal.filtfilt' imported but unused

(F401)


[error] 13-13: expected 2 blank lines, found 1

(E302)


[error] 126-126: local variable 'sps' is assigned to but never used

(F841)


[error] 127-127: local variable 'fps' is assigned to but never used

(F841)


[error] 128-128: local variable 'bps' is assigned to but never used

(F841)


[error] 201-201: expected 2 blank lines after class or function definition, found 1

(E305)

🪛 Ruff (0.11.9)
chordspy/__init__.py

1-1: chordspy.app.main imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


2-2: chordspy.connection.Connection imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)

chordspy/chords_serial.py

108-108: Local variable sampling_rate is assigned to but never used

Remove assignment to unused variable sampling_rate

(F841)

chordspy/chords_ble.py

108-108: Local variable channels is assigned to but never used

Remove assignment to unused variable channels

(F841)

chordspy/connection.py

161-161: Use a context manager for opening files

(SIM115)


264-264: Local variable estimated_rate is assigned to but never used

Remove assignment to unused variable estimated_rate

(F841)


668-669: Use a single if statement instead of nested if statements

(SIM102)

chordspy/app.py

25-25: Redefinition of unused logging from line 17

Remove definition: logging

(F811)


59-59: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

chordspy/chords_wifi.py

11-11: scipy.signal.butter imported but unused

Remove unused import

(F401)


11-11: scipy.signal.filtfilt imported but unused

Remove unused import

(F401)


126-126: Local variable sps is assigned to but never used

Remove assignment to unused variable sps

(F841)


127-127: Local variable fps is assigned to but never used

Remove assignment to unused variable fps

(F841)


128-128: Local variable bps is assigned to but never used

Remove assignment to unused variable bps

(F841)

🪛 LanguageTool
README.md

[uncategorized] ~3-~3: The adjective “Upside-Down” is spelled with a hyphen.
Context: ...edownlabs/Chords-Arduino-Firmware). Use Upside Down Labs bio-potential amplifiers to read d...

(UPSIDE_DOWN)


[uncategorized] ~3-~3: It seems likely that a singular genitive (’s) apostrophe is missing.
Context: ...ords-Arduino-Firmware). Use Upside Down Labs bio-potential amplifiers to read data, ...

(AI_HYDRA_LEO_APOSTROPHE_S_XS)


[uncategorized] ~3-~3: You might be missing the article “the” here.
Context: ...rd data in CSV Files, and stream it via Lab Streaming Layer. > [!NOTE] > **Fir...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🪛 actionlint (1.7.7)
.github/workflows/release.yaml

128-128: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


130-130: property "details" is not defined in object type {pypi_publish: {outputs: {}; result: string}; setup_and_build: {outputs: {}; result: string}}

(expression)

🪛 YAMLlint (1.37.1)
.github/workflows/release.yaml

[error] 133-133: no new line character at the end of file

(new-line-at-end-of-file)

🪛 Pylint (3.3.7)
chordspy/chords_serial.py

[refactor] 17-17: Too many instance attributes (8/7)

(R0902)


[error] 258-258: Undefined variable 'missing_samples'

(E0602)


[error] 269-269: Undefined variable 'supported_boards'

(E0602)


[error] 269-269: Undefined variable 'board'

(E0602)

chordspy/connection.py

[refactor] 36-36: Too many instance attributes (27/7)

(R0902)


[refactor] 161-161: Consider using 'with' for resource-allocating operations

(R1732)


[error] 290-290: Instance of 'Chords_BLE' has no 'data_available' member

(E1101)


[error] 295-295: Instance of 'Chords_BLE' has no 'get_latest_sample' member

(E1101)


[refactor] 287-319: Too many nested blocks (6/5)

(R1702)


[error] 331-331: Instance of 'Chords_WIFI' has no 'data_available' member

(E1101)


[error] 336-336: Instance of 'Chords_WIFI' has no 'get_latest_sample' member

(E1101)


[refactor] 328-357: Too many nested blocks (6/5)

(R1702)


[refactor] 366-397: Too many nested blocks (6/5)

(R1702)


[refactor] 558-558: Too many branches (15/12)

(R0912)

chordspy/app.py

[refactor] 207-207: Consider using 'with' for resource-allocating operations

(R1732)


[refactor] 226-230: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)


[error] 267-267: Assigning result of a function call, where the function has no return

(E1111)


[error] 278-278: Possibly using variable 'success' before assignment

(E0606)

chordspy/chords_wifi.py

[refactor] 13-13: Too many instance attributes (14/7)

(R0902)


[refactor] 34-34: Too many arguments (6/5)

(R0913)


[refactor] 34-34: Too many positional arguments (6/5)

(R0917)


[refactor] 88-88: Too many branches (15/12)

(R0912)


[refactor] 88-88: Too many statements (58/50)

(R0915)

🪛 ast-grep (0.38.1)
chordspy/app.py

[warning] 365-365: Detected Flask app with debug=True. Do not deploy to production with this flag enabled as it will leak sensitive information. Instead, consider using Flask configuration variables or setting 'debug' using system environment variables.
Context: app.run(debug=True, use_reloader=False, host='0.0.0.0', port=5000)
Note: [CWE-489] Active Debug Code. [REFERENCES]
- https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/

(debug-enabled-python)

🔇 Additional comments (12)
pyproject.toml (2)

32-33: Console script entry point is well-defined.

The console script configuration properly maps the chordspy command to the main application entry point.


12-31: Verify dependency security using pip-audit with correct invocation

The Poetry migration is solid—next, ensure all fixed versions are free of known vulnerabilities. Since pip-audit may install to ~/.local/bin (outside your PATH), invoke it like this:

# Create a temporary requirements file
cat > temp_requirements.txt << EOF
numpy==2.1.3
pylsl==1.16.2
pyserial==3.5
bleak==0.22.3
pyqtgraph==0.13.7
PyQt5==5.15.11
keyboard==0.13.5
scipy==1.14.1
pygame==2.6.1
neurokit2==0.2.10
plotly==5.24.1
pandas==2.2.3
tk==0.1.0
PyAutoGUI==0.9.54
Flask==3.1.1
psutil==6.1.1
websocket-client==1.8.0
PyYAML==6.0.2
EOF

# Install pip-audit if needed
pip install --user pip-audit

# Run vulnerability scan via module to avoid PATH issues
python3 -m pip_audit --requirement temp_requirements.txt

# Cleanup
rm temp_requirements.txt

• Ensure the scan completes without errors.
• Review any reported CVEs and update versions as necessary.
• Once all vulnerabilities are addressed, merge the Poetry changes.

MANIFEST.in (1)

1-5: Well-structured manifest for Flask web application

The MANIFEST.in file correctly includes all necessary resources for the Flask web interface:

  • Essential documentation files (requirements.txt, README.md)
  • Web application assets (templates, static)
  • Configuration files (config)

This ensures the package will work correctly when installed via pip.

chordspy/static/script.js (2)

115-118: Clean implementation of launch button functionality

The inline onclick approach works well for the dynamic button generation. The launchApp function properly handles different response scenarios including already running apps.


127-149: Robust error handling in launchApp function

Good implementation with:

  • Proper async/await usage
  • Specific handling of ALREADY_RUNNING status
  • Comprehensive error catching and logging
  • Clear user feedback through alerts
README.md (2)

16-32: Excellent simplification of installation process

The new installation instructions are much clearer and more user-friendly compared to the previous multi-step process. The virtual environment setup with platform-specific activation commands is particularly helpful.


35-38: Updated usage reflects new package structure

The change from python app.py to chordspy command correctly reflects the new console script entry point defined in the package configuration.

chordspy/templates/index.html (2)

3-5: Excellent documentation enhancement

The added HTML comments throughout the template significantly improve code maintainability by clearly explaining:

  • The purpose of each major section
  • UI component functionality
  • Dark mode configuration
  • Interactive element behavior

This documentation will be valuable for future developers working on the frontend.


177-181: Clear Tailwind configuration documentation

The comment explaining the class-based dark mode configuration is helpful for understanding the styling approach used throughout the application.

chordspy/chords_ble.py (1)

1-52: Excellent documentation improvements!

The added docstrings and comments significantly improve code readability and maintainability.

chordspy/chords_wifi.py (1)

1-43: Well-documented class and methods!

The added docstrings provide excellent clarity on the WiFi client's functionality and usage.

chordspy/connection.py (1)

161-161: Current file handling approach is appropriate.

The file needs to remain open across multiple method calls for continuous recording, so a context manager is not suitable here. The existing error handling and cleanup in stop_csv_recording() ensures proper resource management.

@PayalLakra PayalLakra changed the title Code Refactoring and Documentation Python Package, Code Refactoring and Documentation Jul 5, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
chordspy/app.py (2)

365-365: Disable debug mode for production deployment.

Running Flask with debug=True is a security risk as it exposes sensitive information and enables the interactive debugger.

Consider using environment variables to control debug mode:

-    app.run(debug=True, use_reloader=False, host='0.0.0.0', port=5000)
+    debug_mode = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
+    app.run(debug=debug_mode, use_reloader=False, host='0.0.0.0', port=5000)

258-284: Fix connection return value handling in run_connection.

The connection methods are expected to return boolean values, but based on the static analysis and past review comments, these methods don't consistently return success/failure status. This causes issues where success may be None even when connections succeed.

For WiFi connections specifically, the method doesn't return a value, so success becomes None:

             elif protocol == 'wifi':
-                success = connection_manager.connect_wifi()
+                connection_manager.connect_wifi()
+                success = True  # WiFi connection starts background threads

This ensures the success variable is properly set for all connection types.

chordspy/chords_wifi.py (1)

125-128: Log or use the calculated rates.

The sample rate (sps), frame rate (fps), and byte rate (bps) are calculated but never used or logged, making this computation wasteful.

                 # Calculate samples, packets, and bytes per second
                 sps = self.calculate_rate(self.sample_size, elapsed_time)
                 fps = self.calculate_rate(self.packet_size, elapsed_time)
                 bps = self.calculate_rate(self.data_size, elapsed_time)
+                
+                # Log the rates for monitoring
+                print(f"Rates - Samples: {sps:.1f}/s, Packets: {fps:.1f}/s, Bytes: {bps:.1f}/s")

Alternatively, if these rates aren't needed, consider removing the calculations entirely.

🧹 Nitpick comments (6)
.github/workflows/release.yaml (1)

133-133: Add missing newline at end of file.

The file is missing a newline character at the end, which violates YAML formatting standards.

          generate_release_notes: true
+
chordspy/app.py (1)

58-58: Remove unused exception variable.

The exception variable e is assigned but never used in the except block.

     except Exception as e:
-        return jsonify({'status': 'error', 'message': 'Logging failed'}), 500
+        return jsonify({'status': 'error', 'message': 'Logging failed'}), 500

Or utilize the exception for better error reporting:

     except Exception as e:
-        return jsonify({'status': 'error', 'message': 'Logging failed'}), 500
+        return jsonify({'status': 'error', 'message': f'Logging failed: {str(e)}'}), 500
chordspy/connection.py (4)

44-44: Fix typo in docstring.

There's a typo "monitorin" that should be "monitoring".

-    - Sample validation and rate monitorin
+    - Sample validation and rate monitoring

36-36: Add missing blank lines before class definition.

Python PEP 8 requires 2 blank lines before class definitions.

+

 class Connection:

628-628: Add missing blank lines before function definition.

Python PEP 8 requires 2 blank lines before top-level function definitions.

+

 def main():

680-681: Add missing blank lines after function definition.

Python PEP 8 requires 2 blank lines after class or function definitions at module level.

 if __name__ == '__main__':
     main()
+

+
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 187e58b and 58f37ce.

📒 Files selected for processing (4)
  • .github/workflows/release.yaml (1 hunks)
  • chordspy/app.py (1 hunks)
  • chordspy/chords_wifi.py (6 hunks)
  • chordspy/connection.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
chordspy/connection.py (3)
chordspy/chords_serial.py (5)
  • Chords_USB (17-275)
  • read_data (161-200)
  • detect_hardware (121-141)
  • send_command (143-159)
  • cleanup (222-231)
chordspy/chords_wifi.py (3)
  • Chords_WIFI (12-198)
  • connect (60-74)
  • cleanup (180-192)
chordspy/chords_ble.py (6)
  • Chords_BLE (13-238)
  • scan_devices (55-69)
  • notification_handler (113-129)
  • connect (215-231)
  • cleanup (204-213)
  • stop (233-238)
🪛 ast-grep (0.38.1)
chordspy/app.py

[warning] 364-364: Detected Flask app with debug=True. Do not deploy to production with this flag enabled as it will leak sensitive information. Instead, consider using Flask configuration variables or setting 'debug' using system environment variables.
Context: app.run(debug=True, use_reloader=False, host='0.0.0.0', port=5000)
Note: [CWE-489] Active Debug Code. [REFERENCES]
- https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/

(debug-enabled-python)

🪛 Ruff (0.11.9)
chordspy/app.py

58-58: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

chordspy/connection.py

161-161: Use a context manager for opening files

(SIM115)


264-264: Local variable estimated_rate is assigned to but never used

Remove assignment to unused variable estimated_rate

(F841)


668-669: Use a single if statement instead of nested if statements

(SIM102)

chordspy/chords_wifi.py

125-125: Local variable sps is assigned to but never used

Remove assignment to unused variable sps

(F841)


126-126: Local variable fps is assigned to but never used

Remove assignment to unused variable fps

(F841)


127-127: Local variable bps is assigned to but never used

Remove assignment to unused variable bps

(F841)

🪛 Flake8 (7.2.0)
chordspy/app.py

[error] 40-40: expected 2 blank lines, found 1

(E302)


[error] 58-58: local variable 'e' is assigned to but never used

(F841)


[error] 62-62: expected 2 blank lines, found 1

(E302)


[error] 80-80: expected 2 blank lines, found 1

(E302)


[error] 86-86: expected 2 blank lines, found 1

(E302)


[error] 94-94: at least two spaces before inline comment

(E261)


[error] 109-109: expected 2 blank lines, found 1

(E302)


[error] 121-121: continuation line under-indented for visual indent

(E128)


[error] 128-128: expected 2 blank lines, found 1

(E302)


[error] 139-139: expected 2 blank lines, found 1

(E302)


[error] 151-151: expected 2 blank lines, found 1

(E302)


[error] 165-165: expected 2 blank lines, found 1

(E302)


[error] 181-181: expected 2 blank lines, found 1

(E302)


[error] 199-199: missing whitespace after ','

(E231)


[error] 215-215: expected 2 blank lines, found 1

(E302)


[error] 233-233: expected 2 blank lines, found 1

(E302)


[error] 292-292: expected 2 blank lines, found 1

(E302)


[error] 299-299: global connection_manager is unused: name is never assigned in scope

(F824)


[error] 308-308: expected 2 blank lines, found 1

(E302)


[error] 315-315: global connection_manager is unused: name is never assigned in scope

(F824)


[error] 336-336: expected 2 blank lines, found 1

(E302)


[error] 343-343: global connection_manager is unused: name is never assigned in scope

(F824)


[error] 356-356: expected 2 blank lines, found 1

(E302)


[error] 367-367: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/connection.py

[error] 36-36: expected 2 blank lines, found 1

(E302)


[error] 264-264: local variable 'estimated_rate' is assigned to but never used

(F841)


[error] 264-264: at least two spaces before inline comment

(E261)


[error] 428-428: continuation line under-indented for visual indent

(E128)


[error] 628-628: expected 2 blank lines, found 1

(E302)


[error] 680-680: expected 2 blank lines after class or function definition, found 1

(E305)

chordspy/chords_wifi.py

[error] 12-12: expected 2 blank lines, found 1

(E302)


[error] 125-125: local variable 'sps' is assigned to but never used

(F841)


[error] 126-126: local variable 'fps' is assigned to but never used

(F841)


[error] 127-127: local variable 'bps' is assigned to but never used

(F841)


[error] 200-200: expected 2 blank lines after class or function definition, found 1

(E305)

🪛 Pylint (3.3.7)
chordspy/app.py

[refactor] 206-206: Consider using 'with' for resource-allocating operations

(R1732)


[refactor] 225-229: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it

(R1705)


[error] 266-266: Assigning result of a function call, where the function has no return

(E1111)


[error] 277-277: Possibly using variable 'success' before assignment

(E0606)

chordspy/connection.py

[refactor] 36-36: Too many instance attributes (27/7)

(R0902)


[refactor] 161-161: Consider using 'with' for resource-allocating operations

(R1732)


[error] 290-290: Instance of 'Chords_BLE' has no 'data_available' member

(E1101)


[error] 295-295: Instance of 'Chords_BLE' has no 'get_latest_sample' member

(E1101)


[refactor] 287-319: Too many nested blocks (6/5)

(R1702)


[error] 331-331: Instance of 'Chords_WIFI' has no 'data_available' member

(E1101)


[error] 336-336: Instance of 'Chords_WIFI' has no 'get_latest_sample' member

(E1101)


[refactor] 328-357: Too many nested blocks (6/5)

(R1702)


[refactor] 366-397: Too many nested blocks (6/5)

(R1702)


[refactor] 558-558: Too many branches (15/12)

(R0912)

chordspy/chords_wifi.py

[refactor] 12-12: Too many instance attributes (14/7)

(R0902)


[refactor] 33-33: Too many arguments (6/5)

(R0913)


[refactor] 33-33: Too many positional arguments (6/5)

(R0917)


[refactor] 87-87: Too many branches (15/12)

(R0912)


[refactor] 87-87: Too many statements (58/50)

(R0915)

🪛 YAMLlint (1.37.1)
.github/workflows/release.yaml

[error] 133-133: no new line character at the end of file

(new-line-at-end-of-file)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (9)
chordspy/chords_serial.py (1)

241-276: Fix global variable issues in timer methods.

The timer methods reference undefined global variables which will cause NameError exceptions at runtime. These should be instance variables initialized in __init__ and accessed via self.

    def __init__(self):
        """
        Initialize the Chords_USB client and sets up serial connection attributes, data buffer, and installs signal handler for clean exit on interrupt signals.
        """
        self.ser = None               # Serial connection object
        self.buffer = bytearray()     # Buffer for incoming data
        self.retry_limit = 4          # Maximum connection retries
        self.packet_length = None     # Expected packet length for current board
        self.num_channels = None      # Number of data channels for current board
        self.data = None              # Numpy array for storing channel data
        self.board = ""               # Detected board name
        self.streaming_active = False # Streaming state flag
+        
+        # Timer and logging attributes
+        self.start_time = None
+        self.last_ten_minute_time = None
+        self.total_packet_count = 0
+        self.cumulative_packet_count = 0
+        self.samples_per_second = 0
+        self.missing_samples = 0

    def start_timer(self):
        """
        Start the timer for packet counting and logging.
        """
-        global start_time, last_ten_minute_time, total_packet_count, cumulative_packet_count
        current_time = time.time()
-        start_time = current_time            # Session start time
-        last_ten_minute_time = current_time  # 10-minute interval start time
-        total_packet_count = 0               # Counter for packets in current second
-        cumulative_packet_count = 0          # Counter for all packets
+        self.start_time = current_time            # Session start time
+        self.last_ten_minute_time = current_time  # 10-minute interval start time
+        self.total_packet_count = 0               # Counter for packets in current second
+        self.cumulative_packet_count = 0          # Counter for all packets

    def log_one_second_data(self):
        """
        Log data for one second intervals and displays: Number of packets received in the last second, Number of missing samples (if any)
        """
-        global total_packet_count, samples_per_second, missing_samples
-        samples_per_second = total_packet_count
-        print(f"Data count for the last second: {total_packet_count} samples, "f"Missing samples: {missing_samples}")
-        total_packet_count = 0  # Reset for next interval
+        self.samples_per_second = self.total_packet_count
+        print(f"Data count for the last second: {self.total_packet_count} samples, "f"Missing samples: {self.missing_samples}")
+        self.total_packet_count = 0  # Reset for next interval

    def log_ten_minute_data(self):
        """
        Log data for 10-minute intervals and displays: Total packets received, Actual sampling rate, Drift from expected rate
        """
-        global cumulative_packet_count, last_ten_minute_time, supported_boards, board
-        print(f"Total data count after 10 minutes: {cumulative_packet_count}")
-        sampling_rate = cumulative_packet_count / (10 * 60)    # Calculate actual sampling rate
+        print(f"Total data count after 10 minutes: {self.cumulative_packet_count}")
+        sampling_rate = self.cumulative_packet_count / (10 * 60)    # Calculate actual sampling rate
         print(f"Sampling rate: {sampling_rate:.2f} samples/second")
-        expected_sampling_rate = supported_boards[board]["sampling_rate"]
+        expected_sampling_rate = self.supported_boards[self.board]["sampling_rate"]
         drift = ((sampling_rate - expected_sampling_rate) / expected_sampling_rate) * 3600    # Calculate drift from expected rate
         print(f"Drift: {drift:.2f} seconds/hour")
         
         # Reset counters
-        cumulative_packet_count = 0
-        last_ten_minute_time = time.time()
+        self.cumulative_packet_count = 0
+        self.last_ten_minute_time = time.time()
chordspy/connection.py (8)

36-95: Consider refactoring to reduce class complexity.

The Connection class has 27 instance attributes and handles multiple responsibilities (protocol management, LSL streaming, CSV recording, rate monitoring, thread management). This violates the Single Responsibility Principle.

Consider breaking this into separate classes:

  • ConnectionManager (main orchestrator)
  • LSLStreamer (handles LSL operations)
  • CSVRecorder (handles file recording)
  • RateMonitor (handles sample rate calculations)

This would improve modularity, testability, and maintainability.


169-169: Use context manager for file operations.

The file is opened without a context manager, which could lead to resource leaks if an exception occurs.

Since the file needs to remain open for recording duration, implement a proper context manager pattern:

class Connection:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cleanup()

Or ensure robust error handling in cleanup methods.


272-272: Complete rate checker implementation.

The method calculates estimated_rate but never uses it, making the calculation ineffective.

                if filtered_diffs:
                    estimated_rate = 1 / np.mean(filtered_diffs) # Rate = 1/average interval between samples
+                    print(f"\nLSL stream rate verification: {estimated_rate:.2f} Hz")
+                    return estimated_rate
                else:
                    print("\nAll timestamps had zero difference (invalid).")
+                    return None
            else:
                print("\nNot enough timestamps collected to estimate rate.")
+                return None

290-327: Fix references to non-existent BLE methods.

The BLE data handler references self.ble_connection.data_available and self.ble_connection.get_latest_sample() which don't exist in the Chords_BLE class.

Based on the relevant code snippets, the Chords_BLE class doesn't expose these methods. Either:

  1. Implement missing methods in Chords_BLE:
@property
def data_available(self) -> bool:
    # Check if samples are available in internal buffer
    return len(self.sample_buffer) > 0

def get_latest_sample(self) -> list:
    # Return and remove the latest sample
    if self.sample_buffer:
        return self.sample_buffer.popleft()
    return None
  1. Or modify the handler to use existing BLE notification mechanisms.

331-365: Fix references to non-existent WiFi methods.

Similar to BLE, the WiFi handler references self.wifi_connection.data_available and self.wifi_connection.get_latest_sample() which don't exist in the Chords_WIFI class.

The Chords_WIFI class needs these methods implemented or the handler needs to be updated to use existing WiFi data processing methods.


407-467: Consolidate BLE data handling approach.

The BLE connection creates a custom notification handler that processes data while also starting a ble_data_handler thread, causing duplicate data processing logic and potential race conditions.

Consolidate the data handling by either:

  1. Using only the notification handler approach and removing the ble_data_handler thread logic
  2. Storing incoming data in a thread-safe queue in the notification handler and letting ble_data_handler process it

Also fix the indentation issue:

                        channels = [int.from_bytes(sample_data[i:i+2], byteorder='big', signed=True) 
-                            for i in range(1, len(sample_data), 2)]
+                                    for i in range(1, len(sample_data), 2)]

468-531: Consolidate WiFi data handling approach.

The WiFi connection processes data in its own loop while also starting a wifi_data_handler thread, creating inconsistent data handling architecture.

Move all data receiving and processing logic from the main loop into the wifi_data_handler thread for consistency. The main connect_wifi method should only manage connection setup and teardown.


671-683: Fix inconsistent error handling for WiFi connection.

The WiFi connection doesn't check the return value like USB and BLE connections do, and uses nested if statements.

         # WiFi Protocol Handling        
         elif args.protocol == 'wifi':
-            if manager.connect_wifi():                 # Attempt WiFi connection
-                while manager.running:                 # Main execution loop
-                    time.sleep(1)                      # Prevent CPU overutilization
+            manager.connect_wifi()                     # WiFi handles its own loop
                     
         # BLE Protocol Handling
         elif args.protocol == 'ble':
-            if manager.connect_ble(args.ble_address):  # Attempt BLE connection
-                while manager.running:                 # Main execution loop
-                    time.sleep(1)                      # Prevent CPU overutilization
+            if not manager.connect_ble(args.ble_address):  # Attempt BLE connection
+                print("Failed to connect to BLE device")
+                return
+            while manager.running:                     # Main execution loop
+                time.sleep(1)                          # Prevent CPU overutilization
🧹 Nitpick comments (2)
chordspy/chords_serial.py (1)

108-108: Remove unused variable assignment.

The sampling_rate variable is assigned but never used in this method.

-                    sampling_rate = self.supported_boards[self.board]["sampling_rate"]
+                    # sampling_rate = self.supported_boards[self.board]["sampling_rate"]  # Available if needed
chordspy/connection.py (1)

44-44: Fix typo in docstring.

There's a typo in "monitorin" - should be "monitoring".

-    - Sample validation and rate monitorin
+    - Sample validation and rate monitoring
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b1049b and ea979e3.

📒 Files selected for processing (2)
  • chordspy/chords_serial.py (1 hunks)
  • chordspy/connection.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
chordspy/connection.py (3)
chordspy/chords_serial.py (5)
  • Chords_USB (17-275)
  • read_data (161-200)
  • detect_hardware (121-141)
  • send_command (143-159)
  • cleanup (222-231)
chordspy/chords_wifi.py (3)
  • Chords_WIFI (12-198)
  • connect (60-74)
  • cleanup (180-192)
chordspy/chords_ble.py (6)
  • Chords_BLE (13-238)
  • scan_devices (55-69)
  • notification_handler (113-129)
  • connect (215-231)
  • cleanup (204-213)
  • stop (233-238)
🪛 Ruff (0.12.2)
chordspy/chords_serial.py

108-108: Local variable sampling_rate is assigned to but never used

Remove assignment to unused variable sampling_rate

(F841)

chordspy/connection.py

169-169: Use a context manager for opening files

(SIM115)


272-272: Local variable estimated_rate is assigned to but never used

Remove assignment to unused variable estimated_rate

(F841)


679-680: Use a single if statement instead of nested if statements

(SIM102)

🔇 Additional comments (3)
chordspy/chords_serial.py (1)

17-240: LGTM! Well-structured USB communication class.

The class implementation is comprehensive and well-documented. The packet parsing logic, error handling, and resource management are all properly implemented. The signal handling and cleanup mechanisms ensure graceful shutdown.

chordspy/connection.py (2)

532-567: LGTM! Well-implemented USB connection method.

The USB connection method properly handles device detection, configuration, and thread management. The error handling and resource management are appropriate.


569-637: LGTM! Comprehensive cleanup implementation.

The cleanup method properly handles resource deallocation in the correct order, with appropriate error handling and timeout management for thread termination.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
chordspy/chords_serial.py (1)

241-276: Remove global variable declarations and fix undefined variable references.

These timer methods still have the issues previously identified:

  • Remove all global declarations as you're using instance attributes
  • Line 258: missing_samples is undefined and never calculated
  • Line 269: Use self.board instead of undefined board

Apply these fixes:

 def start_timer(self):
     """
     Start the timer for packet counting and logging.
     """
-    global start_time, last_ten_minute_time, total_packet_count, cumulative_packet_count
     current_time = time.time()
     self.start_time = current_time            # Session start time
     self.last_ten_minute_time = current_time  # 10-minute interval start time
     self.total_packet_count = 0               # Counter for packets in current second
     self.cumulative_packet_count = 0          # Counter for all packets

 def log_one_second_data(self):
     """
     Log data for one second intervals and displays: Number of packets received in the last second, Number of missing samples (if any)
     """
-    global total_packet_count, samples_per_second, missing_samples
-    self.samples_per_second = total_packet_count
-    print(f"Data count for the last second: {total_packet_count} samples, "f"Missing samples: {missing_samples}")
+    self.samples_per_second = self.total_packet_count
+    # Calculate missing samples based on expected sampling rate
+    expected_samples = self.supported_boards[self.board]["sampling_rate"]
+    missing_samples = max(0, expected_samples - self.total_packet_count)
+    print(f"Data count for the last second: {self.total_packet_count} samples, "
+          f"Missing samples: {missing_samples}")
     self.total_packet_count = 0  # Reset for next interval

 def log_ten_minute_data(self):
     """
     Log data for 10-minute intervals and displays: Total packets received, Actual sampling rate, Drift from expected rate
     """
-    global cumulative_packet_count, last_ten_minute_time, supported_boards, board
     print(f"Total data count after 10 minutes: {self.cumulative_packet_count}")
     sampling_rate = self.cumulative_packet_count / (10 * 60)    # Calculate actual sampling rate
     print(f"Sampling rate: {sampling_rate:.2f} samples/second")
-    expected_sampling_rate = self.supported_boards[board]["sampling_rate"]
+    expected_sampling_rate = self.supported_boards[self.board]["sampling_rate"]
     drift = ((sampling_rate - expected_sampling_rate) / expected_sampling_rate) * 3600    # Calculate drift from expected rate
     print(f"Drift: {drift:.2f} seconds/hour")
     
     # Reset counters
     self.cumulative_packet_count = 0
     self.last_ten_minute_time = time.time()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 010021e and 7abb380.

📒 Files selected for processing (1)
  • chordspy/chords_serial.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.12.2)
chordspy/chords_serial.py

108-108: Local variable sampling_rate is assigned to but never used

Remove assignment to unused variable sampling_rate

(F841)

🔇 Additional comments (6)
chordspy/chords_serial.py (6)

1-58: Well-structured class definition with comprehensive documentation.

The imports are appropriate, and the class structure with protocol constants and supported boards dictionary provides a clean, maintainable design.


59-75: Proper initialization with thread-safe signal handling.

Good practice to check for the main thread before installing the signal handler. All attributes are correctly initialized as instance variables.


121-142: Effective hardware auto-detection implementation.

The method systematically tries all ports and baud rates, providing good user feedback during the detection process.


143-160: Robust command sending with proper buffer management.

Good practice to flush buffers before sending commands and handle Unicode decode errors gracefully.


161-201: Well-implemented packet parsing with robust error handling.

The method effectively handles packet synchronization, validation, and data extraction. The rolling buffer approach for data storage is efficient.


202-240: Clean streaming control with proper resource management.

The streaming methods provide clear start/stop control with robust cleanup and signal handling for graceful shutdown.

Comment on lines +277 to +280
if __name__ == "__main__":
client = Chords_USB() # Create and run the USB client
client.detect_hardware() # Detect and connect to hardware
client.start_streaming() # Start streaming data No newline at end of file
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for hardware detection failure.

The script should handle the case where hardware detection fails to prevent crashes.

 if __name__ == "__main__":
     client = Chords_USB()     # Create and run the USB client
-    client.detect_hardware()  # Detect and connect to hardware
-    client.start_streaming()  # Start streaming data
+    if client.detect_hardware():  # Detect and connect to hardware
+        client.start_streaming()  # Start streaming data
+    else:
+        print("Failed to detect hardware. Exiting.")
+        sys.exit(1)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In chordspy/chords_serial.py around lines 277 to 280, the code calls
detect_hardware() without handling potential failures, which can cause crashes
if hardware detection fails. Add error handling around the detect_hardware()
call to catch exceptions or check for failure returns, and handle these cases
gracefully by logging an error message and exiting or retrying as appropriate.

self.board = response
print(f"{response} detected at {port} with baudrate {baudrate}")
self.num_channels = self.supported_boards[self.board]["Num_channels"]
sampling_rate = self.supported_boards[self.board]["sampling_rate"]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unused variable assignment.

The sampling_rate variable is assigned but never used in this method.

-sampling_rate = self.supported_boards[self.board]["sampling_rate"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sampling_rate = self.supported_boards[self.board]["sampling_rate"]
🧰 Tools
🪛 Ruff (0.12.2)

108-108: Local variable sampling_rate is assigned to but never used

Remove assignment to unused variable sampling_rate

(F841)

🤖 Prompt for AI Agents
In chordspy/chords_serial.py at line 108, the variable sampling_rate is assigned
but never used. Remove the assignment of sampling_rate entirely from this line
to clean up the code and avoid unused variable warnings.

@lorforlinux
Copy link
Member

@PayalLakra Thank you so much for your contributions, I am merging this now.

@lorforlinux lorforlinux merged commit ee0102d into upsidedownlabs:main Jul 18, 2025
1 check 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.

2 participants