mirror of
https://codeberg.org/icewind/taspromto.git
synced 2026-06-03 08:34:21 +02:00
updates
This commit is contained in:
parent
5c87b874e4
commit
c3b5238a51
13 changed files with 532 additions and 603 deletions
960
Cargo.lock
generated
960
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
26
Cargo.toml
26
Cargo.toml
|
|
@ -2,26 +2,26 @@
|
|||
name = "taspromto"
|
||||
version = "0.2.0"
|
||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.74.1"
|
||||
edition = "2024"
|
||||
rust-version = "1.85.0"
|
||||
|
||||
[dependencies]
|
||||
rumqttc = "0.24.0"
|
||||
tokio = { version = "1.41.0", features = ["macros", "rt-multi-thread"] }
|
||||
rumqttc = "0.25.1"
|
||||
tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread"] }
|
||||
dashmap = "6.1.0"
|
||||
jzon = "0.12.5"
|
||||
warp = "0.3.7"
|
||||
warp = { version = "0.4.2", features = ["server"] }
|
||||
dotenvy = "0.15.7"
|
||||
ctrlc = { version = "3.4.5", features = ["termination"] }
|
||||
color-eyre = "0.6.3"
|
||||
ctrlc = { version = "3.5.2", features = ["termination"] }
|
||||
color-eyre = "0.6.5"
|
||||
async-stream = "0.3.6"
|
||||
pin-utils = "0.1.0"
|
||||
hostname = "0.4.0"
|
||||
tokio-stream = { version = "0.1.16", features = ["net"] }
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
secretfile = "0.1.0"
|
||||
toml = "0.8.19"
|
||||
clap = { version = "4.5.20", features = ["derive"] }
|
||||
hostname = "0.4.2"
|
||||
tokio-stream = { version = "0.1.18", features = ["net"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
secretfile = "0.1.1"
|
||||
toml = "1.1.0"
|
||||
clap = { version = "4.5.61", features = ["derive"] }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
|||
22
Dockerfile
22
Dockerfile
|
|
@ -1,22 +0,0 @@
|
|||
FROM rust:alpine AS build
|
||||
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
|
||||
# Build with a dummy main to pre-build dependencies
|
||||
RUN apk add --no-cache alpine-sdk && \
|
||||
mkdir src && \
|
||||
echo "fn main(){}" > src/main.rs && \
|
||||
cargo build --release --target x86_64-unknown-linux-musl && \
|
||||
rm -r src
|
||||
|
||||
COPY src/* ./src/
|
||||
|
||||
RUN touch src/main.rs && \
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=build /target/x86_64-unknown-linux-musl/release/taspromto /
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["/taspromto"]
|
||||
34
flake.lock
generated
34
flake.lock
generated
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1742394900,
|
||||
"narHash": "sha256-vVOAp9ahvnU+fQoKd4SEXB2JG2wbENkpqcwlkIXgUC0=",
|
||||
"lastModified": 1774313767,
|
||||
"narHash": "sha256-hy0XTQND6avzGEUFrJtYBBpFa/POiiaGBr2vpU6Y9tY=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "70947c1908108c0c551ddfd73d4f750ff2ea67cd",
|
||||
"rev": "3d9df76e29656c679c744968b17fbaf28f0e923d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -22,11 +22,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748868585,
|
||||
"narHash": "sha256-DrrbahOQAwvNM8l5EuGxxkVS7X5/S59zcG0N9ZWQFhk=",
|
||||
"lastModified": 1774271954,
|
||||
"narHash": "sha256-FbvMOykx7f7uEPdRVzUSABnLjqCdEp22wa0nDkuEd3s=",
|
||||
"owner": "nix-community",
|
||||
"repo": "flakelight",
|
||||
"rev": "dfbecd12d99c1bf82906521a6a7d5b75d2aa1ca2",
|
||||
"rev": "c90878b309508083094f465d6aa11b3963f48b9f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -44,11 +44,11 @@
|
|||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748205441,
|
||||
"narHash": "sha256-W+UUBT/l1DSTZo5G43494mRNNspJ2i9jW2QELC9JuMQ=",
|
||||
"lastModified": 1774538603,
|
||||
"narHash": "sha256-IG0fOFNUjselW61zc/jOZU3abmt7FjcRdES9SXeHun4=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "dac3b74a89cebbeb21cc6602e4a346604adbee8b",
|
||||
"revCount": 49,
|
||||
"rev": "0ef9ce3384617243e1b9e94a7057f879f621f014",
|
||||
"revCount": 69,
|
||||
"type": "git",
|
||||
"url": "https://codeberg.org/icewind/mill-scale"
|
||||
},
|
||||
|
|
@ -59,16 +59,16 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1748708770,
|
||||
"narHash": "sha256-q8jG2HJWgooWa9H0iatZqBPF3bp0504e05MevFmnFLY=",
|
||||
"lastModified": 1774388614,
|
||||
"narHash": "sha256-tFwzTI0DdDzovdE9+Ras6CUss0yn8P9XV4Ja6RjA+nU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a59eb7800787c926045d51b70982ae285faa2346",
|
||||
"rev": "1073dad219cb244572b74da2b20c7fe39cb3fa9e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-25.05",
|
||||
"ref": "nixos-25.11",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
|
|
@ -88,11 +88,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742697269,
|
||||
"narHash": "sha256-Lpp0XyAtIl1oGJzNmTiTGLhTkcUjwSkEb0gOiNzYFGM=",
|
||||
"lastModified": 1774535687,
|
||||
"narHash": "sha256-dpKS/8+uB0EoI4mCrpio+xs8Xxry6ZhLLwV8VIbbfrs=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "01973c84732f9275c50c5f075dd1f54cc04b3316",
|
||||
"rev": "75900435aa883f84b038316864b3f60956681523",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
12
flake.nix
12
flake.nix
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
nixpkgs.url = "nixpkgs/nixos-25.11";
|
||||
flakelight = {
|
||||
url = "github:nix-community/flakelight";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
|
@ -12,7 +12,13 @@
|
|||
};
|
||||
outputs = {mill-scale, ...}:
|
||||
mill-scale ./. {
|
||||
packages.tasprompto = import ./package.nix;
|
||||
withOverlays = [
|
||||
(import ./nix/overlay.nix)
|
||||
];
|
||||
|
||||
packages = {
|
||||
taspromto = pkgs: pkgs.taspromto;
|
||||
};
|
||||
|
||||
nixosModules = {outputs, ...}: {
|
||||
default = {
|
||||
|
|
@ -21,7 +27,7 @@
|
|||
lib,
|
||||
...
|
||||
}: {
|
||||
imports = [./module.nix];
|
||||
imports = [./nix/module.nix];
|
||||
config = lib.mkIf config.services.taspromto.enable {
|
||||
nixpkgs.overlays = [outputs.overlays.default];
|
||||
services.taspromto.package = lib.mkDefault pkgs.taspromto;
|
||||
|
|
|
|||
20
nix/package.nix
Normal file
20
nix/package.nix
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
stdenv,
|
||||
rustPlatform,
|
||||
lib,
|
||||
}: let
|
||||
inherit (lib.sources) sourceByRegex;
|
||||
inherit (builtins) fromTOML readFile;
|
||||
src = sourceByRegex ../. ["Cargo.*" "(src)(/.*)?"];
|
||||
version = (fromTOML (readFile ../Cargo.toml)).package.version;
|
||||
in
|
||||
rustPlatform.buildRustPackage rec {
|
||||
pname = "taspromto";
|
||||
|
||||
inherit src version;
|
||||
|
||||
cargoLock = {
|
||||
lockFile = ../Cargo.lock;
|
||||
};
|
||||
meta.mainProgram = "taspromto";
|
||||
}
|
||||
18
package.nix
18
package.nix
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
stdenv,
|
||||
rustPlatform,
|
||||
lib,
|
||||
}: let
|
||||
inherit (lib.sources) sourceByRegex;
|
||||
src = sourceByRegex ./. ["Cargo.*" "(src)(/.*)?"];
|
||||
in
|
||||
rustPlatform.buildRustPackage rec {
|
||||
pname = "taspromto";
|
||||
version = "0.1.0";
|
||||
|
||||
inherit src;
|
||||
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
};
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ pub struct NamesConfig {
|
|||
#[serde(rename = "mitemp")]
|
||||
pub mi_temp: BTreeMap<BDAddr, String>,
|
||||
#[serde(rename = "rftemp")]
|
||||
pub rf_temp: HashMap<RfDeviceId<'static>, String>,
|
||||
pub rf_temp: HashMap<RfDeviceId, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ pub struct DeviceStates {
|
|||
pub devices: HashMap<Device, DeviceState>,
|
||||
pub dsmr_devices: HashMap<Device, DsmrState>,
|
||||
pub mi_temp_devices: BTreeMap<BDAddr, MiTempState>,
|
||||
pub rf_temp_devices: HashMap<RfDeviceId<'static>, TempState>,
|
||||
active_rf_temp_id: RfDeviceId<'static>,
|
||||
pub rf_temp_devices: HashMap<RfDeviceId, TempState>,
|
||||
active_rf_temp_id: RfDeviceId,
|
||||
}
|
||||
|
||||
impl DeviceStates {
|
||||
|
|
@ -65,10 +65,7 @@ impl DeviceStates {
|
|||
|
||||
pub fn update_rf(&mut self, payload: &str) {
|
||||
if let Some(data) = parse_rf_payload(payload) {
|
||||
let state = self
|
||||
.rf_temp_devices
|
||||
.entry(data.device_id().to_owned())
|
||||
.or_default();
|
||||
let state = self.rf_temp_devices.entry(data.device_id()).or_default();
|
||||
state.humidity = data.humidity;
|
||||
state.temperature = data.temperature;
|
||||
} else {
|
||||
|
|
@ -79,7 +76,7 @@ impl DeviceStates {
|
|||
pub fn update_rtl(&mut self, device: &str, field: &str, payload: &str) {
|
||||
if self.active_rf_temp_id.name != device {
|
||||
self.active_rf_temp_id = RfDeviceId::default();
|
||||
self.active_rf_temp_id.name = device.to_string().into();
|
||||
self.active_rf_temp_id.name = device.into();
|
||||
}
|
||||
match field {
|
||||
"id" => self.active_rf_temp_id.id = payload.parse().unwrap_or_default(),
|
||||
|
|
@ -92,7 +89,7 @@ impl DeviceStates {
|
|||
fn update_active_rtl(&mut self, field: &str, payload: &str) {
|
||||
let state = self
|
||||
.rf_temp_devices
|
||||
.entry(self.active_rf_temp_id.to_owned())
|
||||
.entry(self.active_rf_temp_id.clone())
|
||||
.or_default();
|
||||
match field {
|
||||
"temperature_F" => {
|
||||
|
|
@ -110,7 +107,7 @@ impl DeviceStates {
|
|||
self.mi_temp_devices.iter()
|
||||
}
|
||||
|
||||
pub fn rf_temp(&self) -> impl Iterator<Item = (&RfDeviceId<'static>, &TempState)> {
|
||||
pub fn rf_temp(&self) -> impl Iterator<Item = (&RfDeviceId, &TempState)> {
|
||||
self.rf_temp_devices.iter()
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +330,7 @@ pub fn format_device_state<W: Write>(
|
|||
mut writer: W,
|
||||
device: &Device,
|
||||
state: &DeviceState,
|
||||
) -> std::fmt::Result {
|
||||
) -> fmt::Result {
|
||||
if state.name.is_empty() {
|
||||
println!("{} has no name set, skipping", device.hostname);
|
||||
return Ok(());
|
||||
|
|
@ -437,7 +434,7 @@ pub fn format_mi_temp_state<W: Write>(
|
|||
addr: BDAddr,
|
||||
names: &BTreeMap<BDAddr, String>,
|
||||
state: &MiTempState,
|
||||
) -> std::fmt::Result {
|
||||
) -> fmt::Result {
|
||||
// sensor_battery{name="Living Room", mac="58:2D:34:39:1D:5B"} 100
|
||||
// sensor_temperature{name="Living Room", mac="58:2D:34:39:1D:5B"} 16.2
|
||||
// sensor_humidity{name="Living Room", mac="58:2D:34:39:1D:5B"} 61.
|
||||
|
|
@ -485,7 +482,7 @@ pub fn format_rf_temp_state<W: Write>(
|
|||
channel: &RfDeviceId,
|
||||
names: &HashMap<RfDeviceId, String>,
|
||||
state: &TempState,
|
||||
) -> std::fmt::Result {
|
||||
) -> fmt::Result {
|
||||
let name = if let Some(name) = names.get(channel) {
|
||||
name
|
||||
} else {
|
||||
|
|
@ -510,11 +507,7 @@ pub fn format_rf_temp_state<W: Write>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn format_dsmr_state<W: Write>(
|
||||
mut writer: W,
|
||||
device: &str,
|
||||
state: &DsmrState,
|
||||
) -> std::fmt::Result {
|
||||
pub fn format_dsmr_state<W: Write>(mut writer: W, device: &str, state: &DsmrState) -> fmt::Result {
|
||||
let power_total = state.power_total_tariff_1.unwrap_or_default()
|
||||
+ state.power_total_tariff_2.unwrap_or_default();
|
||||
if power_total > 0.0 {
|
||||
|
|
@ -715,7 +708,7 @@ pub fn format_pms_state<W: Write>(
|
|||
device: &Device,
|
||||
device_state: &DeviceState,
|
||||
state: &PMSState,
|
||||
) -> std::fmt::Result {
|
||||
) -> fmt::Result {
|
||||
let name = &device_state.name;
|
||||
|
||||
writeln!(
|
||||
|
|
@ -792,33 +785,23 @@ struct RfPayload<'a> {
|
|||
}
|
||||
|
||||
impl<'a> RfPayload<'a> {
|
||||
pub fn device_id(&self) -> RfDeviceId<'a> {
|
||||
pub fn device_id(&self) -> RfDeviceId {
|
||||
RfDeviceId {
|
||||
name: Cow::Borrowed(self.name),
|
||||
name: self.name.into(),
|
||||
id: self.id,
|
||||
channel: self.channel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Clone, Default)]
|
||||
pub struct RfDeviceId<'a> {
|
||||
name: Cow<'a, str>,
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Default, Clone)]
|
||||
pub struct RfDeviceId {
|
||||
name: String,
|
||||
id: u16,
|
||||
channel: u8,
|
||||
}
|
||||
|
||||
impl RfDeviceId<'_> {
|
||||
pub fn to_owned(&self) -> RfDeviceId<'static> {
|
||||
RfDeviceId {
|
||||
name: Cow::Owned(self.name.to_string()),
|
||||
id: self.id,
|
||||
channel: self.channel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for RfDeviceId<'static> {
|
||||
impl<'de> Deserialize<'de> for RfDeviceId {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
|
@ -828,7 +811,7 @@ impl<'de> Deserialize<'de> for RfDeviceId<'static> {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for RfDeviceId<'static> {
|
||||
impl FromStr for RfDeviceId {
|
||||
type Err = ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
|
@ -837,14 +820,14 @@ impl FromStr for RfDeviceId<'static> {
|
|||
let id = parts.next().unwrap_or_default().parse()?;
|
||||
let channel = parts.next().unwrap_or_default().parse()?;
|
||||
Ok(RfDeviceId {
|
||||
name: name.to_string().into(),
|
||||
name: name.into(),
|
||||
id,
|
||||
channel,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_rf_payload(payload: &str) -> Option<RfPayload> {
|
||||
fn parse_rf_payload<'a>(payload: &'a str) -> Option<RfPayload<'a>> {
|
||||
let mut parts = payload.split(";").skip(2);
|
||||
let name = parts.next()?;
|
||||
let id = parts.next()?.strip_prefix("ID=")?.parse().ok()?;
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
async fn serve(device_states: Arc<Mutex<DeviceStates>>, config: Config) {
|
||||
let mi_temp_names = config.names.mi_temp.clone();
|
||||
let rf_temp_names = config.names.rf_temp.clone();
|
||||
let mi_temp_names = config.names.mi_temp;
|
||||
let rf_temp_names = config.names.rf_temp;
|
||||
|
||||
let state = warp::any().map(move || device_states.clone());
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ async fn serve(device_states: Arc<Mutex<DeviceStates>>, config: Config) {
|
|||
ListenConfig::Unix { socket: path } => {
|
||||
let listener = UnixListener::bind(path).unwrap();
|
||||
let incoming = UnixListenerStream::new(listener);
|
||||
warp::serve(metrics).run_incoming(incoming).await;
|
||||
warp::serve(metrics).incoming(incoming);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue