Compare commits

...

49 Commits
v0.2.1 ... main

Author SHA1 Message Date
Paul van Tilburg 03c51e2a2c
Bump the version to 0.3.5
Check and lint using Cargo / Check and lint (push) Successful in 2m10s Details
Release / Release (push) Successful in 1m14s Details
Release / Release Rust crate (push) Successful in 2m53s Details
Release / Release Docker image (push) Successful in 8m41s Details
2024-02-27 15:37:56 +01:00
Paul van Tilburg cc1a0e4748
Update the changelog 2024-02-27 15:37:35 +01:00
Paul van Tilburg d2b90c16f6
Fix clippy issue 2024-02-27 15:37:05 +01:00
Paul van Tilburg d5eed08072
Bump the version to 0.3.4
Check and lint using Cargo / Check and lint (push) Has been cancelled Details
Release / Release (push) Has been cancelled Details
Release / Release Rust crate (push) Has been cancelled Details
Release / Release Docker image (push) Has been cancelled Details
2024-02-27 15:28:09 +01:00
Paul van Tilburg 247e9d51f5
Update the changelog 2024-02-27 15:27:21 +01:00
Paul van Tilburg 5241d90e79
Cargo update; fixes several security advisories
Fixes RUSTSEC-2024-0003 and RUSTSEC-2023-0072
2024-02-27 15:25:21 +01:00
Paul van Tilburg c61bbfef5b
Bump the version to 0.3.3
Check and lint using Cargo / Check and lint (push) Successful in 2m35s Details
Release / Release (push) Successful in 1m14s Details
Release / Release Rust crate (push) Successful in 3m28s Details
Release / Release Docker image (push) Successful in 11m19s Details
2023-11-03 21:25:03 +01:00
Paul van Tilburg f3ff02e4ff
Update the changelog 2023-11-03 21:24:22 +01:00
Paul van Tilburg f2be089fb9
Add missing dates 2023-11-03 21:16:26 +01:00
Paul van Tilburg 45b3f52e71
Cargo update; fixes RUSTSEC-2020-0071
Fix the usage of a deprecated method.
2023-11-03 21:12:04 +01:00
Paul van Tilburg ff12875a08
Bump the version to 0.3.2
Check and lint using Cargo / Check and lint (push) Successful in 2m41s Details
Release / Release (push) Successful in 1m20s Details
Release / Release Rust crate (push) Successful in 3m46s Details
Release / Release Docker image (push) Successful in 8m18s Details
2023-08-27 13:30:11 +02:00
Paul van Tilburg 1bf6a4e772
Update the changelog 2023-08-27 13:29:48 +02:00
Paul van Tilburg c6f7511fc7
Switch to Debian bookworm Docker image for runtime
The Rust Docker build image is also based on Bookworm and would lead to
a binary that is linked against OpenSSL 3 which would then not be
available in the bullseye runtime Docker image.
2023-08-27 13:28:28 +02:00
Paul van Tilburg 3fed86d36f
Bump the version to 0.3.1
Check and lint using Cargo / Check and lint (push) Successful in 3m15s Details
Release / Release (push) Successful in 1m26s Details
Release / Release Rust crate (push) Successful in 4m15s Details
Release / Release Docker image (push) Successful in 11m37s Details
2023-08-26 11:50:26 +02:00
Paul van Tilburg d3cc19524b
Update the changelog 2023-08-26 11:48:55 +02:00
Paul van Tilburg 92c75d09b9
Fix and improve the release workflow
Ad the relevant part of the changelog as release notes to the release
and fix some schema-related issues.
2023-08-26 11:47:31 +02:00
Paul van Tilburg e1319dcfc2
Cargo update 2023-08-26 11:44:56 +02:00
Paul van Tilburg 0f1bc9d83d
Fix typo in comment
Check and lint using Cargo / Check and lint (push) Successful in 5m4s Details
2023-07-17 21:50:13 +02:00
Paul van Tilburg 0068f6e9de
Cargo update
Check and lint using Cargo / Check and lint (push) Successful in 3m9s Details
2023-06-08 11:10:06 +02:00
Paul van Tilburg caad71389b
Also here no longer set sparse Cargo index for crates.io
Check and lint using Cargo / Check and lint (push) Failing after 26s Details
2023-06-08 11:01:13 +02:00
Paul van Tilburg 0b76db96f0
Use the personal Cargo token
Check and lint using Cargo / Check and lint (push) Successful in 2m57s Details
2023-06-08 10:58:33 +02:00
Paul van Tilburg 42a43cc83d
No longer configure using a sparse Cargo index for crates.io
Check and lint using Cargo / Check and lint (push) Successful in 2m53s Details
This is the default since Rust 1.70.
2023-06-06 07:47:23 +02:00
Paul van Tilburg 1aca61d3fd
Add a full release workflow
Check and lint using Cargo / Check and lint (push) Successful in 2m45s Details
2023-05-22 20:02:03 +02:00
Paul van Tilburg 35dda781a3
Tweak workflow step name 2023-05-22 19:56:52 +02:00
Paul van Tilburg 14bda61a9e
Fix name of Gitea Actions workflow
Check and lint using Cargo / Check and lint (push) Successful in 3m2s Details
2023-04-25 16:30:05 +02:00
Paul van Tilburg bc22fd2d70
Run cargo clippy right after check; install missing components
Check, test and lint using Cargo / Check and lint (push) Successful in 2m57s Details
2023-04-25 16:28:02 +02:00
Paul van Tilburg fd00ef0b4f
Simplify Gitea Actions check and lint workflow
Check, test and lint using Cargo / Check and lint (push) Failing after 2m50s Details
2023-04-25 16:23:32 +02:00
Paul van Tilburg c070877384
Bump the version to 0.3.0
Check, Test and Lint Using Cargo / Lints (push) Successful in 3m8s Details
Check, Test and Lint Using Cargo / Check (push) Successful in 2m39s Details
2023-04-15 12:16:30 +02:00
Paul van Tilburg 732d4b83f2
Update the changelog 2023-04-15 12:15:40 +02:00
Paul van Tilburg 12a797baa9
Cargo update
Check, Test and Lint Using Cargo / Check (push) Successful in 2m53s Details
Check, Test and Lint Using Cargo / Lints (push) Successful in 3m3s Details
2023-04-14 23:26:07 +02:00
Paul van Tilburg 5586ae4d70
Update build dependecy on the vergen crate to 8.1.1
This change allows for dropping the dependency on the `anyhow` crate.
2023-04-14 23:25:25 +02:00
Paul van Tilburg 59e3b53263
Implement backoff for login/update API calls (closes: #8)
Check, Test and Lint Using Cargo / Check (push) Successful in 3m45s Details
Check, Test and Lint Using Cargo / Lints (push) Successful in 3m35s Details
Start from an interval of 10 seconds, increase with a factor of 2.0 on
each failure up to a maximum of 320 seconds.

This commit also fixes an issue where the update loop would be aborted
if a relogin fails.
2023-04-14 23:02:21 +02:00
Paul van Tilburg f236499125
Fix login errors not being detected 2023-04-14 22:54:54 +02:00
Paul van Tilburg 02a4d1ca9b
Update to Rocket 0.5.0-rc.3
Check Details
Lints Details
2023-03-24 14:38:42 +01:00
Paul van Tilburg 3fff79a2cd
Fix missing build script/git repo during build
Check Details
Lints Details
When building the dependencies, the build script should not be
considered. When building the actual binary, the git repository needs to
be present and the build script should be run.
2023-03-22 15:27:12 +01:00
Paul van Tilburg 9200a10cef
Speed up build by using sparse Cargo index for crates.io 2023-03-22 15:26:45 +01:00
Paul van Tilburg beb49373fb
Bump the version to 0.2.2
Check Details
Lints Details
2023-03-22 15:00:35 +01:00
Paul van Tilburg 99e7e8a68c
Update the changelog 2023-03-22 14:59:18 +01:00
Paul van Tilburg bab9228b0f
Drop unused dependency on the toml crate 2023-03-22 13:38:16 +01:00
Paul van Tilburg 81e82e90da
Cargo update; fixes RUSTSEC-2023-0018 2023-03-22 13:37:56 +01:00
Paul van Tilburg b07bb73da4
Fix clippy issue
Check Details
Lints Details
2023-03-21 12:10:52 +01:00
Paul van Tilburg 58759d5309
Add Gitea Actions workflow for cargo
Check Details
Lints Details
2023-03-21 11:53:05 +01:00
Paul van Tilburg b1764b7fe3
Use 7 chars for the git SHA 2023-01-29 15:44:29 +01:00
Paul van Tilburg 7c704b69ed Merge pull request 'Print the version on lift off and add version endpoint' (#10) from 6-print-version-add-endpoint into main
Reviewed-on: #10
2023-01-29 15:37:14 +01:00
Paul van Tilburg 04e28a33c3
Add a /version API endpoint
* Introduce the `VersionInfo` struct, build from the vergen environment
  variables
* Add the `version` handler to construct and return the version info
* Update the README
2023-01-29 15:29:32 +01:00
Paul van Tilburg de1ad37b95
Use the vergen crate to generate version information
* Add depend on the `vergen` crate (only use the `build` and `git`
  features)
* Add the build script `build.rs` to setup the environment variables
  from the build system
* Update the printed version information to use these environment
  variables
2023-01-29 15:22:47 +01:00
Paul van Tilburg 5cbc3a04fc
Print the version on lift off 2023-01-16 21:18:20 +01:00
Paul van Tilburg ca116351db
Implement error catchers for all requests (closes: #5)
* Introduce an error JSON output
* Return error JSON output if the status data is not there (yet)
* Introduce a default catcher to return error JSON output in all other
  unsupported/unhandled cases
* Update the documentation
2023-01-16 21:08:03 +01:00
Paul van Tilburg 8d892d8619
Tweak documentation (to look like the rest) 2023-01-16 21:05:15 +01:00
13 changed files with 1097 additions and 730 deletions

View File

@ -5,9 +5,6 @@ target
Dockerfile*
docker-compose*
# Git folder
.git
# Dot files
.gitignore

View File

@ -0,0 +1,46 @@
name: "Check and lint using Cargo"
on:
- pull_request
- push
- workflow_dispatch
jobs:
check_lint:
name: Check and lint
runs-on: debian-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install Rust stable toolchain
uses: https://github.com/actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Run cargo check
uses: https://github.com/actions-rs/cargo@v1
with:
command: check
- name: Run cargo clippy
uses: https://github.com/actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
- name: Run cargo fmt
uses: https://github.com/actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
# TODO: Add a test suite first!
# - name: Run cargo test
# uses: https://github.com/actions-rs/cargo@v1
# with:
# command: test
# args: --all-features

View File

@ -0,0 +1,122 @@
name: "Release"
on:
push:
tags:
- "v*"
jobs:
release:
name: "Release"
runs-on: debian-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Determine the version of the release
run: |
VERSION=${GITHUB_REF_NAME#v}
echo "Releasing version: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Get the release notes from the changelog
run: |
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
RELEASE_NOTES=$(sed -n -e "/^## \[$VERSION\]/,/^## \[/{//"'!'"p;}" CHANGELOG.md | sed -e '1d;$d')
echo "Release notes:"
echo
echo "$RELEASE_NOTES"
echo "RELEASE_NOTES<<$EOF" >> "$GITHUB_ENV"
echo "$RELEASE_NOTES" >> "$GITHUB_ENV"
echo "$EOF" >> "$GITHUB_ENV"
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '>=1.20.1'
- name: Release to Gitea
uses: actions/release-action@main
with:
# This is available by default.
api_key: '${{ secrets.RELEASE_TOKEN }}'
files: FIXME
title: 'Release ${{ env.VERSION }}'
body: '${{ env.RELEASE_NOTES }}'
release-crate:
name: "Release Rust crate"
runs-on: debian-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Rust stable toolchain
uses: https://github.com/actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: Use sparse Cargo index for crates.io
run: echo -e '[registries.crates-io]\nprotocol = "sparse"' >> /root/.cargo/config.toml
- name: Register the Gitea crate registry with Cargo
run: echo -e '[registries.luon]\nindex = "https://git.luon.net/paul/_cargo-index.git"' >> /root/.cargo/config.toml
- name: Run cargo publish
uses: https://github.com/actions-rs/cargo@v1
env:
# This needs to be provided for the repository; no login necessary as a result.
CARGO_REGISTRIES_LUON_TOKEN: '${{ secrets.CARGO_TOKEN }}'
with:
command: publish
args: --registry luon
release-docker-image:
name: "Release Docker image"
runs-on: debian-latest
container:
image: ghcr.io/catthehacker/ubuntu:act-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Docker metadata
id: meta
uses: https://github.com/docker/metadata-action@v4
with:
images: |
git.luon.net/paul/solar-grabber
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Set up Docker Buildx
uses: https://github.com/docker/setup-buildx-action@v2
- name: Login to the Gitea Docker registry
uses: https://github.com/docker/login-action@v2
with:
registry: git.luon.net
username: ${{ github.repository_owner }}
# This needs to be provided by the repository owner and have the packages scopes enabled.
# Note that the default `GITEA_TOKEN` secret does not have this scope enabled.
password: ${{ secrets.DOCKER_REGISTRY_TOKEN }}
- name: Docker build and push
uses: https://github.com/docker/build-push-action@v4
env:
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}

View File

@ -7,6 +7,79 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.3.5] - 2024-02-27
### Fixed
* Fix clippy issue
## [0.3.4] - 2024-02-27
### Security
* Updated dependencies, fixes security advisories:
* [RUSTSEC-2024-0003](https://rustsec.org/advisories/RUSTSEC-2024-0003)
* [RUSTSEC-2023-0072](https://rustsec.org/advisories/RUSTSEC-2024-0072)
## [0.3.3] - 2023-11-03
### Security
* Update dependencies ([RUSTSEC-2020-0071](https://rustsec.org/advisories/RUSTSEC-2020-0071.html))
### Changed
* Switch to Rocket 0.5 RC4
## [0.3.2] - 2023-08-27
### Fixed
* Switch to Debian bookworm Docker image for runtime; fixes Docker image
## [0.3.1] - 2023-08-26
### Changed
* Fix and improve Gitea Actions workflow
### Security
* Update dependencies ([RUSTSEC-2023-0044](https://rustsec.org/advisories/RUSTSEC-2023-0044))
## [0.3.0] - 2023-04-15
### Added
* Implement backoff for login/update API call failures (#8)
### Changed
* Update dependencies
* Speed up Docker image builds by using sparse Cargo index for crates.io
### Fixed
* Fix login errors not being detected for My Autarco
* Fix missing build script/git repository during Docker image build
## [0.2.2] - 2023-03-22
### Added
* Implement error catchers for all endpoints (#5)
* Print the version on lift off (#6)
* Add `/version` endpoint to the API (#6)
* Add Gitea Actions workflow for cargo
### Fixed
* Fixed/tweaked documentation
### Security
* Update dependencies ([RUSTSEC-2023-0018](https://rustsec.org/advisories/RUSTSEC-2023-0018.html))
## [0.2.1] - 2023-01-16
### Changed
@ -48,7 +121,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Rename Autarco Scraper project to Solar Grabber.
[Unreleased]: https://git.luon.net/paul/solar-grabber/compare/v0.2.1...HEAD
[Unreleased]: https://git.luon.net/paul/solar-grabber/compare/v0.3.5...HEAD
[0.3.5]: https://git.luon.net/paul/solar-grabber/compare/v0.3.4...v0.3.5
[0.3.4]: https://git.luon.net/paul/solar-grabber/compare/v0.3.3...v0.3.4
[0.3.3]: https://git.luon.net/paul/solar-grabber/compare/v0.3.2...v0.3.3
[0.3.2]: https://git.luon.net/paul/solar-grabber/compare/v0.3.1...v0.3.2
[0.3.1]: https://git.luon.net/paul/solar-grabber/compare/v0.3.0...v0.3.1
[0.3.0]: https://git.luon.net/paul/solar-grabber/compare/v0.2.2...v0.3.0
[0.2.2]: https://git.luon.net/paul/solar-grabber/compare/v0.2.1...v0.2.2
[0.2.1]: https://git.luon.net/paul/solar-grabber/compare/v0.2.0...v0.2.1
[0.2.0]: https://git.luon.net/paul/solar-grabber/compare/v0.1.1...v0.2.0
[0.1.1]: https://git.luon.net/paul/solar-grabber/src/tag/v0.1.1

1367
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "solar-grabber"
version = "0.2.1"
version = "0.3.5"
authors = ["Paul van Tilburg <paul@luon.net>"]
edition = "2021"
description = """"
@ -10,6 +10,7 @@ get statistical data of your solar panels.
readme = "README.md"
repository = "https://git.luon.net/paul/solar-grabber"
license = "MIT"
build = "build.rs"
[dependencies]
chrono = { version = "0.4.23", features = ["serde"] }
@ -18,11 +19,13 @@ enum_dispatch = "0.3.9"
md-5 = "0.10.5"
once_cell = "1.9.0"
reqwest = { version = "0.11.6", features = ["cookies", "json"] }
rocket = { version = "0.5.0-rc.2", features = ["json"] }
rocket = { version = "0.5.0-rc.3", features = ["json"] }
thiserror = "1.0.38"
toml = "0.5.6"
url = "2.2.2"
[build-dependencies]
vergen = { version = "8.1.1", features = ["build", "git", "gitcl"] }
[package.metadata.deb]
maintainer = "Paul van Tilburg <paul@luon.net>"
copyright = "2022, Paul van Tilburg"

View File

@ -10,10 +10,12 @@ FROM docker.io/rust:1 as builder
RUN USER=root cargo new --bin /usr/src/solar-grabber
WORKDIR /usr/src/solar-grabber
COPY ./Cargo.* ./
RUN sed -i -e 's/^build =/#build =/' Cargo.toml
RUN cargo build --release
RUN rm src/*.rs
# Add the real project files from current folder
COPY ./Cargo.toml ./
ADD . ./
# Build the actual binary from the copied local files
@ -22,7 +24,7 @@ RUN cargo build --release
########################## RUNTIME IMAGE ##########################
# Create new stage with a minimal image for the actual runtime image/container
FROM docker.io/debian:bullseye-slim
FROM docker.io/debian:bookworm-slim
# Install CA certificates
RUN apt-get update && \

View File

@ -82,7 +82,7 @@ This also uses `Rocket.toml` from the current working directory as configuration
You can alternatively pass a set of environment variables instead. See
`docker-compose.yml` for a list.
## API endpoint
## Status API Endpoint
The `/` API endpoint provides the current statistical data of your solar panels
once it has successfully logged into the cloud service using your credentials.
@ -92,9 +92,9 @@ There is no path and no query parameters, just:
GET /
```
### Response
### Status API Response
A response uses the JSON format and typically looks like this:
The response uses the JSON format and typically looks like this:
```json
{"current_w":23.0,"total_kwh":6159.0,"last_updated":1661194620}
@ -104,6 +104,36 @@ This contains the current production power (`current_w`) in Watt,
the total of produced energy since installation (`total_kwh`) in kilowatt-hour
and the (UNIX) timestamp that indicates when the information was last updated.
### (Status) API Error Response
If the API endpoint is accessed before any statistical data has been retrieved,
or if any other request than `GET /` is made, an error response is returned
that looks like this:
```json
{"error":"No status found (yet)"}
```
## Version API Endpoint
The `/version` endpoint provides information of the current version and build
of the service. This can be used to check if it needs to be updated.
Again, there is no path and no query parameters, just:
```http
GET /version
```
### Version API Response
The response uses the JSON format and typically looks like this:
```json
{"version":"0.2.1","timestamp":"2023-01-29T14:10:24.971748027Z","git_sha":"5cbc3a04","git_timestamp":"2023-01-16T20:18:20Z"}
```
(Build and git information may be out of date.)
## Integration in Home Assistant
To integrate the Solar Grabber service in your [Home Assistant](https://www.home-assistant.io/)

9
build.rs Normal file
View File

@ -0,0 +1,9 @@
use std::error::Error;
use vergen::EmitBuilder;
fn main() -> Result<(), Box<dyn Error>> {
// Generate the `cargo:` instructions to fill the appropriate environment variables.
EmitBuilder::builder().all_build().all_git().emit()?;
Ok(())
}

View File

@ -20,12 +20,12 @@ mod update;
use std::sync::Mutex;
use once_cell::sync::Lazy;
use rocket::fairing::AdHoc;
use rocket::serde::json::Json;
use rocket::{
catch, catchers,
fairing::AdHoc,
get, routes,
serde::{Deserialize, Serialize},
Build, Rocket,
serde::{json::Json, Deserialize, Serialize},
Build, Request, Rocket,
};
use self::update::update_loop;
@ -45,25 +45,87 @@ struct Config {
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(crate = "rocket::serde")]
struct Status {
/// Current power production (W)
/// The current power production (W).
current_w: f32,
/// Total energy produced since installation (kWh)
/// The total energy produced since installation (kWh).
total_kwh: f32,
/// Timestamp of last update
/// The (UNIX) timestamp of when the status was last updated.
last_updated: u64,
}
/// An error used as JSON response.
#[derive(Debug, Serialize)]
#[serde(crate = "rocket::serde")]
struct Error {
/// The error message.
error: String,
}
impl Error {
/// Creates a new error result from a message.
fn from(message: impl AsRef<str>) -> Self {
let error = String::from(message.as_ref());
Self { error }
}
}
/// Returns the current (last known) status.
#[get("/", format = "application/json")]
async fn status() -> Option<Json<Status>> {
async fn status() -> Result<Json<Status>, Json<Error>> {
let status_guard = STATUS.lock().expect("Status mutex was poisoined");
status_guard.map(Json)
status_guard
.map(Json)
.ok_or_else(|| Json(Error::from("No status found (yet)")))
}
/// The version information as JSON response.
#[derive(Debug, Serialize)]
#[serde(crate = "rocket::serde")]
struct VersionInfo {
/// The version of the build.
version: String,
/// The timestamp of the build.
timestamp: String,
/// The (most recent) git SHA used for the build.
git_sha: String,
/// The timestamp of the last git commit used for the build.
git_timestamp: String,
}
impl VersionInfo {
/// Retrieves the version information from the environment variables.
fn new() -> Self {
Self {
version: String::from(env!("CARGO_PKG_VERSION")),
timestamp: String::from(env!("VERGEN_BUILD_TIMESTAMP")),
git_sha: String::from(&env!("VERGEN_GIT_SHA")[0..7]),
git_timestamp: String::from(env!("VERGEN_GIT_COMMIT_TIMESTAMP")),
}
}
}
/// Returns the version information.
#[get("/version", format = "application/json")]
async fn version() -> Result<Json<VersionInfo>, Json<Error>> {
Ok(Json(VersionInfo::new()))
}
/// Default catcher for any unsuppored request
#[catch(default)]
fn unsupported(status: rocket::http::Status, _request: &Request<'_>) -> Json<Error> {
let code = status.code;
Json(Error::from(format!(
"Unhandled/unsupported API call or path (HTTP {code})"
)))
}
/// Creates a Rocket and attaches the config parsing and update loop as fairings.
pub fn setup() -> Rocket<Build> {
rocket::build()
.mount("/", routes![status])
.mount("/", routes![status, version])
.register("/", catchers![unsupported])
.attach(AdHoc::config::<Config>())
.attach(AdHoc::on_liftoff("Updater", |rocket| {
Box::pin(async move {
@ -73,8 +135,17 @@ pub fn setup() -> Rocket<Build> {
.expect("Invalid configuration");
let service = services::get(config.service).expect("Invalid service");
// We don't care about the join handle nor error results?t
let _ = rocket::tokio::spawn(update_loop(service));
// We don't care about the join handle nor error results?
let _service = rocket::tokio::spawn(update_loop(service));
})
}))
.attach(AdHoc::on_liftoff("Version", |_| {
Box::pin(async move {
let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
let git_sha = &env!("VERGEN_GIT_SHA")[0..7];
println!("☀️ Started {name} v{version} (git @{git_sha})");
})
}))
}

View File

@ -6,7 +6,7 @@
use std::sync::Arc;
use chrono::{DateTime, Local, TimeZone};
use chrono::{DateTime, Local, NaiveDateTime, TimeZone};
use md5::{Digest, Md5};
use reqwest::{cookie::Jar as CookieJar, Client, ClientBuilder, Url};
use rocket::{
@ -112,7 +112,7 @@ where
use rocket::serde::de::Error;
match <StringOrObject<'_, T>>::deserialize(deserializer) {
Ok(StringOrObject::String(s)) if s.is_empty() => Ok(None),
Ok(StringOrObject::String("")) => Ok(None),
Ok(StringOrObject::String(_)) => Err(Error::custom("Non-empty string not allowed here")),
Ok(StringOrObject::Object(t)) => Ok(Some(t)),
Ok(StringOrObject::Value(j)) => Err(Error::custom(&format!(
@ -133,9 +133,12 @@ where
use rocket::serde::de::Error;
let s = <&str>::deserialize(deserializer)?;
let dt = NaiveDateTime::parse_from_str(s, DATE_TIME_FORMAT).map_err(D::Error::custom)?;
Local
.datetime_from_str(s, DATE_TIME_FORMAT)
.map_err(D::Error::custom)
.from_local_datetime(&dt)
.latest()
.ok_or_else(|| D::Error::custom("time representation is invalid for server time zone"))
}
/// Deserializes a string ([`&str`]) into a float ([`f32`]).

View File

@ -92,14 +92,17 @@ impl super::Service for Service {
/// It mainly stores the acquired cookie in the client's cookie jar. The login credentials come
/// from the loaded configuration (see [`Config`]).
async fn login(&mut self) -> Result<()> {
let login_url = login_url().expect("valid login URL");
let params = [
("username", &self.config.username),
("password", &self.config.password),
];
let login_url = login_url().expect("valid login URL");
self.client.post(login_url).form(&params).send().await?;
Ok(())
let response = self.client.post(login_url).form(&params).send().await?;
match response.error_for_status() {
Ok(_) => Ok(()),
Err(e) if e.status() == Some(StatusCode::UNAUTHORIZED) => Err(Error::NotAuthorized),
Err(e) => Err(e.into()),
}
}
/// Retrieves a status update from the API of the My Autarco site.

View File

@ -9,6 +9,22 @@ use crate::{
STATUS,
};
/// The default sleep interval to use between checks.
const DEFAULT_SLEEP_INTERVAL: u64 = 10;
/// The sleep interval upper limit when applying exponential backoff.
const MAX_SLEEP_INTERVAL: u64 = 320;
/// The backoff factor.
const BACKOFF_FACTOR: f64 = 2.0;
/// Calculates the new interval by applying the backoff factor and taking the maximum into account.
fn back_off(interval: u64) -> u64 {
let new_interval = (interval as f64 * BACKOFF_FACTOR) as u64;
new_interval.min(MAX_SLEEP_INTERVAL)
}
/// Main update loop that logs in and periodically acquires updates from the API.
///
/// It updates the mutex-guarded current update [`Status`](crate::Status) struct which can be
@ -23,9 +39,10 @@ pub(super) async fn update_loop(service: Services) -> color_eyre::Result<()> {
let mut last_updated = 0;
let poll_interval = service.poll_interval();
let mut sleep_interval = DEFAULT_SLEEP_INTERVAL;
loop {
// Wake up every 10 seconds and check if an update is due.
sleep(Duration::from_secs(10)).await;
sleep(Duration::from_secs(sleep_interval)).await;
let timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
@ -39,15 +56,24 @@ pub(super) async fn update_loop(service: Services) -> color_eyre::Result<()> {
Ok(status) => status,
Err(Error::NotAuthorized) => {
eprintln!("💥 Update unauthorized, trying to log in again...");
service.login().await?;
if let Err(e) = service.login().await {
eprintln!("💥 Login failed: {e}; will retry in {sleep_interval} seconds...");
sleep_interval = back_off(sleep_interval);
continue;
};
println!("⚡ Logged in successfully!");
sleep_interval = DEFAULT_SLEEP_INTERVAL;
continue;
}
Err(e) => {
eprintln!("💥 Failed to update status: {}", e);
eprintln!(
"💥 Failed to update status: {e}; will retry in {sleep_interval} seconds..."
);
sleep_interval = back_off(sleep_interval);
continue;
}
};
sleep_interval = DEFAULT_SLEEP_INTERVAL;
last_updated = timestamp;
println!("⚡ Updated status to: {:#?}", status);