Compare commits

...

2 Commits

Author SHA1 Message Date
J / Jacob Babich
8b966c1210 feat: make power sensor signal support any numeric type 2026-07-15 00:57:59 -04:00
J / Jacob Babich
a2d6d9f4c2 feat: make the input_number attributes and signal support any numeric type 2026-07-15 00:52:01 -04:00
4 changed files with 54 additions and 25 deletions

View File

@@ -4,12 +4,12 @@ use super::InputNumberMode;
#[derive(Debug, FromPyObject)] #[derive(Debug, FromPyObject)]
#[pyo3(from_item_all)] #[pyo3(from_item_all)]
pub struct InputNumberAttributes { pub struct InputNumberAttributes<Numeric> {
initial: Option<f64>, initial: Option<Numeric>,
editable: bool, editable: bool,
min: f64, min: Numeric,
max: f64, max: Numeric,
step: f64, step: Numeric,
mode: InputNumberMode, mode: InputNumberMode,
// todo: CustomUnitOfMeasurement type? probably not? // todo: CustomUnitOfMeasurement type? probably not?
unit_of_measurement: Option<String>, unit_of_measurement: Option<String>,

View File

@@ -1,7 +1,7 @@
use std::{future::Future, sync::Arc}; use std::{future::Future, str::FromStr, sync::Arc};
use emitter_and_signal::{Signal, SignalExt}; use emitter_and_signal::{Signal, SignalExt};
use pyo3::{Py, PyAny, PyErr, Python}; use pyo3::{FromPyObject, Py, PyAny, PyErr, Python};
use snafu::{ResultExt, Snafu}; use snafu::{ResultExt, Snafu};
use crate::{ use crate::{
@@ -32,26 +32,30 @@ pub enum CreateSignalError {
}, },
} }
pub fn signal<'py>( pub fn signal<
'py,
Numeric: 'static + uom::num::Num + Clone + Send + Sync + FromStr + for<'a, 'py2> FromPyObject<'a, 'py2>,
>(
py: Python<'py>, py: Python<'py>,
home_assistant: &'py HomeAssistant, home_assistant: &'py HomeAssistant,
object_id: ObjectId, object_id: ObjectId,
) -> Result< ) -> Result<
( (
Signal<Option<Arc<Result<HomeAssistantState<f64>, StateObjectSignalError<Arc<PyErr>>>>>>, Signal<
Option<Arc<Result<HomeAssistantState<Numeric>, StateObjectSignalError<Arc<PyErr>>>>>,
>,
impl Future<Output = Result<(), emitter_and_signal::signal::JoinError>>, impl Future<Output = Result<(), emitter_and_signal::signal::JoinError>>,
), ),
CreateSignalError, CreateSignalError,
> { > {
let entity_id = EntityId(Domain::InputNumber, object_id); let entity_id = EntityId(Domain::InputNumber, object_id);
let (signal, task1) = let (signal, task1) = StateObject::<
StateObject::<HomeAssistantState<f64>, InputNumberAttributes, Py<PyAny>>::signal( HomeAssistantState<Numeric>,
py, InputNumberAttributes<Numeric>,
home_assistant, Py<PyAny>,
entity_id, >::signal(py, home_assistant, entity_id)
) .context(StateObjectSignalSnafu)?;
.context(StateObjectSignalSnafu)?;
let (signal, task2) = signal let (signal, task2) = signal
.map(|state_object_arc_result_option| { .map(|state_object_arc_result_option| {

View File

@@ -1,10 +1,19 @@
use std::{future::Future, sync::Arc}; use std::{future::Future, str::FromStr, sync::Arc};
use emitter_and_signal::{Signal, SignalExt}; use emitter_and_signal::{Signal, SignalExt};
use pyo3::{FromPyObject, Py, PyAny, PyErr, Python}; use pyo3::{FromPyObject, Py, PyAny, PyErr, Python};
use python_utils::{FromPyFromStr, ToStrToPy}; use python_utils::{FromPyFromStr, ToStrToPy};
use snafu::{ResultExt, Snafu}; use snafu::{ResultExt, Snafu};
use string_literal::StringLiteral; use string_literal::StringLiteral;
use uom::{
si::{
energy::btu,
power::{gigawatt, kilowatt, megawatt, milliwatt, terawatt, watt},
time::hour,
Units, SI,
},
Conversion,
};
use super::super::state_classes::measurement::Measurement; use super::super::state_classes::measurement::Measurement;
use crate::{ use crate::{
@@ -42,7 +51,7 @@ pub enum CreateSignalError {
}, },
} }
pub fn signal<'py>( pub fn signal<'py, V>(
py: Python<'py>, py: Python<'py>,
home_assistant: &'py HomeAssistant, home_assistant: &'py HomeAssistant,
object_id: ObjectId, object_id: ObjectId,
@@ -50,17 +59,33 @@ pub fn signal<'py>(
( (
Signal< Signal<
Option< Option<
Result<HomeAssistantState<uom::si::f64::Power>, StateObjectSignalError<Arc<PyErr>>>, Result<
HomeAssistantState<uom::si::quantities::Power<V>>,
StateObjectSignalError<Arc<PyErr>>,
>,
>, >,
>, >,
impl Future<Output = Result<(), emitter_and_signal::signal::JoinError>>, impl Future<Output = Result<(), emitter_and_signal::signal::JoinError>>,
), ),
CreateSignalError, CreateSignalError,
> { >
where
V: 'static + uom::num::Num + Clone + Send + Sync + FromStr,
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>,
{
let entity_id = EntityId(Domain::Sensor, object_id); let entity_id = EntityId(Domain::Sensor, object_id);
let (signal, task1) = let (signal, task1) =
StateObject::<HomeAssistantState<f64>, PowerSensorAttributes, Py<PyAny>>::signal( StateObject::<HomeAssistantState<V>, PowerSensorAttributes, Py<PyAny>>::signal(
py, py,
home_assistant, home_assistant,
entity_id, entity_id,
@@ -75,9 +100,9 @@ pub fn signal<'py>(
|StateObject { |StateObject {
state, attributes, .. state, attributes, ..
}| { }| {
state state.as_ref().map(|amount| {
.as_ref() attributes.unit_of_measurement.into_uom(amount.clone())
.map(|&amount| attributes.unit_of_measurement.into_uom(amount)) })
}, },
) )
.map_err(Clone::clone) .map_err(Clone::clone)

View File

@@ -34,7 +34,7 @@ pub enum UnitOfMeasurement {
impl UnitOfMeasurement { impl UnitOfMeasurement {
pub fn into_uom<V>(&self, amount: V) -> Power<V> pub fn into_uom<V>(&self, amount: V) -> Power<V>
where where
V: uom::num::Num + uom::Conversion<V, T = V>, V: uom::num::Num + Conversion<V, T = V>,
milliwatt: Conversion<V, T = V>, milliwatt: Conversion<V, T = V>,
watt: Conversion<V, T = V>, watt: Conversion<V, T = V>,
kilowatt: Conversion<V, T = V>, kilowatt: Conversion<V, T = V>,