Skip to content

Allsky Server API

The Allsky Server is a separate process that runs alongside Allsky its primary job is to handle longer running processes and to manage GPIO Coontrol. Its core functions are

  • The Allsky dashboard whcich displays information about Allsky - currently under development
  • The Allsky focuser which allows a camera with a focus motor to be focused - currently under development
  • The Pi's GPIO pins, both digital and PWM

Authentication

The API requires authentication under the following circumstances

  • You are unsing the inbuilt Dashboard, in this case the authentication will be via a login screen using the same login and password as Allaky
  • You are using the api from an external machine. In this case you will need to request a JWT token from the API

If you are using the API from the local machine then no authentication is required for any API

Requesting a JWT token

To get a JWT token use the following API endpoint

POST /login

This expects a username and password and returns a JWT token

TODO: Add Hoppscotch examples TODO: Creating users

The Available API's

allsky

allsky_status()

GET /allsky/status

Retrieve the current Allsky system status.

This endpoint queries backend logic (get_allsky_status) and returns a full status dictionary, plus a rendered HTML fragment used by the web frontend.

Authentication

Requires: api_auth_required("allsky", "read")

Returns:

Name Type Description
dict

A JSON object containing: - All fields provided by get_allsky_status() - status_html: Rendered HTML snippet for UI display

Source code in modules/allsky/__init__.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@allsky_bp.route('/status', methods=['GET'])
@api_auth_required("allsky", "read")
def allsky_status():
    """
    GET /allsky/status

    Retrieve the current Allsky system status.

    This endpoint queries backend logic (get_allsky_status) and returns a full
    status dictionary, plus a rendered HTML fragment used by the web frontend.

    Authentication:
        Requires: ``api_auth_required("allsky", "read")``

    Returns:
        dict: A JSON object containing:
            - All fields provided by ``get_allsky_status()``
            - ``status_html``: Rendered HTML snippet for UI display
    """
    status = get_allsky_status()
    status_html = render_template("_partials/_allskyStatus.html", status=status)
    status['status_html'] = status_html
    return status

restart_allsky()

GET /allsky/restart

Restart the Allsky service.

Authentication

Requires: api_auth_required("allsky", "update")

Returns:

Name Type Description
JSON

{"status": "Allsky started"}

Source code in modules/allsky/__init__.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
@allsky_bp.route('/restart', methods=['GET'])
@api_auth_required("allsky", "update")
def restart_allsky():
    """
    GET /allsky/restart

    Restart the Allsky service.

    Authentication:
        Requires: ``api_auth_required("allsky", "update")``

    Returns:
        JSON: ``{"status": "Allsky started"}``
    """
    subprocess.run(['sudo', 'systemctl', 'restart', 'allsky'])
    return jsonify({'status': 'Allsky started'})

start_allsky()

GET /allsky/start

Start the Allsky service.

Authentication

Requires: api_auth_required("allsky", "update")

Returns:

Name Type Description
JSON

{"status": "Allsky started"}

Source code in modules/allsky/__init__.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
@allsky_bp.route('/start', methods=['GET'])
@api_auth_required("allsky", "update")
def start_allsky():
    """
    GET /allsky/start

    Start the Allsky service.

    Authentication:
        Requires: ``api_auth_required("allsky", "update")``

    Returns:
        JSON: ``{"status": "Allsky started"}``
    """
    subprocess.run(['sudo', 'systemctl', 'start', 'allsky'])
    return jsonify({'status': 'Allsky started'})

stop_allsky()

GET /allsky/stopallsky

Stop the Allsky service and immediately start the AllskyServer service.

This is commonly used during debugging or maintenance when the capture component needs to be restarted but the web interface should remain active.

Authentication

Requires: api_auth_required("allsky", "update")

Returns:

Name Type Description
JSON

{"status": "Allsky stopped"}

Source code in modules/allsky/__init__.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@allsky_bp.route('/stopallsky', methods=['GET'])
@api_auth_required("allsky", "update")
def stop_allsky():
    """
    GET /allsky/stopallsky

    Stop the Allsky service and immediately start the AllskyServer service.

    This is commonly used during debugging or maintenance when the capture
    component needs to be restarted but the web interface should remain active.

    Authentication:
        Requires: ``api_auth_required("allsky", "update")``

    Returns:
        JSON: ``{"status": "Allsky stopped"}``
    """
    subprocess.run(['sudo', 'systemctl', 'stop', 'allsky'])
    subprocess.run(['sudo', 'systemctl', 'start', 'allskyserver'])
    return jsonify({'status': 'Allsky stopped'})

stop_allsky_all()

GET /allsky/stopall

Stop the Allsky service.

This fully stops the "allsky" systemd service.

Authentication

Requires: api_auth_required("allsky", "update")

Returns:

Name Type Description
JSON

{"status": "Allsky stopped"}

Source code in modules/allsky/__init__.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@allsky_bp.route('/stopall', methods=['GET'])
@api_auth_required("allsky", "update")
def stop_allsky_all():
    """
    GET /allsky/stopall

    Stop the Allsky service.

    This fully stops the "allsky" systemd service.

    Authentication:
        Requires: ``api_auth_required("allsky", "update")``

    Returns:
        JSON: ``{"status": "Allsky stopped"}``
    """
    subprocess.run(['sudo', 'systemctl', 'stop', 'allsky'])
    return jsonify({'status': 'Allsky stopped'})

gpio

get_gpio_count()

Return the number of GPIO lines exposed by the active chip.

Source code in modules/gpio/__init__.py
50
51
52
53
54
55
56
57
58
59
60
def get_gpio_count():
    """
    Return the number of GPIO lines exposed by the active chip.
    """
    handle = get_gpiochip_handle()
    del handle  # handle is only used to ensure the chip is opened

    if gpiochip_info is None:
        return 0

    return int(gpiochip_info[1])

get_gpio_status()

Inspect all available GPIO lines and return their current usage.

Returns:

Name Type Description
dict dict

Status for each Dxx pin on the board.

Source code in modules/gpio/__init__.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
def get_gpio_status() -> dict:
    """
    Inspect all available GPIO lines and return their current usage.

    Returns:
        dict: Status for each Dxx pin on the board.
    """
    all_status = {}
    gpio_count = get_gpio_count()

    for pin_num in range(gpio_count):
        pin = str(pin_num)
        status = {"mode": "unused"}

        if pin in digital_pins:
            info = digital_pins[pin]
            direction = info["direction"]
            status["mode"] = f"digital-{direction}"
            status["value"] = "on" if _read_pin_value(pin_num) else "off"
        elif pin in pwm_pins:
            pwm = pwm_pins[pin]
            status["mode"] = "pwm"
            status["frequency"] = pwm["frequency"]
            status["duty"] = pwm["duty"]

        all_status[f"D{pin_num}"] = status

    return all_status

get_gpiochip_handle()

Lazily open the first available gpiochip.

Returns:

Name Type Description
int

lgpio handle for the active gpiochip.

Source code in modules/gpio/__init__.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def get_gpiochip_handle():
    """
    Lazily open the first available gpiochip.

    Returns:
        int: lgpio handle for the active gpiochip.
    """
    global gpiochip_handle, gpiochip_info

    if gpiochip_handle is not None:
        return gpiochip_handle

    last_error = None
    for chip in range(8):
        try:
            handle = lgpio.gpiochip_open(chip)
            info = lgpio.gpio_get_chip_info(handle)
            gpiochip_handle = handle
            gpiochip_info = info
            return gpiochip_handle
        except Exception as exc:
            last_error = exc

    raise RuntimeError(f"Unable to open a gpiochip: {last_error}")

normalise_pin(gpio_str)

Validate and normalise a GPIO pin number.

Parameters:

Name Type Description Default
gpio_str str | int

Pin number, e.g. "18".

required

Returns:

Type Description

tuple[str, int]: String form and integer form of the GPIO number.

Source code in modules/gpio/__init__.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def normalise_pin(gpio_str):
    """
    Validate and normalise a GPIO pin number.

    Args:
        gpio_str (str | int): Pin number, e.g. "18".

    Returns:
        tuple[str, int]: String form and integer form of the GPIO number.
    """
    pin = str(gpio_str).strip()

    try:
        pin_num = int(pin)
    except (TypeError, ValueError):
        raise ValueError(f"Invalid GPIO pin {gpio_str}")

    if pin_num < 0 or pin_num >= get_gpio_count():
        raise ValueError(f"Invalid GPIO pin {gpio_str}")

    return pin, pin_num