chore: extract python_utils and home-assistant to their own crates
This commit is contained in:
39
home-assistant/src/event/context/context.rs
Normal file
39
home-assistant/src/event/context/context.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use super::id::Id;
|
||||
use once_cell::sync::OnceCell;
|
||||
use pyo3::{prelude::*, types::PyType};
|
||||
|
||||
/// The context that triggered something.
|
||||
#[derive(Debug, FromPyObject)]
|
||||
pub struct Context<Event> {
|
||||
pub id: Id,
|
||||
pub user_id: Option<String>,
|
||||
pub parent_id: Option<String>,
|
||||
/// In order to prevent cycles, the user must decide to pass [`Py<PyAny>`] for the `Event` type here
|
||||
/// or for the `Context` type in [`Event`]
|
||||
pub origin_event: Event,
|
||||
}
|
||||
|
||||
impl<'py, Event: IntoPyObject<'py>> IntoPyObject<'py> for Context<Event> {
|
||||
type Target = PyAny;
|
||||
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
|
||||
type Error = PyErr;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
static HOMEASSISTANT_CORE: OnceCell<Py<PyModule>> = OnceCell::new();
|
||||
|
||||
let homeassistant_core = HOMEASSISTANT_CORE
|
||||
.get_or_try_init(|| Result::<_, PyErr>::Ok(py.import("homeassistant.core")?.unbind()))?
|
||||
.bind(py);
|
||||
|
||||
let context_class = homeassistant_core.getattr("Context")?;
|
||||
let context_class = context_class.downcast_into::<PyType>()?;
|
||||
|
||||
let context_instance = context_class.call1((self.user_id, self.parent_id, self.id))?;
|
||||
|
||||
context_instance.setattr("origin_event", self.origin_event)?;
|
||||
|
||||
Ok(context_instance)
|
||||
}
|
||||
}
|
||||
38
home-assistant/src/event/context/id.rs
Normal file
38
home-assistant/src/event/context/id.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::convert::Infallible;
|
||||
|
||||
use pyo3::{prelude::*, types::PyString};
|
||||
use smol_str::SmolStr;
|
||||
use ulid::Ulid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Id {
|
||||
Ulid(Ulid),
|
||||
Other(SmolStr),
|
||||
}
|
||||
|
||||
impl<'py> FromPyObject<'py> for Id {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let s = ob.extract::<String>()?;
|
||||
|
||||
if let Ok(ulid) = s.parse() {
|
||||
Ok(Id::Ulid(ulid))
|
||||
} else {
|
||||
Ok(Id::Other(s.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoPyObject<'py> for Id {
|
||||
type Target = PyString;
|
||||
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
match self {
|
||||
Id::Ulid(ulid) => ulid.to_string().into_pyobject(py),
|
||||
Id::Other(id) => id.as_str().into_pyobject(py),
|
||||
}
|
||||
}
|
||||
}
|
||||
2
home-assistant/src/event/context/mod.rs
Normal file
2
home-assistant/src/event/context/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod context;
|
||||
pub mod id;
|
||||
27
home-assistant/src/event/event.rs
Normal file
27
home-assistant/src/event/event.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use super::event_origin::EventOrigin;
|
||||
|
||||
/// Representation of an event within the bus.
|
||||
#[derive(Debug, FromPyObject)]
|
||||
pub struct Event<Type, Data, Context> {
|
||||
pub event_type: Type,
|
||||
pub data: Data,
|
||||
pub origin: EventOrigin,
|
||||
/// In order to prevent cycles, the user must decide to pass [`Py<PyAny>`] for the `Context` type here
|
||||
/// or for the `Event` type in [`Context`]
|
||||
pub context: Context,
|
||||
time_fired_timestamp: f64,
|
||||
}
|
||||
|
||||
impl<Type, Data, Context> Event<Type, Data, Context> {
|
||||
pub fn time_fired(&self) -> Option<DateTime<Utc>> {
|
||||
const NANOS_PER_SEC: i32 = 1_000_000_000;
|
||||
|
||||
let secs = self.time_fired_timestamp as i64;
|
||||
let nsecs = (self.time_fired_timestamp.fract() * (NANOS_PER_SEC as f64)) as u32;
|
||||
|
||||
DateTime::from_timestamp(secs, nsecs)
|
||||
}
|
||||
}
|
||||
21
home-assistant/src/event/event_origin.rs
Normal file
21
home-assistant/src/event/event_origin.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::{exceptions::PyValueError, prelude::*};
|
||||
|
||||
#[derive(Debug, Clone, strum::EnumString, strum::Display)]
|
||||
#[strum(serialize_all = "UPPERCASE")]
|
||||
pub enum EventOrigin {
|
||||
Local,
|
||||
Remote,
|
||||
}
|
||||
|
||||
impl<'py> FromPyObject<'py> for EventOrigin {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let s = ob.str()?;
|
||||
let s = s.extract()?;
|
||||
let event_origin =
|
||||
EventOrigin::from_str(s).map_err(|err| PyValueError::new_err(err.to_string()))?;
|
||||
|
||||
Ok(event_origin)
|
||||
}
|
||||
}
|
||||
4
home-assistant/src/event/mod.rs
Normal file
4
home-assistant/src/event/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod context;
|
||||
pub mod event;
|
||||
pub mod event_origin;
|
||||
pub mod specific;
|
||||
1
home-assistant/src/event/specific/mod.rs
Normal file
1
home-assistant/src/event/specific/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod state_changed;
|
||||
58
home-assistant/src/event/specific/state_changed.rs
Normal file
58
home-assistant/src/event/specific/state_changed.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use pyo3::exceptions::PyValueError;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use crate::{entity_id::EntityId, state_object::StateObject};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Type;
|
||||
|
||||
impl<'py> FromPyObject<'py> for Type {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let s = ob.extract::<&str>()?;
|
||||
|
||||
if s == "state_changed" {
|
||||
Ok(Type)
|
||||
} else {
|
||||
Err(PyValueError::new_err(format!(
|
||||
"expected a string of value 'state_changed', but got {s}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPyObject)]
|
||||
#[pyo3(from_item_all)]
|
||||
pub struct Data<
|
||||
OldState,
|
||||
OldAttributes,
|
||||
OldStateContextEvent,
|
||||
NewState,
|
||||
NewAttributes,
|
||||
NewStateContextEvent,
|
||||
> {
|
||||
pub entity_id: EntityId,
|
||||
pub old_state: Option<StateObject<OldState, OldAttributes, OldStateContextEvent>>,
|
||||
pub new_state: Option<StateObject<NewState, NewAttributes, NewStateContextEvent>>,
|
||||
}
|
||||
|
||||
/// A state changed event is fired when on state write the state is changed.
|
||||
pub type Event<
|
||||
OldState,
|
||||
OldAttributes,
|
||||
OldStateContextEvent,
|
||||
NewState,
|
||||
NewAttributes,
|
||||
NewStateContextEvent,
|
||||
Context,
|
||||
> = super::super::event::Event<
|
||||
Type,
|
||||
Data<
|
||||
OldState,
|
||||
OldAttributes,
|
||||
OldStateContextEvent,
|
||||
NewState,
|
||||
NewAttributes,
|
||||
NewStateContextEvent,
|
||||
>,
|
||||
Context,
|
||||
>;
|
||||
Reference in New Issue
Block a user