chore: extract python_utils and home-assistant to their own crates
This commit is contained in:
8
home-assistant/src/light/attributes.rs
Normal file
8
home-assistant/src/light/attributes.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Debug, FromPyObject)]
|
||||
#[pyo3(from_item_all)]
|
||||
pub struct LightAttributes {
|
||||
min_color_temp_kelvin: Option<u16>, // TODO: only here to allow compilation!
|
||||
max_color_temp_kelvin: Option<u16>, // TODO: only here to allow compilation!
|
||||
}
|
||||
54
home-assistant/src/light/mod.rs
Normal file
54
home-assistant/src/light/mod.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use attributes::LightAttributes;
|
||||
use pyo3::prelude::*;
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use state::LightState;
|
||||
|
||||
use crate::state::HomeAssistantState;
|
||||
|
||||
use super::{
|
||||
domain::Domain, entity_id::EntityId, home_assistant::HomeAssistant, object_id::ObjectId,
|
||||
state_object::StateObject,
|
||||
};
|
||||
|
||||
mod attributes;
|
||||
mod protocol;
|
||||
mod service;
|
||||
mod state;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HomeAssistantLight {
|
||||
pub home_assistant: HomeAssistant,
|
||||
pub object_id: ObjectId,
|
||||
}
|
||||
|
||||
impl HomeAssistantLight {
|
||||
fn entity_id(&self) -> EntityId {
|
||||
EntityId(Domain::Light, self.object_id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum GetStateObjectError {
|
||||
PythonError { source: PyErr },
|
||||
EntityMissing,
|
||||
}
|
||||
|
||||
impl HomeAssistantLight {
|
||||
fn get_state_object(
|
||||
&self,
|
||||
) -> Result<
|
||||
StateObject<HomeAssistantState<LightState>, LightAttributes, Py<PyAny>>,
|
||||
GetStateObjectError,
|
||||
> {
|
||||
Python::with_gil(|py| {
|
||||
let states = self.home_assistant.states(py).context(PythonSnafu)?;
|
||||
let entity_id = self.entity_id();
|
||||
let state_object = states
|
||||
.get(py, entity_id)
|
||||
.context(PythonSnafu)?
|
||||
.ok_or(GetStateObjectError::EntityMissing)?;
|
||||
|
||||
Ok(state_object)
|
||||
})
|
||||
}
|
||||
}
|
||||
108
home-assistant/src/light/protocol.rs
Normal file
108
home-assistant/src/light/protocol.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use super::service::{turn_off::TurnOff, turn_on::TurnOn};
|
||||
use super::{state::LightState, GetStateObjectError, HomeAssistantLight};
|
||||
use crate::{
|
||||
event::context::context::Context,
|
||||
state::{ErrorState, HomeAssistantState, UnexpectedState},
|
||||
};
|
||||
use arbitrary_value::arbitrary::Arbitrary;
|
||||
use protocol::light::Light;
|
||||
use pyo3::prelude::*;
|
||||
use snafu::{ResultExt, Snafu};
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum IsStateError {
|
||||
GetStateObjectError { source: GetStateObjectError },
|
||||
Error { state: ErrorState },
|
||||
UnexpectedError { state: UnexpectedState },
|
||||
}
|
||||
|
||||
impl Light for HomeAssistantLight {
|
||||
type IsOnError = IsStateError;
|
||||
|
||||
async fn is_on(&self) -> Result<bool, Self::IsOnError> {
|
||||
let state_object = self.get_state_object().context(GetStateObjectSnafu)?;
|
||||
let state = state_object.state;
|
||||
|
||||
match state {
|
||||
HomeAssistantState::Ok(light_state) => Ok(matches!(light_state, LightState::On)),
|
||||
HomeAssistantState::Err(state) => Err(IsStateError::Error { state }),
|
||||
HomeAssistantState::UnexpectedErr(state) => {
|
||||
Err(IsStateError::UnexpectedError { state })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type IsOffError = IsStateError;
|
||||
|
||||
async fn is_off(&self) -> Result<bool, Self::IsOffError> {
|
||||
let state_object = self.get_state_object().context(GetStateObjectSnafu)?;
|
||||
let state = state_object.state;
|
||||
|
||||
match state {
|
||||
HomeAssistantState::Ok(light_state) => Ok(matches!(light_state, LightState::Off)),
|
||||
HomeAssistantState::Err(state) => Err(IsStateError::Error { state }),
|
||||
HomeAssistantState::UnexpectedErr(state) => {
|
||||
Err(IsStateError::UnexpectedError { state })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TurnOnError = PyErr;
|
||||
|
||||
async fn turn_on(&mut self) -> Result<(), Self::TurnOnError> {
|
||||
let context: Option<Context<()>> = None;
|
||||
let target: Option<()> = None;
|
||||
|
||||
let services = Python::with_gil(|py| self.home_assistant.services(py))?;
|
||||
// TODO
|
||||
let service_response: Arbitrary = services
|
||||
.call_service(
|
||||
TurnOn {
|
||||
entity_id: self.entity_id(),
|
||||
},
|
||||
context,
|
||||
target,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// TODO
|
||||
#[cfg(feature = "tracing")]
|
||||
tracing::info!(?service_response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type TurnOffError = PyErr;
|
||||
|
||||
async fn turn_off(&mut self) -> Result<(), Self::TurnOffError> {
|
||||
let context: Option<Context<()>> = None;
|
||||
let target: Option<()> = None;
|
||||
|
||||
let services = Python::with_gil(|py| self.home_assistant.services(py))?;
|
||||
// TODO
|
||||
let service_response: Arbitrary // TODO: a type that validates as None
|
||||
= services
|
||||
.call_service(
|
||||
TurnOff {
|
||||
entity_id: self.entity_id(),
|
||||
},
|
||||
context,
|
||||
target,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// TODO
|
||||
#[cfg(feature = "tracing")]
|
||||
tracing::info!(?service_response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type ToggleError = PyErr;
|
||||
|
||||
async fn toggle(&mut self) -> Result<(), Self::ToggleError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
2
home-assistant/src/light/service/mod.rs
Normal file
2
home-assistant/src/light/service/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod turn_off;
|
||||
pub mod turn_on;
|
||||
33
home-assistant/src/light/service/turn_off.rs
Normal file
33
home-assistant/src/light/service/turn_off.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::IntoPyObject;
|
||||
|
||||
use crate::{
|
||||
entity_id::EntityId,
|
||||
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TurnOff {
|
||||
pub entity_id: EntityId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, IntoPyObject)]
|
||||
pub struct TurnOffServiceData {
|
||||
entity_id: EntityId,
|
||||
}
|
||||
|
||||
impl IntoServiceCall for TurnOff {
|
||||
type ServiceData = TurnOffServiceData;
|
||||
|
||||
fn into_service_call(self) -> (ServiceDomain, ServiceId, Self::ServiceData) {
|
||||
let service_domain = ServiceDomain::from_str("light").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
|
||||
let service_id = ServiceId::from_str("turn_off").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
|
||||
|
||||
let Self { entity_id } = self;
|
||||
|
||||
let service_data = TurnOffServiceData { entity_id };
|
||||
|
||||
(service_domain, service_id, service_data)
|
||||
}
|
||||
}
|
||||
32
home-assistant/src/light/service/turn_on.rs
Normal file
32
home-assistant/src/light/service/turn_on.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::IntoPyObject;
|
||||
|
||||
use crate::{
|
||||
entity_id::EntityId,
|
||||
service::{service_domain::ServiceDomain, service_id::ServiceId, IntoServiceCall},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TurnOn {
|
||||
pub entity_id: EntityId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, IntoPyObject)]
|
||||
pub struct TurnOnServiceData {
|
||||
entity_id: EntityId,
|
||||
}
|
||||
|
||||
impl IntoServiceCall for TurnOn {
|
||||
type ServiceData = TurnOnServiceData;
|
||||
|
||||
fn into_service_call(self) -> (ServiceDomain, ServiceId, Self::ServiceData) {
|
||||
let service_domain = ServiceDomain::from_str("light").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
|
||||
let service_id = ServiceId::from_str("turn_on").expect("statically written and known to be a valid slug; hoping to get compiler checks instead in the future");
|
||||
|
||||
let Self { entity_id } = self;
|
||||
let service_data = TurnOnServiceData { entity_id };
|
||||
|
||||
(service_domain, service_id, service_data)
|
||||
}
|
||||
}
|
||||
22
home-assistant/src/light/state.rs
Normal file
22
home-assistant/src/light/state.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::{exceptions::PyValueError, prelude::*};
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(Debug, Clone, EnumString, strum::Display)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum LightState {
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
|
||||
impl<'py> FromPyObject<'py> for LightState {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let s = ob.extract::<String>()?;
|
||||
|
||||
let state =
|
||||
LightState::from_str(&s).map_err(|err| PyValueError::new_err(err.to_string()))?;
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user