mirror of
https://codeberg.org/icewind/mitemp-prometheus.git
synced 2026-06-03 09:04:13 +02:00
updates
This commit is contained in:
parent
a501b3c4c3
commit
7c3d4a7867
10 changed files with 1435 additions and 653 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
||||||
16
.github/workflows/ci.yaml
vendored
Normal file
16
.github/workflows/ci.yaml
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
name: Continuous integration
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checks:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: cachix/install-nix-action@v27
|
||||||
|
- uses: icewind1991/attic-action@v1
|
||||||
|
with:
|
||||||
|
name: ci
|
||||||
|
instance: https://cache.icewind.me
|
||||||
|
authToken: "${{ secrets.ATTIC_TOKEN }}"
|
||||||
|
- run: nix flake check --keep-going
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,2 +1,6 @@
|
||||||
/target
|
/target
|
||||||
.env
|
**/*.rs.bk
|
||||||
|
result
|
||||||
|
.direnv
|
||||||
|
.env
|
||||||
|
config.toml
|
||||||
1706
Cargo.lock
generated
1706
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
28
Cargo.toml
28
Cargo.toml
|
|
@ -1,18 +1,26 @@
|
||||||
[package]
|
[package]
|
||||||
name = "mitemp-prometheus"
|
name = "mitemp-prometheus"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
description = "Expose Xiaomi MI Temperature and Humidity Sensor to prometheus"
|
description = "Expose Xiaomi MI Temperature and Humidity Sensor to prometheus"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
rust-version = "1.74.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dotenv = "0.15.0"
|
dotenvy = "0.15.7"
|
||||||
main_error = "0.1.0"
|
main_error = "0.1.0"
|
||||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread"] }
|
||||||
warp = "0.3"
|
warp = "0.3.7"
|
||||||
mitemp = "0.3.1"
|
mitemp = "0.4.0"
|
||||||
tokio-stream = "0.1"
|
tokio-stream = { version = "0.1.16", features = ["net"] }
|
||||||
btleplug = "0.9"
|
btleplug = { version = "0.11.6", features = ["serde"] }
|
||||||
env_logger = "0.9"
|
tracing = "0.1.40"
|
||||||
log = "0.4"
|
tracing-subscriber = "0.3.18"
|
||||||
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
|
toml = "0.8.19"
|
||||||
|
secretfile = "0.1.0"
|
||||||
|
clap = { version = "4.5.20", features = ["derive"] }
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
btleplug = { git = "https://github.com/icewind1991/btleplug/", branch = "bdaddr-deserialize-string" }
|
||||||
37
README.md
37
README.md
|
|
@ -4,6 +4,29 @@ Expose Xiaomi MI Temperature and Humidity Sensor to prometheus
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
Configuration can be done by either a config file or environment variables.
|
||||||
|
|
||||||
|
### Usage with a config file
|
||||||
|
|
||||||
|
Create a `config.toml` like
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[listen]
|
||||||
|
port = 3030
|
||||||
|
|
||||||
|
[names]
|
||||||
|
"58:2D:34:39:1A:01" = "Sensor 1"
|
||||||
|
"58:2D:34:39:1A:02" = "Sensor 2"
|
||||||
|
```
|
||||||
|
|
||||||
|
And tun the binary like
|
||||||
|
|
||||||
|
```
|
||||||
|
mitemp-prometheus config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage with environment variables
|
||||||
|
|
||||||
Run the binary with the following environment variables
|
Run the binary with the following environment variables
|
||||||
|
|
||||||
```dotenv
|
```dotenv
|
||||||
|
|
@ -11,6 +34,8 @@ PORT=3030
|
||||||
NAMES="58:2d:34:39:1a:01=Sensor 1,58:2d:34:39:1a:02=Sensor 2"
|
NAMES="58:2d:34:39:1a:01=Sensor 1,58:2d:34:39:1a:02=Sensor 2"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Querying metrics
|
||||||
|
|
||||||
The prometheus metrics will be available at `localhost:3030/metrics`
|
The prometheus metrics will be available at `localhost:3030/metrics`
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -20,20 +45,18 @@ sensor_humidity{name="Sensor 1", mac="58:2d:34:39:1a:01"} 59.2
|
||||||
sensor_battery{name="Sensor 2", mac="58:2d:34:39:1a:02"} 100
|
sensor_battery{name="Sensor 2", mac="58:2d:34:39:1a:02"} 100
|
||||||
sensor_temperature{name="Sensor 2", mac="58:2d:34:39:1a:02"} 16
|
sensor_temperature{name="Sensor 2", mac="58:2d:34:39:1a:02"} 16
|
||||||
sensor_humidity{name="Sensor 2", mac="58:2d:34:39:1a:02"} 55.9
|
sensor_humidity{name="Sensor 2", mac="58:2d:34:39:1a:02"} 55.9
|
||||||
sensor_battery{mac="58:2d:34:39:1a:03"} 100
|
|
||||||
sensor_temperature{mac="58:2d:34:39:1a:03"} 16.1
|
|
||||||
sensor_humidity{mac="58:2d:34:39:1a:03"} 55.3
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
at your option.
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
at your option.
|
||||||
|
|
||||||
### Contribution
|
### Contribution
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||||
for inclusion in the work by you shall be dual licensed as above, without any
|
for inclusion in the work by you shall be dual licensed as above, without any
|
||||||
additional terms or conditions.
|
additional terms or conditions.
|
||||||
|
|
|
||||||
107
flake.lock
generated
Normal file
107
flake.lock
generated
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"crane": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1730060262,
|
||||||
|
"narHash": "sha256-RMgSVkZ9H03sxC+Vh4jxtLTCzSjPq18UWpiM0gq6shQ=",
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"rev": "498d9f122c413ee1154e8131ace5a35a80d8fa76",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flakelight": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731328800,
|
||||||
|
"narHash": "sha256-gy6/aB9qY+PaOgqRXx5DQNsgKCkjjGKG1HYtth+WTlI=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "flakelight",
|
||||||
|
"rev": "76fce036c5e0daf15a926de77f1410ae997c5d4c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "flakelight",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mill-scale": {
|
||||||
|
"inputs": {
|
||||||
|
"crane": "crane",
|
||||||
|
"flakelight": [
|
||||||
|
"flakelight"
|
||||||
|
],
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731858893,
|
||||||
|
"narHash": "sha256-Ugoi82xv7KOc2VdQejeVvKODP6bt7dkMsDsdRRkN4a8=",
|
||||||
|
"owner": "icewind1991",
|
||||||
|
"repo": "mill-scale",
|
||||||
|
"rev": "9cc1f1c214b1d8c85fea7d8afa56983870c5a59d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "icewind1991",
|
||||||
|
"repo": "mill-scale",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731652201,
|
||||||
|
"narHash": "sha256-XUO0JKP1hlww0d7mm3kpmIr4hhtR4zicg5Wwes9cPMg=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "c21b77913ea840f8bcf9adf4c41cecc2abffd38d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-24.05",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flakelight": "flakelight",
|
||||||
|
"mill-scale": "mill-scale",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"mill-scale",
|
||||||
|
"flakelight",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1730255392,
|
||||||
|
"narHash": "sha256-9pydem8OVxa0TwjUai1PJe0yHAJw556CWCEwyoAq8Ik=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "7509d76ce2b3d22b40bd25368b45c0a9f7f36c89",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
14
flake.nix
Normal file
14
flake.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "nixpkgs/nixos-24.05";
|
||||||
|
flakelight = {
|
||||||
|
url = "github:nix-community/flakelight";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
mill-scale = {
|
||||||
|
url = "github:icewind1991/mill-scale";
|
||||||
|
inputs.flakelight.follows = "flakelight";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
outputs = { mill-scale, ... }: mill-scale ./. { };
|
||||||
|
}
|
||||||
117
src/config.rs
Normal file
117
src/config.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
use btleplug::api::BDAddr;
|
||||||
|
use main_error::MainError;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub listen: ListenConfig,
|
||||||
|
pub names: BTreeMap<BDAddr, String>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub mqtt: Option<MqttConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ListenConfig {
|
||||||
|
Ip {
|
||||||
|
#[serde(default = "default_address")]
|
||||||
|
address: IpAddr,
|
||||||
|
port: u16,
|
||||||
|
},
|
||||||
|
Unix {
|
||||||
|
socket: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_address() -> IpAddr {
|
||||||
|
Ipv4Addr::UNSPECIFIED.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct MqttConfig {
|
||||||
|
#[serde(rename = "hostname")]
|
||||||
|
host: String,
|
||||||
|
#[serde(default = "default_mqtt_port")]
|
||||||
|
port: u16,
|
||||||
|
#[serde(flatten)]
|
||||||
|
credentials: Option<Credentials>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_mqtt_port() -> u16 {
|
||||||
|
1883
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Credentials {
|
||||||
|
Raw {
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
},
|
||||||
|
File {
|
||||||
|
username: String,
|
||||||
|
password_file: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl Credentials {
|
||||||
|
pub fn username(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Credentials::Raw { username, .. } => username.clone(),
|
||||||
|
Credentials::File { username, .. } => username.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn password(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Credentials::Raw { password, .. } => password.clone(),
|
||||||
|
Credentials::File { password_file, .. } => secretfile::load(password_file).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn from_env() -> Result<Config, MainError> {
|
||||||
|
let mut env: HashMap<String, String> = dotenvy::vars().collect();
|
||||||
|
let port = env
|
||||||
|
.get("PORT")
|
||||||
|
.and_then(|s| u16::from_str(s).ok())
|
||||||
|
.unwrap_or(80);
|
||||||
|
|
||||||
|
let names = env.remove("NAMES").unwrap_or_default();
|
||||||
|
let names = names
|
||||||
|
.split(',')
|
||||||
|
.map(|pair| {
|
||||||
|
let mut parts = pair.split('=');
|
||||||
|
if let (Some(Ok(mac)), Some(name)) =
|
||||||
|
(parts.next().map(BDAddr::from_str), parts.next())
|
||||||
|
{
|
||||||
|
Ok((mac, name.to_string()))
|
||||||
|
} else {
|
||||||
|
Err(MainError::from("Invalid NAMES"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<BTreeMap<BDAddr, String>, MainError>>()?;
|
||||||
|
|
||||||
|
Ok(Config {
|
||||||
|
listen: ListenConfig::Ip {
|
||||||
|
port,
|
||||||
|
address: default_address(),
|
||||||
|
},
|
||||||
|
names,
|
||||||
|
mqtt: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config, MainError> {
|
||||||
|
let raw = read_to_string(path)?;
|
||||||
|
Ok(toml::from_str(&raw)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/main.rs
62
src/main.rs
|
|
@ -1,41 +1,41 @@
|
||||||
|
mod config;
|
||||||
|
|
||||||
use btleplug::api::{Central, Manager as _};
|
use btleplug::api::{Central, Manager as _};
|
||||||
use btleplug::platform::Manager;
|
use btleplug::platform::Manager;
|
||||||
use log::info;
|
use tracing::info;
|
||||||
use main_error::MainError;
|
use main_error::MainError;
|
||||||
use mitemp::{listen, BDAddr, Sensor};
|
use mitemp::{listen, BDAddr, Sensor};
|
||||||
use std::collections::HashMap;
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tokio::{pin, spawn};
|
use tokio::{pin, spawn};
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
use clap::Parser;
|
||||||
|
use tokio::net::UnixListener;
|
||||||
|
use tokio_stream::wrappers::UnixListenerStream;
|
||||||
|
use crate::config::{Config, ListenConfig};
|
||||||
|
|
||||||
type Cache = Arc<Mutex<HashMap<BDAddr, Sensor>>>;
|
type Cache = Arc<Mutex<HashMap<BDAddr, Sensor>>>;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Config file to use, if omitted the config will be loaded from environment variables
|
||||||
|
config: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), MainError> {
|
async fn main() -> Result<(), MainError> {
|
||||||
env_logger::init();
|
tracing_subscriber::fmt::init();
|
||||||
let cache: Cache = Arc::default();
|
let cache: Cache = Arc::default();
|
||||||
|
|
||||||
let mut env: HashMap<String, String> = dotenv::vars().collect();
|
let args = Args::parse();
|
||||||
let port = env
|
let config = match args.config {
|
||||||
.get("PORT")
|
Some(path) => Config::from_file(path)?,
|
||||||
.and_then(|s| u16::from_str(s).ok())
|
_ => Config::from_env()?,
|
||||||
.unwrap_or(80);
|
};
|
||||||
let names = env.remove("NAMES").unwrap_or_default();
|
info!("{} devices configured", config.names.len());
|
||||||
let names = names
|
|
||||||
.split(',')
|
|
||||||
.map(|pair| {
|
|
||||||
let mut parts = pair.split('=');
|
|
||||||
if let (Some(Ok(mac)), Some(name)) = (parts.next().map(BDAddr::from_str), parts.next())
|
|
||||||
{
|
|
||||||
Ok((mac, name.to_string()))
|
|
||||||
} else {
|
|
||||||
Err(MainError::from("Invalid NAMES"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Result<HashMap<BDAddr, String>, MainError>>()?;
|
|
||||||
|
|
||||||
let manager = Manager::new().await?;
|
let manager = Manager::new().await?;
|
||||||
for adapter in manager.adapters().await? {
|
for adapter in manager.adapters().await? {
|
||||||
|
|
@ -60,6 +60,7 @@ async fn main() -> Result<(), MainError> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let names = config.names;
|
||||||
let metrics = warp::path!("metrics").map(move || {
|
let metrics = warp::path!("metrics").map(move || {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
|
||||||
|
|
@ -70,7 +71,16 @@ async fn main() -> Result<(), MainError> {
|
||||||
result
|
result
|
||||||
});
|
});
|
||||||
|
|
||||||
warp::serve(metrics).run(([0, 0, 0, 0], port)).await;
|
match config.listen {
|
||||||
|
ListenConfig::Ip { address, port } => {
|
||||||
|
warp::serve(metrics).run((address, port)).await;
|
||||||
|
}
|
||||||
|
ListenConfig::Unix { socket: path } => {
|
||||||
|
let listener = UnixListener::bind(path).unwrap();
|
||||||
|
let incoming = UnixListenerStream::new(listener);
|
||||||
|
warp::serve(metrics).run_incoming(incoming).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +88,7 @@ async fn main() -> Result<(), MainError> {
|
||||||
fn format<W: Write>(
|
fn format<W: Write>(
|
||||||
mut writer: W,
|
mut writer: W,
|
||||||
sensor: &Sensor,
|
sensor: &Sensor,
|
||||||
names: &HashMap<BDAddr, String>,
|
names: &BTreeMap<BDAddr, String>,
|
||||||
) -> std::fmt::Result {
|
) -> std::fmt::Result {
|
||||||
if sensor.data.temperature == 0.0 || sensor.data.humidity == 0.0 {
|
if sensor.data.temperature == 0.0 || sensor.data.humidity == 0.0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
@ -92,7 +102,7 @@ fn format<W: Write>(
|
||||||
name, sensor.mac, sensor.data.battery
|
name, sensor.mac, sensor.data.battery
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
info!("Skipping unnamed censor {}", sensor.mac);
|
info!("Skipping unnamed sensor {}", sensor.mac);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
|
|
@ -107,7 +117,7 @@ fn format<W: Write>(
|
||||||
name, sensor.mac, sensor.data.humidity
|
name, sensor.mac, sensor.data.humidity
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
info!("Skipping unnamed censor {}", sensor.mac);
|
info!("Skipping unnamed sensor {}", sensor.mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue