citrascope 0.1.0__tar.gz → 0.4.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 (64) hide show
  1. {citrascope-0.1.0 → citrascope-0.4.0}/.github/copilot-instructions.md +44 -5
  2. citrascope-0.1.0/.github/workflows/publish.yml → citrascope-0.4.0/.github/workflows/pypi-publish.yml +1 -1
  3. {citrascope-0.1.0 → citrascope-0.4.0}/.github/workflows/pytest.yml +9 -4
  4. {citrascope-0.1.0 → citrascope-0.4.0}/.gitignore +2 -6
  5. citrascope-0.4.0/.python-version +1 -0
  6. {citrascope-0.1.0 → citrascope-0.4.0}/.vscode/launch.json +6 -6
  7. {citrascope-0.1.0 → citrascope-0.4.0}/PKG-INFO +87 -47
  8. citrascope-0.4.0/README.md +149 -0
  9. citrascope-0.4.0/citrascope/__main__.py +23 -0
  10. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/api/abstract_api_client.py +7 -0
  11. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/api/citra_api_client.py +43 -2
  12. citrascope-0.4.0/citrascope/citra_scope_daemon.py +234 -0
  13. citrascope-0.4.0/citrascope/constants.py +23 -0
  14. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/hardware/abstract_astro_hardware_adapter.py +70 -2
  15. citrascope-0.4.0/citrascope/hardware/adapter_registry.py +94 -0
  16. citrascope-0.4.0/citrascope/hardware/indi_adapter.py +730 -0
  17. citrascope-0.4.0/citrascope/hardware/kstars_dbus_adapter.py +179 -0
  18. citrascope-0.4.0/citrascope/hardware/nina_adv_http_adapter.py +593 -0
  19. citrascope-0.4.0/citrascope/hardware/nina_adv_http_survey_template.json +328 -0
  20. citrascope-0.4.0/citrascope/logging/__init__.py +4 -0
  21. citrascope-0.4.0/citrascope/logging/_citrascope_logger.py +114 -0
  22. citrascope-0.4.0/citrascope/logging/web_log_handler.py +75 -0
  23. citrascope-0.4.0/citrascope/settings/citrascope_settings.py +140 -0
  24. citrascope-0.4.0/citrascope/settings/settings_file_manager.py +126 -0
  25. citrascope-0.4.0/citrascope/tasks/runner.py +256 -0
  26. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/tasks/scope/base_telescope_task.py +25 -10
  27. citrascope-0.4.0/citrascope/tasks/scope/static_telescope_task.py +24 -0
  28. citrascope-0.4.0/citrascope/web/__init__.py +1 -0
  29. citrascope-0.4.0/citrascope/web/app.py +479 -0
  30. citrascope-0.4.0/citrascope/web/server.py +132 -0
  31. citrascope-0.4.0/citrascope/web/static/api.js +82 -0
  32. citrascope-0.4.0/citrascope/web/static/app.js +502 -0
  33. citrascope-0.4.0/citrascope/web/static/config.js +438 -0
  34. citrascope-0.4.0/citrascope/web/static/img/citra.png +0 -0
  35. citrascope-0.4.0/citrascope/web/static/img/favicon.png +0 -0
  36. citrascope-0.4.0/citrascope/web/static/style.css +152 -0
  37. citrascope-0.4.0/citrascope/web/static/websocket.js +127 -0
  38. citrascope-0.4.0/citrascope/web/templates/dashboard.html +407 -0
  39. {citrascope-0.1.0 → citrascope-0.4.0}/pyproject.toml +30 -13
  40. {citrascope-0.1.0/tests → citrascope-0.4.0/tests/unit}/test_hardware_adapter.py +23 -0
  41. citrascope-0.4.0/tests/unit/test_task_manager.py +299 -0
  42. {citrascope-0.1.0/tests → citrascope-0.4.0/tests/unit}/utils.py +4 -0
  43. citrascope-0.1.0/.env.example +0 -8
  44. citrascope-0.1.0/.github/workflows/docker-publish.yml +0 -29
  45. citrascope-0.1.0/Dockerfile +0 -30
  46. citrascope-0.1.0/README.md +0 -116
  47. citrascope-0.1.0/citrascope/__main__.py +0 -23
  48. citrascope-0.1.0/citrascope/citra_scope_daemon.py +0 -90
  49. citrascope-0.1.0/citrascope/hardware/indi_adapter.py +0 -290
  50. citrascope-0.1.0/citrascope/logging/__init__.py +0 -3
  51. citrascope-0.1.0/citrascope/logging/_citrascope_logger.py +0 -35
  52. citrascope-0.1.0/citrascope/settings/_citrascope_settings.py +0 -42
  53. citrascope-0.1.0/citrascope/tasks/runner.py +0 -156
  54. citrascope-0.1.0/citrascope/tasks/scope/static_telescope_task.py +0 -16
  55. citrascope-0.1.0/docs/index.md +0 -47
  56. {citrascope-0.1.0 → citrascope-0.4.0}/.devcontainer/devcontainer.json +0 -0
  57. {citrascope-0.1.0 → citrascope-0.4.0}/.flake8 +0 -0
  58. {citrascope-0.1.0 → citrascope-0.4.0}/.github/dependabot.yml +0 -0
  59. {citrascope-0.1.0 → citrascope-0.4.0}/.pre-commit-config.yaml +0 -0
  60. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/__init__.py +0 -0
  61. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/settings/__init__.py +0 -0
  62. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/tasks/scope/tracking_telescope_task.py +0 -0
  63. {citrascope-0.1.0 → citrascope-0.4.0}/citrascope/tasks/task.py +0 -0
  64. {citrascope-0.1.0/tests → citrascope-0.4.0/tests/unit}/test_api_client.py +0 -0
@@ -12,12 +12,19 @@ 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
- - `docs/`: Project documentation
21
28
 
22
29
  ## Testing
23
30
  - All new features and bug fixes should include corresponding tests in `tests/`.
@@ -32,17 +39,44 @@ This project is a Python package for interacting with astronomical data and serv
32
39
 
33
40
  ## Common Tasks
34
41
  - Add new API integrations in `citrascope/api/`.
35
- - Extend INDI client features in `citrascope/indi/`.
42
+ - Extend or add hardware adapters:
43
+ - Create new adapter class implementing `AbstractAstroHardwareAdapter` in `citrascope/hardware/`
44
+ - Register it in `citrascope/hardware/adapter_registry.py` by adding an entry to `REGISTERED_ADAPTERS`
45
+ - All adapter discovery and instantiation flows from this registry
36
46
  - Update logging logic in `citrascope/logging/`.
37
47
  - Change settings in `citrascope/settings/`.
38
48
  - Add or modify tasks in `citrascope/tasks/`.
49
+ - Enhance web interface in `citrascope/web/`.
39
50
  - Write or update tests in `tests/`.
40
51
 
52
+ ## Web Interface Guidelines
53
+
54
+ The web interface provides real-time monitoring and configuration for telescope operations. When working on web-related features:
55
+
56
+ ### Design Principles
57
+ - **Dark theme required**: All UI elements must use dark colors suitable for nighttime telescope operations to preserve night vision
58
+ - **Real-time updates**: Use WebSocket connections for live status, logs, and telemetry
59
+ - **Mobile-friendly**: The interface should be responsive and usable on tablets/phones in the field
60
+ - **Minimal distractions**: Reduce visual clutter; prioritize essential telescope and task information
61
+
62
+ ### Architecture
63
+ - **FastAPI backend** (`web/app.py`): RESTful API endpoints and WebSocket handlers
64
+ - **Separate thread**: Web server runs in daemon thread with its own event loop (`web/server.py`)
65
+ - **Log streaming**: Custom `WebLogHandler` broadcasts logs to web clients in real-time
66
+ - **Static files**: HTML, CSS, and JavaScript are served from `web/templates/` and `web/static/`
67
+
68
+ ### Development Notes
69
+ - Keep web-specific code isolated in the `citrascope/web/` directory
70
+ - The daemon should only call `web_server.start()` - all web complexity stays in `web/server.py`
71
+ - Filter out noise from web logs (HTTP requests, WebSocket events, Uvicorn messages)
72
+ - Use thread-safe mechanisms when accessing daemon state from web handlers
73
+ - Port 24872 (CITRA on phone keypad) is the default web interface port
74
+
41
75
  ## Important Packages
42
76
 
43
77
  This project relies on several key Python packages. Below are some of the most important ones and their roles:
44
78
 
45
- - **Click**: Used for building the command-line interface (CLI). The main entry point for the application (`python -m citrascope start`) is implemented using Click.
79
+ - **Click**: Used for building the command-line interface (CLI). The main entry point for the application (`python -m citrascope`) is implemented using Click.
46
80
  - **Pydantic-Settings**: Manages configuration and settings, ensuring type safety and validation for environment variables.
47
81
  - **Requests** and **HTTPX**: Handle HTTP requests for interacting with the Citra.space API.
48
82
  - **Python-Dateutil**: Provides robust date and time parsing utilities.
@@ -58,9 +92,14 @@ This project relies on several key Python packages. Below are some of the most i
58
92
  - **Flake8**: Enforces code style and linting rules.
59
93
  - **Sphinx**: Generates project documentation.
60
94
 
95
+ ### Web Interface Dependencies
96
+ - **FastAPI**: Modern async web framework for the monitoring interface
97
+ - **Uvicorn**: ASGI server for running the web application
98
+ - **WebSockets**: Real-time bidirectional communication for live updates
99
+
61
100
  For a complete list of dependencies, refer to the `pyproject.toml` file.
62
101
 
63
102
  ## Additional Notes
64
103
  - Keep dependencies minimal and update `pyproject.toml` as needed.
65
- - Document any major changes in `docs/index.md`.
104
+ - Documentation is maintained in the [citra-space/docs](https://github.com/citra-space/docs) repository under `docs/citrascope/`.
66
105
  - Use pre-commit hooks for code quality.
@@ -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
@@ -68,9 +68,6 @@ instance/
68
68
  # Scrapy stuff:
69
69
  .scrapy
70
70
 
71
- # Sphinx documentation
72
- docs/build/
73
-
74
71
  # PyBuilder
75
72
  target/
76
73
 
@@ -81,9 +78,6 @@ target/
81
78
  profile_default/
82
79
  ipython_config.py
83
80
 
84
- # pyenv
85
- .python-version
86
-
87
81
  # pipenv
88
82
  # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
83
  # However, in case of collaboration, if having platform-specific dependencies or dependencies
@@ -141,3 +135,5 @@ citra_image.fits
141
135
  /images/*
142
136
  .DS_Store
143
137
  hip_main.dat
138
+ nina_sequence_*.json
139
+ nina_focus_positions.json
@@ -0,0 +1 @@
1
+ 3.12
@@ -4,20 +4,20 @@
4
4
  "version": "0.2.0",
5
5
  "configurations": [
6
6
  {
7
- "name": "Python: citrascope dev start",
8
- "type": "python",
7
+ "name": "Python: citrascope",
8
+ "type": "debugpy",
9
9
  "request": "launch",
10
10
  "module": "citrascope",
11
- "args": ["--dev", "start"],
11
+ "args": [],
12
12
  "console": "integratedTerminal",
13
13
  "justMyCode": false
14
14
  },
15
15
  {
16
- "name": "Python: citrascope dev start DEBUG logging",
17
- "type": "python",
16
+ "name": "Python: citrascope (custom port)",
17
+ "type": "debugpy",
18
18
  "request": "launch",
19
19
  "module": "citrascope",
20
- "args": ["--dev", "--log-level", "DEBUG", "start"],
20
+ "args": ["--web-port", "8080"],
21
21
  "console": "integratedTerminal",
22
22
  "justMyCode": false
23
23
  }
@@ -1,27 +1,31 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: citrascope
3
- Version: 0.1.0
3
+ Version: 0.4.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: pydantic-settings
12
- Requires-Dist: pyindi-client
13
- Requires-Dist: pytest-cov
11
+ Requires-Dist: platformdirs>=4.0.0
14
12
  Requires-Dist: python-dateutil
15
- Requires-Dist: python-json-logger
16
13
  Requires-Dist: requests
17
14
  Requires-Dist: skyfield
18
- Requires-Dist: types-python-dateutil
15
+ Requires-Dist: uvicorn[standard]>=0.24.0
16
+ Requires-Dist: websockets>=12.0
17
+ Provides-Extra: all
18
+ Requires-Dist: dbus-python; extra == 'all'
19
+ Requires-Dist: pixelemon; extra == 'all'
20
+ Requires-Dist: plotly; extra == 'all'
21
+ Requires-Dist: pyindi-client; extra == 'all'
19
22
  Provides-Extra: build
20
23
  Requires-Dist: build; extra == 'build'
21
24
  Provides-Extra: deploy
22
25
  Requires-Dist: twine; extra == 'deploy'
23
26
  Provides-Extra: dev
24
27
  Requires-Dist: black; extra == 'dev'
28
+ Requires-Dist: bump-my-version; extra == 'dev'
25
29
  Requires-Dist: flake8; extra == 'dev'
26
30
  Requires-Dist: flake8-pytest-style; extra == 'dev'
27
31
  Requires-Dist: isort; extra == 'dev'
@@ -30,10 +34,13 @@ Requires-Dist: mypy; extra == 'dev'
30
34
  Requires-Dist: pre-commit; extra == 'dev'
31
35
  Requires-Dist: pytest; extra == 'dev'
32
36
  Requires-Dist: pytest-cov; extra == 'dev'
33
- Provides-Extra: docs
34
- Requires-Dist: sphinx; extra == 'docs'
35
- Requires-Dist: sphinx-autodoc-typehints; extra == 'docs'
36
- Requires-Dist: sphinx-markdown-builder; extra == 'docs'
37
+ Requires-Dist: types-python-dateutil; extra == 'dev'
38
+ Provides-Extra: indi
39
+ Requires-Dist: pixelemon; extra == 'indi'
40
+ Requires-Dist: plotly; extra == 'indi'
41
+ Requires-Dist: pyindi-client; extra == 'indi'
42
+ Provides-Extra: kstars
43
+ Requires-Dist: dbus-python; extra == 'kstars'
37
44
  Provides-Extra: test
38
45
  Requires-Dist: mockito; extra == 'test'
39
46
  Requires-Dist: pytest; extra == 'test'
@@ -41,68 +48,78 @@ Requires-Dist: pytest-cov; extra == 'test'
41
48
  Description-Content-Type: text/markdown
42
49
 
43
50
  # 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)
51
+ [![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
52
 
46
53
  Remotely control a telescope while it polls for tasks, collects observations, and delivers data for further processing.
47
54
 
48
55
  ## Features
49
-
56
+ - Offers a web UI to configure hardware and connect to Citra.space's api
50
57
  - Connects to Citra.space's API and identifies itself as an online telescope
51
- - Connects to configured INDI telescope and camera hardware
58
+ - Connects to configured telescope and camera hardware
52
59
  - Acts as a task daemon carrying out and remitting photography tasks
53
60
 
61
+ ## Documentation
62
+
63
+ Full documentation is available at [docs.citra.space](https://docs.citra.space/citrascope/).
64
+
65
+ Documentation source is maintained in the [citra-space/docs](https://github.com/citra-space/docs) repository.
66
+
54
67
  ## Installation
55
68
 
56
- Install CitraScope from PyPI:
69
+ **Important:** CitraScope requires Python 3.10, 3.11, or 3.12.
70
+
71
+ ### Check Your Python Version
57
72
 
58
73
  ```sh
59
- pip install citrascope
74
+ python3 --version
60
75
  ```
61
76
 
62
- This provides the `citrascope` command-line tool. To see available commands:
77
+ If you don't have a compatible version, install one with [pyenv](https://github.com/pyenv/pyenv):
63
78
 
64
79
  ```sh
65
- citrascope --help
80
+ pyenv install 3.12.0
81
+ pyenv local 3.12.0 # Sets Python 3.12.0 for the current directory
66
82
  ```
67
83
 
68
- ## Usage
84
+ ### Install CitraScope
69
85
 
70
- Run the CLI tool:
86
+ **Recommended: Using pip in a virtual environment**
71
87
 
72
88
  ```sh
73
- citrascope start
89
+ python3 -m venv citrascope-env
90
+ source citrascope-env/bin/activate # On Windows: citrascope-env\Scripts\activate
91
+ pip install citrascope
74
92
  ```
75
93
 
76
- To connect to the Citra Dev server:
94
+ ### Optional Dependencies
95
+
96
+ For Linux-based telescope control (INDI):
77
97
 
78
98
  ```sh
79
- citrascope start --dev
99
+ pip install citrascope[indi]
80
100
  ```
81
101
 
82
- ## Configuration
102
+ This provides the `citrascope` command-line tool. To see available commands:
83
103
 
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.
104
+ ```sh
105
+ citrascope --help
106
+ ```
85
107
 
86
- Example `.env` file:
108
+ ## Usage
87
109
 
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
- ```
110
+ ### Starting the Daemon
96
111
 
97
- **Variable descriptions:**
112
+ Run the daemon with:
98
113
 
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`)
114
+ ```sh
115
+ citrascope
116
+ ```
104
117
 
105
- You can copy `.env.example` to `.env` and tweak your values.
118
+ By default, this starts the web interface on `http://localhost:24872`. You can customize the port:
119
+
120
+ ```sh
121
+ citrascope --web-port 8080
122
+ ```
106
123
 
107
124
  ## Developer Setup
108
125
 
@@ -112,6 +129,16 @@ By opening this project in VS Code and choosing "Reopen in Container" (or using
112
129
 
113
130
  The devcontainer also ensures all required system dependencies (like `cmake`) are installed automatically.
114
131
 
132
+ ### Python Version
133
+
134
+ 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.
135
+
136
+ ### If not using the dev container:
137
+ ```sh
138
+ python -m venv .venv
139
+ source .venv/bin/activate
140
+ ```
141
+
115
142
  ### Installing Development Dependencies
116
143
 
117
144
  To install development dependencies (for code style, linting, and pre-commit hooks):
@@ -138,20 +165,33 @@ pre-commit run --all-files
138
165
 
139
166
  This ensures code style and quality checks are enforced for all contributors.
140
167
 
168
+ ### Releasing a New Version
169
+
170
+ To bump the version and create a release:
171
+
172
+ ```sh
173
+ bump-my-version bump patch # 0.1.3 → 0.1.4
174
+ bump-my-version bump minor # 0.1.3 → 0.2.0
175
+ bump-my-version bump major # 0.1.3 → 1.0.0
176
+ git push && git push --tags
177
+ ```
178
+
179
+ Then create a release in the GitHub UI from the new tag. This triggers automatic PyPI publishing.
180
+
141
181
  ### Running and Debugging with VS Code
142
182
 
143
183
  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`:
144
184
 
145
- - **Python: citrascope dev start** — Runs the main entry point with development options.
146
- - **Python: citrascope dev start DEBUG logging** — Runs with development options and sets log level to DEBUG for more detailed output.
185
+ - **Python: citrascope** — Runs the daemon with default settings
186
+ - **Python: citrascope (custom port)** — Runs with web interface on port 8080
147
187
 
148
- To use these, open the Run and Debug panel in VS Code, select the desired configuration, and click the Run or Debug button. This is a convenient way to start or debug the app without manually entering commands.
188
+ To use these, open the Run and Debug panel in VS Code, select the desired configuration, and click the Run or Debug button.
149
189
 
150
190
  ## Running Tests
151
191
 
152
192
  This project uses [pytest](https://pytest.org/) for unit testing. All tests are located in the `tests/` directory.
153
193
 
154
- To run tests manually:
194
+ To run unit tests within your devcontainer:
155
195
 
156
196
  ```bash
157
197
  pytest
@@ -0,0 +1,149 @@
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) [![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
+
4
+ Remotely control a telescope while it polls for tasks, collects observations, and delivers data for further processing.
5
+
6
+ ## Features
7
+ - Offers a web UI to configure hardware and connect to Citra.space's api
8
+ - Connects to Citra.space's API and identifies itself as an online telescope
9
+ - Connects to configured telescope and camera hardware
10
+ - Acts as a task daemon carrying out and remitting photography tasks
11
+
12
+ ## Documentation
13
+
14
+ Full documentation is available at [docs.citra.space](https://docs.citra.space/citrascope/).
15
+
16
+ Documentation source is maintained in the [citra-space/docs](https://github.com/citra-space/docs) repository.
17
+
18
+ ## Installation
19
+
20
+ **Important:** CitraScope requires Python 3.10, 3.11, or 3.12.
21
+
22
+ ### Check Your Python Version
23
+
24
+ ```sh
25
+ python3 --version
26
+ ```
27
+
28
+ If you don't have a compatible version, install one with [pyenv](https://github.com/pyenv/pyenv):
29
+
30
+ ```sh
31
+ pyenv install 3.12.0
32
+ pyenv local 3.12.0 # Sets Python 3.12.0 for the current directory
33
+ ```
34
+
35
+ ### Install CitraScope
36
+
37
+ **Recommended: Using pip in a virtual environment**
38
+
39
+ ```sh
40
+ python3 -m venv citrascope-env
41
+ source citrascope-env/bin/activate # On Windows: citrascope-env\Scripts\activate
42
+ pip install citrascope
43
+ ```
44
+
45
+ ### Optional Dependencies
46
+
47
+ For Linux-based telescope control (INDI):
48
+
49
+ ```sh
50
+ pip install citrascope[indi]
51
+ ```
52
+
53
+ This provides the `citrascope` command-line tool. To see available commands:
54
+
55
+ ```sh
56
+ citrascope --help
57
+ ```
58
+
59
+ ## Usage
60
+
61
+ ### Starting the Daemon
62
+
63
+ Run the daemon with:
64
+
65
+ ```sh
66
+ citrascope
67
+ ```
68
+
69
+ By default, this starts the web interface on `http://localhost:24872`. You can customize the port:
70
+
71
+ ```sh
72
+ citrascope --web-port 8080
73
+ ```
74
+
75
+ ## Developer Setup
76
+
77
+ 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.
78
+
79
+ By opening this project in VS Code and choosing "Reopen in Container" (or using the Dev Containers extension), you can develop and run the project seamlessly, regardless of your host OS.
80
+
81
+ The devcontainer also ensures all required system dependencies (like `cmake`) are installed automatically.
82
+
83
+ ### Python Version
84
+
85
+ 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.
86
+
87
+ ### If not using the dev container:
88
+ ```sh
89
+ python -m venv .venv
90
+ source .venv/bin/activate
91
+ ```
92
+
93
+ ### Installing Development Dependencies
94
+
95
+ To install development dependencies (for code style, linting, and pre-commit hooks):
96
+
97
+ ```sh
98
+ pip install '.[dev]'
99
+ ```
100
+
101
+ ### Setting up Pre-commit Hooks
102
+
103
+ This project uses [pre-commit](https://pre-commit.com/) to run code quality checks (like Flake8, Black, isort, etc.) automatically before each commit.
104
+
105
+ After installing the dev dependencies, enable the hooks with:
106
+
107
+ ```sh
108
+ pre-commit install
109
+ ```
110
+
111
+ You can manually run all pre-commit checks on all files with:
112
+
113
+ ```sh
114
+ pre-commit run --all-files
115
+ ```
116
+
117
+ This ensures code style and quality checks are enforced for all contributors.
118
+
119
+ ### Releasing a New Version
120
+
121
+ To bump the version and create a release:
122
+
123
+ ```sh
124
+ bump-my-version bump patch # 0.1.3 → 0.1.4
125
+ bump-my-version bump minor # 0.1.3 → 0.2.0
126
+ bump-my-version bump major # 0.1.3 → 1.0.0
127
+ git push && git push --tags
128
+ ```
129
+
130
+ Then create a release in the GitHub UI from the new tag. This triggers automatic PyPI publishing.
131
+
132
+ ### Running and Debugging with VS Code
133
+
134
+ 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`:
135
+
136
+ - **Python: citrascope** — Runs the daemon with default settings
137
+ - **Python: citrascope (custom port)** — Runs with web interface on port 8080
138
+
139
+ To use these, open the Run and Debug panel in VS Code, select the desired configuration, and click the Run or Debug button.
140
+
141
+ ## Running Tests
142
+
143
+ This project uses [pytest](https://pytest.org/) for unit testing. All tests are located in the `tests/` directory.
144
+
145
+ To run unit tests within your devcontainer:
146
+
147
+ ```bash
148
+ pytest
149
+ ```
@@ -0,0 +1,23 @@
1
+ import click
2
+
3
+ from citrascope.citra_scope_daemon import CitraScopeDaemon
4
+ from citrascope.constants import DEFAULT_WEB_PORT
5
+ from citrascope.settings.citrascope_settings import CitraScopeSettings
6
+
7
+
8
+ @click.command()
9
+ @click.option(
10
+ "--web-port",
11
+ default=DEFAULT_WEB_PORT,
12
+ type=int,
13
+ help=f"Web server port (default: {DEFAULT_WEB_PORT})",
14
+ )
15
+ def cli(web_port):
16
+ """CitraScope daemon - configure via web UI at http://localhost:24872"""
17
+ settings = CitraScopeSettings(web_port=web_port)
18
+ daemon = CitraScopeDaemon(settings)
19
+ daemon.run()
20
+
21
+
22
+ if __name__ == "__main__":
23
+ 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
@@ -27,7 +41,19 @@ class CitraApiClient(AbstractCitraApiClient):
27
41
  return resp.json()
28
42
  except httpx.HTTPStatusError as e:
29
43
  if self.logger:
30
- self.logger.error(f"HTTP error: {e.response.status_code} {e.response.text}")
44
+ # Check if response is HTML (e.g., Cloudflare error pages)
45
+ content_type = e.response.headers.get("content-type", "")
46
+ response_text = e.response.text
47
+
48
+ if "text/html" in content_type or response_text.strip().startswith("<"):
49
+ # Log only status and a brief message for HTML responses, sometimes we get Cloudflare error pages
50
+ self.logger.error(
51
+ f"HTTP error: {e.response.status_code} - "
52
+ f"Received HTML error page (likely Cloudflare or server error) for {method} {endpoint}"
53
+ )
54
+ else:
55
+ # Log full response for non-HTML errors (JSON, plain text, etc.)
56
+ self.logger.error(f"HTTP error: {e.response.status_code} {response_text}")
31
57
  return None
32
58
  except Exception as e:
33
59
  if self.logger:
@@ -57,8 +83,10 @@ class CitraApiClient(AbstractCitraApiClient):
57
83
 
58
84
  def upload_image(self, task_id, telescope_id, filepath):
59
85
  """Upload an image file for a given task."""
86
+ file_size = os.path.getsize(filepath)
60
87
  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}"
88
+ "POST",
89
+ f"/my/images?filename=citra_task_{task_id}_image.fits&telescope_id={telescope_id}&task_id={task_id}&file_size={file_size}",
62
90
  )
63
91
  if not signed_url_response or "uploadUrl" not in signed_url_response:
64
92
  if self.logger:
@@ -99,3 +127,16 @@ class CitraApiClient(AbstractCitraApiClient):
99
127
  if self.logger:
100
128
  self.logger.error(f"Failed to mark task {task_id} as complete: {e}")
101
129
  return None
130
+
131
+ def mark_task_failed(self, task_id):
132
+ """Mark a task as failed using the API."""
133
+ try:
134
+ body = {"status": "Failed"}
135
+ response = self._request("PUT", f"/tasks/{task_id}", json=body)
136
+ if self.logger:
137
+ self.logger.debug(f"Marked task {task_id} as failed: {response}")
138
+ return response
139
+ except Exception as e:
140
+ if self.logger:
141
+ self.logger.error(f"Failed to mark task {task_id} as failed: {e}")
142
+ return None