Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/qa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ jobs:
npx pyright@latest
- name: Run tests
run: |
pytest ./tests --ignore=tests/test_integration.py
pytest ./tests --ignore-glob='./tests/integration/**'
28 changes: 21 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
yyyy/mm/dd Version x.y.z-1
yyyy/mm/dd Version 5.0.0-1
------------------------
- Include generated DBus interface docs
- Fix links in documentation
- CLI respects `CRYSTALFONTZ_CONFIG_FILE` environment variable
- Remove `tox` from development tools
- Consistently licensed as MPL-2.0
- Improved PyPI classifiers
- CLI changes:
- Main CLI respects `PLUSDECK_CONFIG_FILE` environment variable
- **BREAKING:** `plusdeck`, `plusdeckd` and `plusdeckctl` have been removed from the Python package in favor of `python3 -m plusdeck`, `python3 -m plusdeck.dbus.service` and `python3 -m plusdeck.dbus.client`, respectively
- Optional alias scripts for `plusdeck`, `plusdeck-service` and `plusdeck-dbus` included in the `./bin` folder
- DBus service CLI includes a `--system/--user` flag for explicitly selecting the bus
- **BREAKING:** DBus client CLI now uses `--user/--default` flag for selecting the bus
- DBus API Changes:
- **BREAKING:** Root `plusdeck.dbus` no longer includes convenience exports
- Addition of `plusdeck.dbus.domain` module for domain mapping
- Addition of `plusdeck.dbus.select` module for selecting the DBus bus
- Testing changes:
- Additional integration test for DBus
- Remove `tox` from development tools
- Documentation improvements:
- Include generated DBus interface docs
- Fix links in documentation
- General overhaul based on lessons from `crystalfontz`
- Packaging & Licensing
- **BREAKING:** Consistently licensed as MPL-2.0
- Improved PyPI classifiers

2025/02/09 Version 4.0.1-1
--------------------------
Expand Down
3 changes: 3 additions & 0 deletions bin/plusdeck
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/sh

exec python3 -m plusdeck "$@"
3 changes: 3 additions & 0 deletions bin/plusdeck-dbus
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/sh

exec python3 -m plusdeck.dbus.client "$@"
3 changes: 3 additions & 0 deletions bin/plusdeck-service
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/sh

exec python3 -m plusdeck.dbus.service "$@"
12 changes: 10 additions & 2 deletions docs/dialout.md → docs/access.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Dialout
# Access and Permissions

In Linux, by default, accessing serial ports requires `sudo`. This is inconvenient - you probably want your current user to have acccess without using `sudo`.
In Linux, by default, accessing serial ports requires `sudo`. This is inconvenient - you probably want your current user to have access without using `sudo`. There are two ways to manage this access: either through the DBus service, or with the `dialout` group.

## DBus

The DBus service has the advantage of more fine-grained access control. However, it is only supported in Linux.

For more information, see [DBus Access and Permissions](./dbus/access.md)

## Dialout

Typically, Linux serial ports are generally owned by `root` and are attached to the `dialout` group, with permissions such that members of the `dialout` group may read and write to the port.

Expand Down
7 changes: 0 additions & 7 deletions docs/api/plusdeck.dbus.md

This file was deleted.

46 changes: 46 additions & 0 deletions docs/api/plusdeck.dbus/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# DBus API Overview

The `plusdeck` library includes a DBus service and client. This service allows for multitenancy on Linux - the centralized service controls the serial bus, and clients - including `python3 -m plusdeck.dbus.client` - can connect to the service.

The DBus APIs largely depend on the [sdbus](https://pypi.org/project/sdbus/) Python library, which in turn depends on the [sd-bus](https://www.freedesktop.org/software/systemd/man/latest/sd-bus.html) library. This means that, effectively, the DBus API is only available on Linux. `sdbus` is therefore an optional dependency, under the `dbus` extra.

As a consequence, the DBus API is (unlike the primary `plusdeck` API) not re-exported at the top level. This is to make it viable to run unit tests for parts of the DBus API which don't strictly depend on `sdbus`, namely tests for `plusdeck.dbus.domain`.

For information on the DBus service and client CLI, check out [the core DBus documentation](../../dbus/index.md).

## plusdeck.dbus.client

This module contains the core `DbusClient` class. This class is used for interacting with a live DBus service. This is where most users will want to start.

For more information, view the API docs for [`plusdeck.dbus.client`](./plusdeck.dbus.client.md).

## plusdeck.dbus.domain

The DBus interface uses DBus compatible types, rather than the standard `plusdeck` domain objects. The domain module contains type aliases and mapper classes for converting to and from `plusdeck` domain objects and DBus types. While not strictly necessary for using the client, it's highly recommended.

For more information, view the API docs for [`plusdeck.dbus.domain`](./plusdeck.dbus.domain.md).

## plusdeck.dbus.config

Configuration for the DBus service is a little different than for the serial client. This is because the DBus service doesn't live reload a config after it changes. In other words, if you edit the config file, the DBus service's loaded config will show drift. This module helps track the drift between these sources.

For more information, view the API docs for [`plusdeck.dbus.config`](./plusdeck.dbus.config.md).

## plusdeck.dbus.service

This module contains abstractions for running the DBus service. It will typically be used through [the service CLI](../../dbus/service.md). But this module may be useful for users wishing to embed it in another program.

For more information, view the API docs for [`plusdeck.dbus.service`](./plusdeck.dbus.service.md).

## plusdeck.dbus.select

This module contains convenience functions for configuring which bus the program uses.

For more information, view the API docs for [`plusdeck.dbus.select`](./plusdeck.dbus.select.md).

## plusdeck.dbus.interface

This module contains the core `DbusInterface` class. This is used directly when serving the interface, and subclassed by the client.

For more information, view the API docs for [`plusdeck.dbus.interface`](./plusdeck.dbus.interface.md).

7 changes: 7 additions & 0 deletions docs/api/plusdeck.dbus/plusdeck.dbus.client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# plusdeck.dbus.client

This module contains the core `DbusClient` abstraction, which is used to interact with a DBus service.

For information on how to use `plusdeck` domain objects with the DBus client, see [the API docs for `plusdeck.dbus.domain`](./plusdeck.dbus.domain.md). For examples of how to use the DBus client, look at [the source code for `plusdeckctl`](https://github.com/jfhbrook/plusdeck/blob/main/plusdeck/dbus/client/cli.py).

::: plusdeck.dbus.client
3 changes: 3 additions & 0 deletions docs/api/plusdeck.dbus/plusdeck.dbus.config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# plusdeck.dbus.config

::: plusdeck.dbus.config
7 changes: 7 additions & 0 deletions docs/api/plusdeck.dbus/plusdeck.dbus.domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# plusdeck.dbus.domain

DBus uses a collection of types that are documented [in the specification](https://dbus.freedesktop.org/doc/dbus-specification.html#basic-types). The base types are more fine-grained than Python's base types, but are non-nullable - ie, there's no `None` type in DBus. Moreover, its collection types don't map cleanly to arbitrary Python class instances - rather, you get basic structs (which correspond to Python tuples) and arrays (which correspond to Python lists). This means that, when interacting with the DBus client, you will need to work with types *other* than the domain objects used in the standard `plusdeck` client.

To facilitate this, `plusdeck` includes a submodule for mapping between domain objects and DBus types, at `plusdeck.dbus.domain`. This module exports both aliases for the types used by the DBus interface, and mapper classes for packing and unpacking domain objects to and from DBus types.

::: plusdeck.dbus.domain
7 changes: 7 additions & 0 deletions docs/api/plusdeck.dbus/plusdeck.dbus.interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# plusdeck.dbus.interface

This module contains the core `DbusInterface` class. This class is used directly to serve the interface, and is subclassed by the `DbusClient` class in `plusdeck.dbus.client`.

You will likely not use this module directly. For information on the client, see [`plusdeck.dbus.client`](./plusdeck.dbus.client.md). For information on the service, see [`plusdeck.dbus.service`](./plusdeck.dbus.service.md).

::: plusdeck.dbus.interface
3 changes: 3 additions & 0 deletions docs/api/plusdeck.dbus/plusdeck.dbus.select.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# plusdeck.dbus.select

::: plusdeck.dbus.select
3 changes: 3 additions & 0 deletions docs/api/plusdeck.dbus/plusdeck.dbus.service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# plusdeck.dbus.service

::: plusdeck.dbus.service
14 changes: 11 additions & 3 deletions docs/cli.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Command Line Interface

The `plusdeck` CLI allows for direct control of the Plus Deck 2C over its serial port:
This library has a CLI, which you can run like so:

```sh
$ plusdeck --help
Usage: plusdeck [OPTIONS] COMMAND [ARGS]...
$ python3 -m plusdeck --help
Usage: python3 -m plusdeck [OPTIONS] COMMAND [ARGS]...

Control your Plus Deck 2C tape deck.

Expand Down Expand Up @@ -32,3 +32,11 @@ Commands:
```

For more information, use the `--help` flag for any command.

## Output Format

This CLI supports two output formats: `text` and `json`. The former will output a human-readable format, and the latter will output JSON. This is mostly relevant for the `subscribe` command.

## Installing the `plusdeck` Shim

Included in this project is `./bin/plusdeck`, a script that you can add to your PATH for convenience.
8 changes: 4 additions & 4 deletions docs/dbus/access.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Dbus Access Policies
# DBus Access and Permissions

When running services under the `system` bus, care must be taken to manage access policies. Dbus does this primarily with [an XML-based policy language](https://dbus.freedesktop.org/doc/dbus-daemon.1.html). Systemd additionally manages access to privileged methods, seemingly with the intent of delegating to polkit.

By default, Dbus is configured with the following policies:

* The root user may own the bus, and send and receive messages from `org.jfhbrook.plusdeck`
* Users in the `plusdeck` Unix group may additionally send and receive messages from `org.jfhbrook.plusdeck`
- The root user may own the bus, and send and receive messages from `org.jfhbrook.plusdeck`
- Users in the `plusdeck` Unix group may additionally send and receive messages from `org.jfhbrook.plusdeck`

This means that, if the service is running, `sudo plusdeckctl` commands should always work; and that if your user is in the `plusdeck` Unix group, Dbus will allow for unprivileged `plusdeckctl` commands as well. You can create this group and add yourself to it by running:
This means that, if the service is running, `sudo plusdeckctl` commands should always work; and that if your user is in the `plusdeck` Unix group, Dbus will allow for `plusdeckctl` commands as well. You can create this group and add yourself to it by running:

```bash
sudo groupadd plusdeck
Expand Down
20 changes: 12 additions & 8 deletions docs/dbus/client.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# DBus Client CLI

Assuming `plusdeckd` is running, you may interact with the service using `plusdeckctl`:
Assuming the DBus service is running, you may interact with the service using the client CLI:

```sh
$ plusdeckctl --help
Usage: plusdeckctl [OPTIONS] COMMAND [ARGS]...
$ python3 -m plusdeck.dbus.client --help
Usage: python3 -m plusdeck.dbus.client [OPTIONS] COMMAND [ARGS]...

Control your Plus Deck 2C Cassette Drive through dbus.

Options:
--log-level [DEBUG|INFO|WARNING|ERROR|CRITICAL]
Set the log level
--output [text|json] Output either human-friendly text or JSON
--user / --no-user Connect to the user bus
--user / --default Connect to either the user or default bus
--help Show this message and exit.

Commands:
Expand All @@ -28,8 +28,12 @@ Commands:
subscribe Subscribe to state changes
```

The interface is similar to the vanilla `plusdeck` CLI. However, there are a few differences:
The interface is similar to the vanilla CLI. However, there are a few differences:

1. By default, `plusdeckctl` will connect to the `system` bus. To connect to the local bus, set the `--user` flag.
2. Configuration commands do not reload `plusdeckctl`'s configuration. Instead, they will update the relevant config file, and show the differences between the file config and the service's loaded config.
3. If the config file isn't owned by the user, `plusdeckctl` will attempt to run editing commands with `sudo`.
1. By default, the DBus client CLI will connect to the default bus. To connect to the user session bus, set the `--user` flag. To connect to the system bus, set the `--system` flag.
2. Configuration commands do not reload the service's configuration. Instead, they will update the relevant config file, and show the differences between the file config and the service's loaded config.
3. If the config file isn't owned by the user, the client CLI will attempt to run editing commands with `sudo`.

## Installing the `plusdeck-dbus` Shim

Included in this project is `./bin/plusdeck-dbus`, a script that you can add to your PATH for convenience. If you primarily interact with the device through DBus, you may want to name this `plusdeck` on your system.
4 changes: 2 additions & 2 deletions docs/dbus/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# DBus Support

The `plusdeck` library includes a DBus interface, service and client. This service allows for multitenancy on Linux - the centralized service controls the serial bus, and clients (including `plusdeckctl`) can connect to the service.
The `plusdeck` library includes a DBus interface, service and client. This service allows for multitenancy on Linux - the centralized service controls the serial bus, and clients (including `python3 -m plusdeck.dbus.client`) can connect to the service.

For information on the API, visit [the API docs for `plusdeck.dbus`](../api/plusdeck.dbus.md).
For information on the API, visit [the API docs for dbus](../api/plusdeck.dbus/index.md):
18 changes: 11 additions & 7 deletions docs/dbus/service.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# DBus Service

`plusdeck` includes a dbus service, which can be started with the `plusdeckctl` CLI tool.
`plusdeck` includes a dbus service, which can be started either through SystemD or directly through the command line.

## Starting the Service
## Starting the Service with SystemD

`plusdeck` ships with a systemd unit that configures the service as a Dbus service. To set up the service, run:

Expand All @@ -13,21 +13,25 @@ sudo systemctl start plusdeck # optional

This unit will start on the `system` bus, under the root user.

## Running `plusdeckd` Directly
## Running the Service Directly

The DBus service can be launched directly using `plusdeckd`:
The DBus service can be launched directly using `python3 -m plusdeck.dbus.service`:

```sh
$ plusdeckd --help
Usage: plusdeckd [OPTIONS]
$ python3 -m plusdeck.dbus.service --help
Usage: python3 -m plusdeck.dbus.service [OPTIONS]

Expose the Plus Deck 2C PC Cassette Deck as a DBus service.

Options:
--global / --no-global Load the global config file at
/etc/plusdeck.yaml (default true when
called with sudo)
-C, --config-file PATH A path to a config file
--log-level [DEBUG|INFO|WARNING|ERROR|CRITICAL]
Set the log level
--user / --system Connect to either the user or system bus
--help Show this message and exit.
```

In most cases, this can be called without arguments. By default, `plusdeckd` will listen on the `system` bus and load the global config file (`/etc/plusdeck.yml`) if launched as root; and otherwise listen on the `user` bus and load the user's config file (`~/.config/plusdeck.yml`).
In most cases, this can be called without arguments. By default, the service will listen on the `system` bus and load the global config file (`/etc/plusdeck.yml`) if launched as root; and otherwise listen on the `user` bus and load the user's config file (`~/.config/plusdeck.yml`).
35 changes: 33 additions & 2 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@
### Interactive

- `run` - Thin wrapper around `uv run`
- `start` - Run `plusdeck` CLI
- `jupyterlab` - Run jupyterlab
- `client` - Run `plusdeck` serial client CLI
- `service` - Run `plusdeck.dbus.service` DBus service on user session bus
- `dbus-client` - Run `plusdeck.dbus.client` DBus client CLI on user session bus
- `console` - Run a Python repl
- `shell` - Start a bash shell with a sourced virtualenv

### Other
Expand All @@ -56,6 +58,35 @@
- `docs` - Serve the mkdocs documentation
- `publish` - Run all publish tasks

## Integration Tests

You can run integration tests with `just integration`. It optionally takes arguments. You can show them with `just integration --help`:

```
./scripts/integration.sh --help
USAGE: ./scripts/integration.sh [OPTIONS] [COMPONENTS]

Run integration tests for the supplied components. By default, runs client tests.

Components:
client Run plusdeck client integration tests
dbus Start plusdeck dbus integration tests

Options:
--help Show this help text
--snapshot-update Update snapshots
--system Run any dbus tests against the system bus

Other options are passed to pytest.

Environment:
PLUSDECK_CONFIG_FILE Use an alternative config file. The default is
./tests/fixtures/plusdeck.yaml.
PLUSDECK_LOG_LEVEL
```

To run standard client tests, you can do `just integration`. For DBus tests, do `just integration dbus`.

## CHANGELOG.md

When submitting features, be sure to update the changelog!
20 changes: 19 additions & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Install

`plusdeck` is released as a PyPI package, a series of COPR packages, and a GitHub release.

## Python Package

`plusdeck` is a Python package, and therefore can be installed [from PyPi](https://pypi.org/project/plusdeck/), for instance with `pip`:

```sh
Expand All @@ -12,9 +16,23 @@ To install support for DBus, run:
pip install plusdeck[dbus]
```

In addition, I have a Fedora package on COPR, which can be installed like so:
This package contains the Python library, with the CLIs exposed with Python's `-m` flag (ie. `python3 -m plusdeck`).

## COPR Packages

I package `plusdeck` for Fedora on COPR. It can be installed like so:

```sh
sudo dnf copr enable jfhbrook/joshiverse
sudo dnf install plusdeck
```

This package installs the Python package via `python-plusdeck`, configures the systemd service, and includes a bin called `plusdeck` that wraps `python3 -m plusdeck.dbus.client`.

## GitHub Release

`plusdeck` is also published as a GitHub release:

<https://github.com/jfhbrook/plusdeck/releases>

These releases simply contain packaged source code, and will mostly be useful for package authors.
Loading