chore+feat(home-assistant): update to pyo3 0.27 and update extraction errors, switch out SmolStr for Arc<str>, tighten up light service calls and implement some for notify, start implementing units of measurement like for power

This commit is contained in:
2026-01-07 02:10:03 -05:00
parent 97aef026b2
commit fa36b39e81
35 changed files with 1255 additions and 259 deletions

View File

@@ -0,0 +1 @@
pub mod power;

View File

@@ -0,0 +1,102 @@
use std::str::FromStr;
use pyo3::{
exceptions::{PyException, PyValueError},
prelude::*,
};
use snafu::{ResultExt, Snafu};
use strum::EnumString;
use uom::{
si::{
energy::btu,
power::{gigawatt, kilowatt, megawatt, milliwatt, terawatt, watt},
quantities::{Energy, Power, Time},
time::hour,
Units, SI,
},
Conversion,
};
/// Power units
#[derive(Debug, Clone, Copy, EnumString, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum UnitOfMeasurement {
#[strum(serialize = "mW")]
MilliWatt,
#[strum(serialize = "W")]
Watt,
#[strum(serialize = "kW")]
KiloWatt,
#[strum(serialize = "MW")]
MegaWatt,
#[strum(serialize = "GW")]
GigaWatt,
#[strum(serialize = "TW")]
TeraWatt,
#[strum(serialize = "BTU/h")]
BtuPerhour,
}
#[derive(Debug, Snafu)]
pub enum ExtractUnitOfMeasurementError {
/// couldn't extract the object as a string
ExtractStringError { source: PyErr },
/// couldn't parse the string as a [`UnitOfMeasurement`]
ParseError {
source: <UnitOfMeasurement as FromStr>::Err,
},
}
impl From<ExtractUnitOfMeasurementError> for PyErr {
fn from(error: ExtractUnitOfMeasurementError) -> Self {
match &error {
ExtractUnitOfMeasurementError::ExtractStringError { .. } => {
PyException::new_err(error.to_string())
}
ExtractUnitOfMeasurementError::ParseError { .. } => {
PyValueError::new_err(error.to_string())
}
}
}
}
// TODO: replace with a derive(PyFromStr) (analogous to serde_with::DeserializeFromStr) once I make one
impl<'a, 'py> FromPyObject<'a, 'py> for UnitOfMeasurement {
type Error = ExtractUnitOfMeasurementError;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
let s = obj.extract().context(ExtractStringSnafu)?;
let unit_of_measurement = UnitOfMeasurement::from_str(s).context(ParseSnafu)?;
Ok(unit_of_measurement)
}
}
impl UnitOfMeasurement {
pub fn into_uom<V>(&self, amount: V) -> Power<V>
where
V: uom::num::Num + uom::Conversion<V, T = V>,
milliwatt: Conversion<V, T = V>,
watt: Conversion<V, T = V>,
kilowatt: Conversion<V, T = V>,
megawatt: Conversion<V, T = V>,
gigawatt: Conversion<V, T = V>,
terawatt: Conversion<V, T = V>,
btu: Conversion<V, T = V>,
hour: Conversion<V, T = V>,
SI<V>: Units<V>,
{
match self {
UnitOfMeasurement::MilliWatt => Power::new::<milliwatt>(amount),
UnitOfMeasurement::Watt => Power::new::<watt>(amount),
UnitOfMeasurement::KiloWatt => Power::new::<kilowatt>(amount),
UnitOfMeasurement::MegaWatt => Power::new::<megawatt>(amount),
UnitOfMeasurement::GigaWatt => Power::new::<gigawatt>(amount),
UnitOfMeasurement::TeraWatt => Power::new::<terawatt>(amount),
UnitOfMeasurement::BtuPerhour => {
Energy::new::<btu>(amount) / Time::new::<hour>(V::one())
}
}
}
}