Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
Paul van Tilburg | beb49373fb | |
Paul van Tilburg | 99e7e8a68c | |
Paul van Tilburg | bab9228b0f | |
Paul van Tilburg | 81e82e90da | |
Paul van Tilburg | b07bb73da4 | |
Paul van Tilburg | 58759d5309 | |
Paul van Tilburg | b1764b7fe3 | |
Paul van Tilburg | 7c704b69ed | |
Paul van Tilburg | 04e28a33c3 | |
Paul van Tilburg | de1ad37b95 | |
Paul van Tilburg | 5cbc3a04fc | |
Paul van Tilburg | ca116351db | |
Paul van Tilburg | 8d892d8619 |
|
@ -0,0 +1,83 @@
|
|||
name: "Check, Test and Lint Using Cargo"
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
- workflow_dispatch
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install 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: Run cargo check
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
|
||||
# TODO: Add a test suite first!
|
||||
# test:
|
||||
# name: Test Suite
|
||||
# runs-on: debian-latest
|
||||
# steps:
|
||||
# - name: Checkout sources
|
||||
# uses: actions/checkout@v3
|
||||
#
|
||||
# - name: Install 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: Run cargo test
|
||||
# uses: https://github.com/actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: test
|
||||
# args: --all-features
|
||||
|
||||
lints:
|
||||
name: Lints
|
||||
runs-on: debian-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: https://github.com/actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Use sparse Cargo index for crates.io
|
||||
run: echo -e '[registries.crates-io]\nprotocol = "sparse"' >> /root/.cargo/config.toml
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Run cargo clippy
|
||||
uses: https://github.com/actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [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 +65,8 @@ 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.2.2...HEAD
|
||||
[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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "solar-grabber"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
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"] }
|
||||
|
@ -20,9 +21,12 @@ once_cell = "1.9.0"
|
|||
reqwest = { version = "0.11.6", features = ["cookies", "json"] }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
thiserror = "1.0.38"
|
||||
toml = "0.5.6"
|
||||
url = "2.2.2"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.68"
|
||||
vergen = { version = "7.5.0", default_features = false, features = ["build", "git"] }
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "Paul van Tilburg <paul@luon.net>"
|
||||
copyright = "2022, Paul van Tilburg"
|
||||
|
|
36
README.md
36
README.md
|
@ -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/)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use vergen::{vergen, Config};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Generate the `cargo:` instructions to fill the appropriate environment variables.
|
||||
vergen(Config::default())
|
||||
}
|
93
src/lib.rs
93
src/lib.rs
|
@ -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!("VERGEN_BUILD_SEMVER")),
|
||||
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 {
|
||||
|
@ -74,7 +136,16 @@ pub fn setup() -> Rocket<Build> {
|
|||
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));
|
||||
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!("VERGEN_BUILD_SEMVER");
|
||||
let git_sha = &env!("VERGEN_GIT_SHA")[0..7];
|
||||
|
||||
println!("☀️ Started {name} v{version} (git @{git_sha})");
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue