RabbitMQ (AMQP) connector for the reMarkable API
The connector is configured with environment variables:
AMQP_HOST
: RabbitMQ hostAMQP_USER
: RabbitMQ userAMQP_PASS
: RabbitMQ passwordAMQP_VHOST
: RabbitMQ virtual host, defaults to '/'AMQP_EXCHANGE
: RabbitMQ binding key to choose the exchange, defaults torabbarkable
RMAPI_DEVICETOKEN
: reMarkable device tokenPORT
: Port for the HTTP endpoint (default8080
, only change when running locally!)
To obtain the reMarkable device token, you need to register an application with rmapi and extract it from your ~/.config/rmapi.conf
file.
The connector is intended to be run as a Docker container:
docker run --rm \
-p 8080:8080 \
--env-file .env \
mrtux/rabbarkable
This project uses the Micronaut Framework.
Version numbers are determined with jgitver.
Please check your IDE settings to avoid problems, as there are still some unresolved issues.
If you encounter a project version 0
there is an issue with the jgitver generator.
To use the configuration from a .env
file, run using dotenv:
dotenv ./mvnw mn:run
The build is split into two stages:
- Packaging with Maven
- Building the Docker container
This means that the Dockerfile expects one (and only one) JAR file in the target directory. Build as follows:
mvn --batch-mode --update-snapshots clean package
docker build .
The process is coded in the docker-publish workflow and only needs to be executed manually for local builds.
A more detailed description on the reMarkable API will follow. The development has heavily benefited from the following documents:
- https://docs.google.com/document/d/1peZh79C2BThlp2AC3sITzinAQKJccQ1gn9ppdCIWLl8
- https://akeil.de/posts/remarkable-cloud-api/
Please note that I currently have only one account and device to test. There seem to have been massive changes to the API recently, so please let me know if this implementation does not work. I am happy to sort things out and, on the way, improve this project.
If the reMarkable API sends a SyncComplete event on the WebSocket (this seems to be the only event left), a similar
event is sent to the AMQP_EXCHANGE
with the following JSON form:
{
"timestamp": "<ISO8601 timestamp>",
"root": "<GCS id of root document>",
"device-id": "<ID of the device that initiated the sync>",
"type": "sync complete"
}
The fields type
and device-id
are added as headers (x-type
, x-device-id
) to allow value-based binding in a header exchange.
Please note that the type
is a literal value that signifies a synchronization event.
If the root GCS id is used immediately it may be used to look up the root index. Since there are already lots of API calls invoked, it may be safer to trigger the lookup in conjunction with an index download. The root GCS id is mainly intended for cache validation.
Regarding Reliability: Experiments have shown that the WebSocket implementation cannot detect link failures and will happily go on until an external timeout is sent (see #15). During this period, no notifications are received. In this sense SyncComplete events should rather be used to improve latency in a polling observation mode, and not as the lone source of updates.
- Stefan Haun (@penguineer)
PRs are welcome!
If possible, please stick to the following guidelines:
- Keep PRs reasonably small and their scope limited to a feature or module within the code.
- If a large change is planned, it is best to open a feature request issue first, then link subsequent PRs to this issue, so that the PRs move the code towards the intended feature.
MIT © 2022 Stefan Haun and contributors