citrascope 0.1.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {citrascope-0.1.0 → citrascope-0.3.0}/.github/copilot-instructions.md +42 -2
  2. citrascope-0.1.0/.github/workflows/publish.yml → citrascope-0.3.0/.github/workflows/pypi-publish.yml +1 -1
  3. {citrascope-0.1.0 → citrascope-0.3.0}/.github/workflows/pytest.yml +9 -4
  4. {citrascope-0.1.0 → citrascope-0.3.0}/.gitignore +2 -3
  5. citrascope-0.3.0/.python-version +1 -0
  6. {citrascope-0.1.0 → citrascope-0.3.0}/.vscode/launch.json +11 -2
  7. {citrascope-0.1.0 → citrascope-0.3.0}/PKG-INFO +68 -36
  8. {citrascope-0.1.0 → citrascope-0.3.0}/README.md +49 -31
  9. citrascope-0.3.0/citrascope/__main__.py +26 -0
  10. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/api/abstract_api_client.py +7 -0
  11. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/api/citra_api_client.py +30 -1
  12. citrascope-0.3.0/citrascope/citra_scope_daemon.py +243 -0
  13. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/hardware/abstract_astro_hardware_adapter.py +70 -2
  14. citrascope-0.3.0/citrascope/hardware/adapter_registry.py +94 -0
  15. citrascope-0.3.0/citrascope/hardware/indi_adapter.py +730 -0
  16. citrascope-0.3.0/citrascope/hardware/kstars_dbus_adapter.py +179 -0
  17. citrascope-0.3.0/citrascope/hardware/nina_adv_http_adapter.py +593 -0
  18. citrascope-0.3.0/citrascope/hardware/nina_adv_http_survey_template.json +328 -0
  19. citrascope-0.3.0/citrascope/logging/__init__.py +4 -0
  20. citrascope-0.3.0/citrascope/logging/_citrascope_logger.py +114 -0
  21. citrascope-0.3.0/citrascope/logging/web_log_handler.py +74 -0
  22. citrascope-0.3.0/citrascope/settings/citrascope_settings.py +145 -0
  23. citrascope-0.3.0/citrascope/settings/settings_file_manager.py +126 -0
  24. citrascope-0.3.0/citrascope/tasks/runner.py +252 -0
  25. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/tasks/scope/base_telescope_task.py +25 -10
  26. citrascope-0.3.0/citrascope/tasks/scope/static_telescope_task.py +24 -0
  27. citrascope-0.3.0/citrascope/web/__init__.py +1 -0
  28. citrascope-0.3.0/citrascope/web/app.py +470 -0
  29. citrascope-0.3.0/citrascope/web/server.py +123 -0
  30. citrascope-0.3.0/citrascope/web/static/api.js +82 -0
  31. citrascope-0.3.0/citrascope/web/static/app.js +500 -0
  32. citrascope-0.3.0/citrascope/web/static/config.js +362 -0
  33. citrascope-0.3.0/citrascope/web/static/img/citra.png +0 -0
  34. citrascope-0.3.0/citrascope/web/static/img/favicon.png +0 -0
  35. citrascope-0.3.0/citrascope/web/static/style.css +120 -0
  36. citrascope-0.3.0/citrascope/web/static/websocket.js +127 -0
  37. citrascope-0.3.0/citrascope/web/templates/dashboard.html +354 -0
  38. {citrascope-0.1.0 → citrascope-0.3.0}/pyproject.toml +29 -5
  39. {citrascope-0.1.0/tests → citrascope-0.3.0/tests/unit}/test_hardware_adapter.py +23 -0
  40. citrascope-0.3.0/tests/unit/test_task_manager.py +299 -0
  41. {citrascope-0.1.0/tests → citrascope-0.3.0/tests/unit}/utils.py +4 -0
  42. citrascope-0.1.0/.env.example +0 -8
  43. citrascope-0.1.0/.github/workflows/docker-publish.yml +0 -29
  44. citrascope-0.1.0/Dockerfile +0 -30
  45. citrascope-0.1.0/citrascope/__main__.py +0 -23
  46. citrascope-0.1.0/citrascope/citra_scope_daemon.py +0 -90
  47. citrascope-0.1.0/citrascope/hardware/indi_adapter.py +0 -290
  48. citrascope-0.1.0/citrascope/logging/__init__.py +0 -3
  49. citrascope-0.1.0/citrascope/logging/_citrascope_logger.py +0 -35
  50. citrascope-0.1.0/citrascope/settings/_citrascope_settings.py +0 -42
  51. citrascope-0.1.0/citrascope/tasks/runner.py +0 -156
  52. citrascope-0.1.0/citrascope/tasks/scope/static_telescope_task.py +0 -16
  53. {citrascope-0.1.0 → citrascope-0.3.0}/.devcontainer/devcontainer.json +0 -0
  54. {citrascope-0.1.0 → citrascope-0.3.0}/.flake8 +0 -0
  55. {citrascope-0.1.0 → citrascope-0.3.0}/.github/dependabot.yml +0 -0
  56. {citrascope-0.1.0 → citrascope-0.3.0}/.pre-commit-config.yaml +0 -0
  57. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/__init__.py +0 -0
  58. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/settings/__init__.py +0 -0
  59. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/tasks/scope/tracking_telescope_task.py +0 -0
  60. {citrascope-0.1.0 → citrascope-0.3.0}/citrascope/tasks/task.py +0 -0
  61. {citrascope-0.1.0 → citrascope-0.3.0}/docs/index.md +0 -0
  62. {citrascope-0.1.0/tests → citrascope-0.3.0/tests/unit}/test_api_client.py +0 -0
@@ -12,10 +12,18 @@ This project is a Python package for interacting with astronomical data and serv
12
12
 
13
13
  ## Directory Structure
14
14
  - `citrascope/api/`: API client code
15
- - `citrascope/indi/`: INDI client integration
15
+ - `citrascope/hardware/`: Hardware adapter implementations and registry
16
+ - `adapter_registry.py`: Central registry for all hardware adapters (add new adapters here)
17
+ - `abstract_astro_hardware_adapter.py`: Base class all adapters must implement
18
+ - Individual adapter implementations (indi_adapter.py, nina_adv_http_adapter.py, etc.)
16
19
  - `citrascope/logging/`: Logging utilities
17
20
  - `citrascope/settings/`: Settings and configuration
18
21
  - `citrascope/tasks/`: Task runner and definitions
22
+ - `citrascope/web/`: Web interface for monitoring and configuration
23
+ - `citrascope/web/app.py`: FastAPI application and routes
24
+ - `citrascope/web/server.py`: Web server management and threading
25
+ - `citrascope/web/templates/`: HTML templates
26
+ - `citrascope/web/static/`: CSS and JavaScript files
19
27
  - `tests/`: Unit and integration tests
20
28
  - `docs/`: Project documentation
21
29
 
@@ -32,12 +40,39 @@ This project is a Python package for interacting with astronomical data and serv
32
40
 
33
41
  ## Common Tasks
34
42
  - Add new API integrations in `citrascope/api/`.
35
- - Extend INDI client features in `citrascope/indi/`.
43
+ - Extend or add hardware adapters:
44
+ - Create new adapter class implementing `AbstractAstroHardwareAdapter` in `citrascope/hardware/`
45
+ - Register it in `citrascope/hardware/adapter_registry.py` by adding an entry to `REGISTERED_ADAPTERS`
46
+ - All adapter discovery and instantiation flows from this registry
36
47
  - Update logging logic in `citrascope/logging/`.
37
48
  - Change settings in `citrascope/settings/`.
38
49
  - Add or modify tasks in `citrascope/tasks/`.
50
+ - Enhance web interface in `citrascope/web/`.
39
51
  - Write or update tests in `tests/`.
40
52
 
53
+ ## Web Interface Guidelines
54
+
55
+ The web interface provides real-time monitoring and configuration for telescope operations. When working on web-related features:
56
+
57
+ ### Design Principles
58
+ - **Dark theme required**: All UI elements must use dark colors suitable for nighttime telescope operations to preserve night vision
59
+ - **Real-time updates**: Use WebSocket connections for live status, logs, and telemetry
60
+ - **Mobile-friendly**: The interface should be responsive and usable on tablets/phones in the field
61
+ - **Minimal distractions**: Reduce visual clutter; prioritize essential telescope and task information
62
+
63
+ ### Architecture
64
+ - **FastAPI backend** (`web/app.py`): RESTful API endpoints and WebSocket handlers
65
+ - **Separate thread**: Web server runs in daemon thread with its own event loop (`web/server.py`)
66
+ - **Log streaming**: Custom `WebLogHandler` broadcasts logs to web clients in real-time
67
+ - **Static files**: HTML, CSS, and JavaScript are served from `web/templates/` and `web/static/`
68
+
69
+ ### Development Notes
70
+ - Keep web-specific code isolated in the `citrascope/web/` directory
71
+ - The daemon should only call `web_server.start()` - all web complexity stays in `web/server.py`
72
+ - Filter out noise from web logs (HTTP requests, WebSocket events, Uvicorn messages)
73
+ - Use thread-safe mechanisms when accessing daemon state from web handlers
74
+ - Port 24872 (CITRA on phone keypad) is the default web interface port
75
+
41
76
  ## Important Packages
42
77
 
43
78
  This project relies on several key Python packages. Below are some of the most important ones and their roles:
@@ -58,6 +93,11 @@ This project relies on several key Python packages. Below are some of the most i
58
93
  - **Flake8**: Enforces code style and linting rules.
59
94
  - **Sphinx**: Generates project documentation.
60
95
 
96
+ ### Web Interface Dependencies
97
+ - **FastAPI**: Modern async web framework for the monitoring interface
98
+ - **Uvicorn**: ASGI server for running the web application
99
+ - **WebSockets**: Real-time bidirectional communication for live updates
100
+
61
101
  For a complete list of dependencies, refer to the `pyproject.toml` file.
62
102
 
63
103
  ## Additional Notes
@@ -1,4 +1,4 @@
1
- name: Publish Python 🐍 package
1
+ name: Publish Python Package
2
2
 
3
3
  on:
4
4
  release:
@@ -9,24 +9,29 @@ on:
9
9
  jobs:
10
10
  test:
11
11
  runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ['3.10', '3.11', '3.12']
12
16
  steps:
13
17
  - name: Checkout code
14
18
  uses: actions/checkout@v4
15
- - name: Set up Python
19
+ - name: Set up Python ${{ matrix.python-version }}
16
20
  uses: actions/setup-python@v5
17
21
  with:
18
- python-version: '3.12'
22
+ python-version: ${{ matrix.python-version }}
19
23
  - name: Install project prerequisites
20
24
  run: sudo apt-get update && sudo apt-get install -y libglib2.0-dev libdbus-1-dev libjpeg-dev zlib1g-dev
21
25
  - name: Install dependencies
22
26
  run: |
23
27
  pip install --upgrade pip
24
- pip install .[dev,test]
28
+ pip install .[dev,test,indi,all]
25
29
  - name: Run pytest with coverage
26
30
  run: |
27
- pytest --cov=citrascope --cov-report=term-missing --cov-report=xml --cov-branch
31
+ pytest -m "not integration" --cov=citrascope --cov-report=term-missing --cov-report=xml --cov-branch
28
32
  - name: Upload coverage report
29
33
  uses: actions/upload-artifact@v4
34
+ if: matrix.python-version == '3.12'
30
35
  with:
31
36
  name: coverage-xml
32
37
  path: coverage.xml
@@ -81,9 +81,6 @@ target/
81
81
  profile_default/
82
82
  ipython_config.py
83
83
 
84
- # pyenv
85
- .python-version
86
-
87
84
  # pipenv
88
85
  # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
86
  # However, in case of collaboration, if having platform-specific dependencies or dependencies
@@ -141,3 +138,5 @@ citra_image.fits
141
138
  /images/*
142
139
  .DS_Store
143
140
  hip_main.dat
141
+ nina_sequence_*.json
142
+ nina_focus_positions.json
@@ -0,0 +1 @@
1
+ 3.12
@@ -5,16 +5,25 @@
5
5
  "configurations": [
6
6
  {
7
7
  "name": "Python: citrascope dev start",
8
- "type": "python",
8
+ "type": "debugpy",
9
9
  "request": "launch",
10
10
  "module": "citrascope",
11
11
  "args": ["--dev", "start"],
12
12
  "console": "integratedTerminal",
13
13
  "justMyCode": false
14
14
  },
15
+ {
16
+ "name": "Python: citrascope dev start, keep images",
17
+ "type": "debugpy",
18
+ "request": "launch",
19
+ "module": "citrascope",
20
+ "args": ["--dev", "--keep-images", "start"],
21
+ "console": "integratedTerminal",
22
+ "justMyCode": false
23
+ },
15
24
  {
16
25
  "name": "Python: citrascope dev start DEBUG logging",
17
- "type": "python",
26
+ "type": "debugpy",
18
27
  "request": "launch",
19
28
  "module": "citrascope",
20
29
  "args": ["--dev", "--log-level", "DEBUG", "start"],
@@ -1,27 +1,35 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: citrascope
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: Remotely control a telescope while it polls for tasks, collects and edge processes data, and delivers results and data for further processing.
5
- Author-email: Christopher Stevens <chris@citra.space>
5
+ Author-email: Patrick McDavid <patrick@citra.space>
6
6
  License-Expression: MIT
7
- Requires-Python: >=3.9
7
+ Requires-Python: <3.13,>=3.10
8
8
  Requires-Dist: click
9
+ Requires-Dist: fastapi>=0.104.0
9
10
  Requires-Dist: httpx
10
- Requires-Dist: pixelemon
11
+ Requires-Dist: platformdirs>=4.0.0
11
12
  Requires-Dist: pydantic-settings
12
- Requires-Dist: pyindi-client
13
13
  Requires-Dist: pytest-cov
14
14
  Requires-Dist: python-dateutil
15
15
  Requires-Dist: python-json-logger
16
16
  Requires-Dist: requests
17
17
  Requires-Dist: skyfield
18
18
  Requires-Dist: types-python-dateutil
19
+ Requires-Dist: uvicorn[standard]>=0.24.0
20
+ Requires-Dist: websockets>=12.0
21
+ Provides-Extra: all
22
+ Requires-Dist: dbus-python; extra == 'all'
23
+ Requires-Dist: pixelemon; extra == 'all'
24
+ Requires-Dist: plotly; extra == 'all'
25
+ Requires-Dist: pyindi-client; extra == 'all'
19
26
  Provides-Extra: build
20
27
  Requires-Dist: build; extra == 'build'
21
28
  Provides-Extra: deploy
22
29
  Requires-Dist: twine; extra == 'deploy'
23
30
  Provides-Extra: dev
24
31
  Requires-Dist: black; extra == 'dev'
32
+ Requires-Dist: bump-my-version; extra == 'dev'
25
33
  Requires-Dist: flake8; extra == 'dev'
26
34
  Requires-Dist: flake8-pytest-style; extra == 'dev'
27
35
  Requires-Dist: isort; extra == 'dev'
@@ -34,6 +42,12 @@ Provides-Extra: docs
34
42
  Requires-Dist: sphinx; extra == 'docs'
35
43
  Requires-Dist: sphinx-autodoc-typehints; extra == 'docs'
36
44
  Requires-Dist: sphinx-markdown-builder; extra == 'docs'
45
+ Provides-Extra: indi
46
+ Requires-Dist: pixelemon; extra == 'indi'
47
+ Requires-Dist: plotly; extra == 'indi'
48
+ Requires-Dist: pyindi-client; extra == 'indi'
49
+ Provides-Extra: kstars
50
+ Requires-Dist: dbus-python; extra == 'kstars'
37
51
  Provides-Extra: test
38
52
  Requires-Dist: mockito; extra == 'test'
39
53
  Requires-Dist: pytest; extra == 'test'
@@ -41,24 +55,44 @@ Requires-Dist: pytest-cov; extra == 'test'
41
55
  Description-Content-Type: text/markdown
42
56
 
43
57
  # CitraScope
44
- [![Pytest](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml) [![Build and Push Docker Image](https://github.com/citra-space/citrascope/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/docker-publish.yml)
58
+ [![Pytest](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml) [![Publish Python Package](https://github.com/citra-space/citrascope/actions/workflows/pypi-publish.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/pypi-publish.yml)
45
59
 
46
60
  Remotely control a telescope while it polls for tasks, collects observations, and delivers data for further processing.
47
61
 
48
62
  ## Features
49
-
63
+ - Offers a web UI to configure hardware and connect to Citra.space's api
50
64
  - Connects to Citra.space's API and identifies itself as an online telescope
51
- - Connects to configured INDI telescope and camera hardware
65
+ - Connects to configured telescope and camera hardware
52
66
  - Acts as a task daemon carrying out and remitting photography tasks
53
67
 
54
68
  ## Installation
55
69
 
56
- Install CitraScope from PyPI:
70
+ ### Install with pipx
71
+
72
+ [pipx](https://pipx.pypa.io/) installs the CLI tool in an isolated environment while making it globally available:
57
73
 
58
74
  ```sh
59
- pip install citrascope
75
+ pipx install citrascope
60
76
  ```
61
77
 
78
+ ### Optional Dependencies
79
+
80
+ CitraScope supports different hardware adapters through optional dependency groups:
81
+
82
+ - **INDI adapter** (for Linux-based telescope control):
83
+ ```sh
84
+ pipx install citrascope[indi]
85
+ # or with pip: pip install citrascope[indi]
86
+ ```
87
+
88
+ - **All optional dependencies**:
89
+ ```sh
90
+ pipx install citrascope[all]
91
+ # or with pip: pip install citrascope[all]
92
+ ```
93
+
94
+ The base installation without optional dependencies supports the NINA adapter, which works via HTTP API calls and does not require additional Python packages.
95
+
62
96
  This provides the `citrascope` command-line tool. To see available commands:
63
97
 
64
98
  ```sh
@@ -79,31 +113,6 @@ To connect to the Citra Dev server:
79
113
  citrascope start --dev
80
114
  ```
81
115
 
82
- ## Configuration
83
-
84
- Settings are managed via environment variables with the prefix `CITRASCOPE_`. You must configure your personal access token and telescope ID, as well as INDI server details. You can set these variables in your shell or in a `.env` file at the project root.
85
-
86
- Example `.env` file:
87
-
88
- ```env
89
- CITRASCOPE_PERSONAL_ACCESS_TOKEN=citra_pat_xxx
90
- CITRASCOPE_TELESCOPE_ID=xxx
91
- # CITRASCOPE_INDI_SERVER_URL=127.0.0.1
92
- CITRASCOPE_INDI_SERVER_URL=host.docker.internal # use with devcontainer for accessing a localhost indi server
93
- CITRASCOPE_INDI_SERVER_PORT=7624
94
- CITRASCOPE_INDI_TELESCOPE_NAME=Telescope Simulator
95
- ```
96
-
97
- **Variable descriptions:**
98
-
99
- - `CITRASCOPE_PERSONAL_ACCESS_TOKEN`: Your CitraScope personal access token (required)
100
- - `CITRASCOPE_TELESCOPE_ID`: Your telescope ID (required)
101
- - `CITRASCOPE_INDI_SERVER_URL`: Hostname or IP address of the INDI server (default: `host.docker.internal` for devcontainers, or `127.0.0.1` for local)
102
- - `CITRASCOPE_INDI_SERVER_PORT`: Port for the INDI server (default: `7624`)
103
- - `CITRASCOPE_INDI_TELESCOPE_NAME`: Name of the INDI telescope device (default: `Telescope Simulator`)
104
-
105
- You can copy `.env.example` to `.env` and tweak your values.
106
-
107
116
  ## Developer Setup
108
117
 
109
118
  If you are developing on macOS or Windows, use the provided [VS Code Dev Container](https://code.visualstudio.com/docs/devcontainers/containers) setup. The devcontainer provides a full Linux environment, which is required for the `pyindi-client` dependency to work. This is necessary because `pyindi-client` only works on Linux, and will not function natively on Mac or Windows.
@@ -112,6 +121,16 @@ By opening this project in VS Code and choosing "Reopen in Container" (or using
112
121
 
113
122
  The devcontainer also ensures all required system dependencies (like `cmake`) are installed automatically.
114
123
 
124
+ ### Python Version
125
+
126
+ This project requires Python 3.10 or higher, up to Python 3.12. A `.python-version` file is included specifying Python 3.12 as the recommended version. If you use [pyenv](https://github.com/pyenv/pyenv), it will automatically use this version when you enter the project directory.
127
+
128
+ ### If not using the dev container:
129
+ ```sh
130
+ python -m venv .venv
131
+ source .venv/bin/activate
132
+ ```
133
+
115
134
  ### Installing Development Dependencies
116
135
 
117
136
  To install development dependencies (for code style, linting, and pre-commit hooks):
@@ -138,6 +157,19 @@ pre-commit run --all-files
138
157
 
139
158
  This ensures code style and quality checks are enforced for all contributors.
140
159
 
160
+ ### Releasing a New Version
161
+
162
+ To bump the version and create a release:
163
+
164
+ ```sh
165
+ bump-my-version bump patch # 0.1.3 → 0.1.4
166
+ bump-my-version bump minor # 0.1.3 → 0.2.0
167
+ bump-my-version bump major # 0.1.3 → 1.0.0
168
+ git push && git push --tags
169
+ ```
170
+
171
+ Then create a release in the GitHub UI from the new tag. This triggers automatic PyPI publishing.
172
+
141
173
  ### Running and Debugging with VS Code
142
174
 
143
175
  If you are using Visual Studio Code, you can run or debug the project directly using the pre-configured launch options in `.vscode/launch.json`:
@@ -151,7 +183,7 @@ To use these, open the Run and Debug panel in VS Code, select the desired config
151
183
 
152
184
  This project uses [pytest](https://pytest.org/) for unit testing. All tests are located in the `tests/` directory.
153
185
 
154
- To run tests manually:
186
+ To run unit tests within your devcontainer:
155
187
 
156
188
  ```bash
157
189
  pytest
@@ -1,22 +1,42 @@
1
1
  # CitraScope
2
- [![Pytest](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml) [![Build and Push Docker Image](https://github.com/citra-space/citrascope/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/docker-publish.yml)
2
+ [![Pytest](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/pytest.yml) [![Publish Python Package](https://github.com/citra-space/citrascope/actions/workflows/pypi-publish.yml/badge.svg)](https://github.com/citra-space/citrascope/actions/workflows/pypi-publish.yml)
3
3
 
4
4
  Remotely control a telescope while it polls for tasks, collects observations, and delivers data for further processing.
5
5
 
6
6
  ## Features
7
-
7
+ - Offers a web UI to configure hardware and connect to Citra.space's api
8
8
  - Connects to Citra.space's API and identifies itself as an online telescope
9
- - Connects to configured INDI telescope and camera hardware
9
+ - Connects to configured telescope and camera hardware
10
10
  - Acts as a task daemon carrying out and remitting photography tasks
11
11
 
12
12
  ## Installation
13
13
 
14
- Install CitraScope from PyPI:
14
+ ### Install with pipx
15
+
16
+ [pipx](https://pipx.pypa.io/) installs the CLI tool in an isolated environment while making it globally available:
15
17
 
16
18
  ```sh
17
- pip install citrascope
19
+ pipx install citrascope
18
20
  ```
19
21
 
22
+ ### Optional Dependencies
23
+
24
+ CitraScope supports different hardware adapters through optional dependency groups:
25
+
26
+ - **INDI adapter** (for Linux-based telescope control):
27
+ ```sh
28
+ pipx install citrascope[indi]
29
+ # or with pip: pip install citrascope[indi]
30
+ ```
31
+
32
+ - **All optional dependencies**:
33
+ ```sh
34
+ pipx install citrascope[all]
35
+ # or with pip: pip install citrascope[all]
36
+ ```
37
+
38
+ The base installation without optional dependencies supports the NINA adapter, which works via HTTP API calls and does not require additional Python packages.
39
+
20
40
  This provides the `citrascope` command-line tool. To see available commands:
21
41
 
22
42
  ```sh
@@ -37,31 +57,6 @@ To connect to the Citra Dev server:
37
57
  citrascope start --dev
38
58
  ```
39
59
 
40
- ## Configuration
41
-
42
- Settings are managed via environment variables with the prefix `CITRASCOPE_`. You must configure your personal access token and telescope ID, as well as INDI server details. You can set these variables in your shell or in a `.env` file at the project root.
43
-
44
- Example `.env` file:
45
-
46
- ```env
47
- CITRASCOPE_PERSONAL_ACCESS_TOKEN=citra_pat_xxx
48
- CITRASCOPE_TELESCOPE_ID=xxx
49
- # CITRASCOPE_INDI_SERVER_URL=127.0.0.1
50
- CITRASCOPE_INDI_SERVER_URL=host.docker.internal # use with devcontainer for accessing a localhost indi server
51
- CITRASCOPE_INDI_SERVER_PORT=7624
52
- CITRASCOPE_INDI_TELESCOPE_NAME=Telescope Simulator
53
- ```
54
-
55
- **Variable descriptions:**
56
-
57
- - `CITRASCOPE_PERSONAL_ACCESS_TOKEN`: Your CitraScope personal access token (required)
58
- - `CITRASCOPE_TELESCOPE_ID`: Your telescope ID (required)
59
- - `CITRASCOPE_INDI_SERVER_URL`: Hostname or IP address of the INDI server (default: `host.docker.internal` for devcontainers, or `127.0.0.1` for local)
60
- - `CITRASCOPE_INDI_SERVER_PORT`: Port for the INDI server (default: `7624`)
61
- - `CITRASCOPE_INDI_TELESCOPE_NAME`: Name of the INDI telescope device (default: `Telescope Simulator`)
62
-
63
- You can copy `.env.example` to `.env` and tweak your values.
64
-
65
60
  ## Developer Setup
66
61
 
67
62
  If you are developing on macOS or Windows, use the provided [VS Code Dev Container](https://code.visualstudio.com/docs/devcontainers/containers) setup. The devcontainer provides a full Linux environment, which is required for the `pyindi-client` dependency to work. This is necessary because `pyindi-client` only works on Linux, and will not function natively on Mac or Windows.
@@ -70,6 +65,16 @@ By opening this project in VS Code and choosing "Reopen in Container" (or using
70
65
 
71
66
  The devcontainer also ensures all required system dependencies (like `cmake`) are installed automatically.
72
67
 
68
+ ### Python Version
69
+
70
+ This project requires Python 3.10 or higher, up to Python 3.12. A `.python-version` file is included specifying Python 3.12 as the recommended version. If you use [pyenv](https://github.com/pyenv/pyenv), it will automatically use this version when you enter the project directory.
71
+
72
+ ### If not using the dev container:
73
+ ```sh
74
+ python -m venv .venv
75
+ source .venv/bin/activate
76
+ ```
77
+
73
78
  ### Installing Development Dependencies
74
79
 
75
80
  To install development dependencies (for code style, linting, and pre-commit hooks):
@@ -96,6 +101,19 @@ pre-commit run --all-files
96
101
 
97
102
  This ensures code style and quality checks are enforced for all contributors.
98
103
 
104
+ ### Releasing a New Version
105
+
106
+ To bump the version and create a release:
107
+
108
+ ```sh
109
+ bump-my-version bump patch # 0.1.3 → 0.1.4
110
+ bump-my-version bump minor # 0.1.3 → 0.2.0
111
+ bump-my-version bump major # 0.1.3 → 1.0.0
112
+ git push && git push --tags
113
+ ```
114
+
115
+ Then create a release in the GitHub UI from the new tag. This triggers automatic PyPI publishing.
116
+
99
117
  ### Running and Debugging with VS Code
100
118
 
101
119
  If you are using Visual Studio Code, you can run or debug the project directly using the pre-configured launch options in `.vscode/launch.json`:
@@ -109,7 +127,7 @@ To use these, open the Run and Debug panel in VS Code, select the desired config
109
127
 
110
128
  This project uses [pytest](https://pytest.org/) for unit testing. All tests are located in the `tests/` directory.
111
129
 
112
- To run tests manually:
130
+ To run unit tests within your devcontainer:
113
131
 
114
132
  ```bash
115
133
  pytest
@@ -0,0 +1,26 @@
1
+ import click
2
+
3
+ from citrascope.citra_scope_daemon import CitraScopeDaemon
4
+ from citrascope.settings.citrascope_settings import CitraScopeSettings
5
+
6
+
7
+ @click.group()
8
+ @click.option("--dev", is_flag=True, default=False, help="Use the development API (dev.app.citra.space)")
9
+ @click.option("--log-level", default="INFO", help="Set logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)")
10
+ @click.option("--keep-images", is_flag=True, default=False, help="Keep image files after upload (do not delete)")
11
+ @click.pass_context
12
+ def cli(ctx, dev, log_level, keep_images):
13
+ ctx.obj = {"settings": CitraScopeSettings(dev=dev, log_level=log_level, keep_images=keep_images)}
14
+
15
+
16
+ @cli.command("start")
17
+ @click.option("--web-host", default="0.0.0.0", help="Web server host address (default: 0.0.0.0)")
18
+ @click.option("--web-port", default=24872, type=int, help="Web server port (default: 24872)")
19
+ @click.pass_context
20
+ def start(ctx, web_host, web_port):
21
+ daemon = CitraScopeDaemon(ctx.obj["settings"], enable_web=True, web_host=web_host, web_port=web_port)
22
+ daemon.run()
23
+
24
+
25
+ if __name__ == "__main__":
26
+ cli()
@@ -21,3 +21,10 @@ class AbstractCitraApiClient(ABC):
21
21
  @abstractmethod
22
22
  def get_ground_station(self, ground_station_id):
23
23
  pass
24
+
25
+ @abstractmethod
26
+ def put_telescope_status(self, body):
27
+ """
28
+ PUT to /telescopes to report online status.
29
+ """
30
+ pass
@@ -6,6 +6,20 @@ from .abstract_api_client import AbstractCitraApiClient
6
6
 
7
7
 
8
8
  class CitraApiClient(AbstractCitraApiClient):
9
+ def put_telescope_status(self, body):
10
+ """
11
+ PUT to /telescopes to report online status.
12
+ """
13
+ try:
14
+ response = self._request("PUT", "/telescopes", json=body)
15
+ if self.logger:
16
+ self.logger.debug(f"PUT /telescopes: {response}")
17
+ return response
18
+ except Exception as e:
19
+ if self.logger:
20
+ self.logger.error(f"Failed PUT /telescopes: {e}")
21
+ return None
22
+
9
23
  def __init__(self, host: str, token: str, use_ssl: bool = True, logger=None):
10
24
  self.base_url = ("https" if use_ssl else "http") + "://" + host
11
25
  self.token = token
@@ -57,8 +71,10 @@ class CitraApiClient(AbstractCitraApiClient):
57
71
 
58
72
  def upload_image(self, task_id, telescope_id, filepath):
59
73
  """Upload an image file for a given task."""
74
+ file_size = os.path.getsize(filepath)
60
75
  signed_url_response = self._request(
61
- "POST", f"/my/images?filename=citra_task_{task_id}_image.fits&telescope_id={telescope_id}&task_id={task_id}"
76
+ "POST",
77
+ f"/my/images?filename=citra_task_{task_id}_image.fits&telescope_id={telescope_id}&task_id={task_id}&file_size={file_size}",
62
78
  )
63
79
  if not signed_url_response or "uploadUrl" not in signed_url_response:
64
80
  if self.logger:
@@ -99,3 +115,16 @@ class CitraApiClient(AbstractCitraApiClient):
99
115
  if self.logger:
100
116
  self.logger.error(f"Failed to mark task {task_id} as complete: {e}")
101
117
  return None
118
+
119
+ def mark_task_failed(self, task_id):
120
+ """Mark a task as failed using the API."""
121
+ try:
122
+ body = {"status": "Failed"}
123
+ response = self._request("PUT", f"/tasks/{task_id}", json=body)
124
+ if self.logger:
125
+ self.logger.debug(f"Marked task {task_id} as failed: {response}")
126
+ return response
127
+ except Exception as e:
128
+ if self.logger:
129
+ self.logger.error(f"Failed to mark task {task_id} as failed: {e}")
130
+ return None