mirror of
https://codeberg.org/icewind/taspromto.git
synced 2026-08-02 12:14:45 +02:00
155 lines
3.9 KiB
Rust
155 lines
3.9 KiB
Rust
use std::borrow::Cow;
|
|
use std::fmt::{self, Display};
|
|
use std::num::ParseIntError;
|
|
use std::str::FromStr;
|
|
|
|
use serde::{Deserialize, Deserializer, de::Error};
|
|
|
|
use crate::device::FormattableDevice;
|
|
|
|
#[derive(Debug)]
|
|
pub struct TempState {
|
|
pub name: String,
|
|
pub id: RfDeviceId,
|
|
pub temperature: f32,
|
|
pub humidity: u8,
|
|
}
|
|
|
|
impl TempState {
|
|
pub fn new(id: RfDeviceId, name: String) -> Self {
|
|
TempState {
|
|
name,
|
|
id,
|
|
temperature: 0.0,
|
|
humidity: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FormattableDevice for TempState {
|
|
fn format_open_metrics(&self) -> impl Display + '_ {
|
|
TempFormatter { device: self }
|
|
}
|
|
}
|
|
|
|
struct TempFormatter<'a> {
|
|
device: &'a TempState,
|
|
}
|
|
|
|
impl Display for TempFormatter<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if self.device.temperature > 0.0 {
|
|
writeln!(
|
|
f,
|
|
"sensor_temperature{{model=\"{}\", id=\"{}\", channel=\"{}\", name=\"{}\"}} {}",
|
|
self.device.id.name,
|
|
self.device.id.id,
|
|
self.device.id.channel,
|
|
self.device.name,
|
|
self.device.temperature
|
|
)?;
|
|
}
|
|
|
|
if self.device.humidity > 0 {
|
|
writeln!(
|
|
f,
|
|
"sensor_humidity{{model=\"{}\", id=\"{}\", channel=\"{}\", name=\"{}\"}} {}",
|
|
self.device.id.name,
|
|
self.device.id.id,
|
|
self.device.id.channel,
|
|
self.device.name,
|
|
self.device.humidity
|
|
)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub struct RfPayload<'a> {
|
|
pub name: &'a str,
|
|
pub id: u16,
|
|
pub channel: u8,
|
|
pub battery: bool,
|
|
pub temperature: f32,
|
|
pub humidity: u8,
|
|
}
|
|
|
|
impl<'a> RfPayload<'a> {
|
|
pub fn device_id(&self) -> RfDeviceId {
|
|
RfDeviceId {
|
|
name: self.name.into(),
|
|
id: self.id,
|
|
channel: self.channel,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq, Debug, Default, Clone)]
|
|
pub struct RfDeviceId {
|
|
pub name: String,
|
|
pub id: u16,
|
|
pub channel: u8,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for RfDeviceId {
|
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let str = <Cow<'de, str>>::deserialize(deserializer)?;
|
|
Self::from_str(&str).map_err(D::Error::custom)
|
|
}
|
|
}
|
|
|
|
impl FromStr for RfDeviceId {
|
|
type Err = ParseIntError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let mut parts = s.splitn(3, ':');
|
|
let name = parts.next().unwrap_or_default();
|
|
let id = parts.next().unwrap_or_default().parse()?;
|
|
let channel = parts.next().unwrap_or_default().parse()?;
|
|
Ok(RfDeviceId {
|
|
name: name.into(),
|
|
id,
|
|
channel,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub 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()?;
|
|
let channel = parts.next()?.strip_prefix("CHN=")?.parse().ok()?;
|
|
let battery = parts.next()?.strip_prefix("BAT=")? == "OK";
|
|
let temperature = parts.next()?.strip_prefix("TEMP=")?;
|
|
let temperature = u32::from_str_radix(temperature, 16).ok()?;
|
|
let humidity = parts.next()?.strip_prefix("HUM=")?.parse().ok()?;
|
|
|
|
Some(RfPayload {
|
|
name,
|
|
id,
|
|
channel,
|
|
battery,
|
|
temperature: temperature as f32 / 10.0,
|
|
humidity,
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn test_rf_payload() {
|
|
assert_eq!(
|
|
RfPayload {
|
|
name: "Bresser-3CH",
|
|
id: 49,
|
|
channel: 1,
|
|
battery: true,
|
|
temperature: 16.1,
|
|
humidity: 58
|
|
},
|
|
parse_rf_payload("20;1E;Bresser-3CH;ID=49;CHN=0001;BAT=OK;TEMP=00a1;HUM=58;").unwrap()
|
|
)
|
|
}
|