homecore_plugins/plugin.rs
1//! `HomeCorePlugin` trait + `PluginId` newtype.
2//!
3//! Every first-party and third-party HOMECORE integration must implement
4//! `HomeCorePlugin`. P1 provides an in-process native Rust implementation;
5//! the WASM ABI wrapper (which maps the WASM exports `setup_entry`,
6//! `call_service_handler`, `receive_event` to this trait) lands in P2.
7
8use std::fmt;
9
10use async_trait::async_trait;
11use homecore::HomeCore;
12
13use crate::error::PluginError;
14use crate::StateChangedEventJson;
15
16/// Unique identifier for a loaded plugin — mirrors the `domain` field of
17/// the plugin's `PluginManifest` (e.g. `"mqtt"`, `"homecore_lights"`).
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct PluginId(pub String);
20
21impl PluginId {
22 /// Create a new `PluginId` from any string-like value.
23 pub fn new(s: impl Into<String>) -> Self {
24 Self(s.into())
25 }
26
27 /// Return the inner domain string.
28 pub fn as_str(&self) -> &str {
29 &self.0
30 }
31}
32
33impl fmt::Display for PluginId {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 f.write_str(&self.0)
36 }
37}
38
39/// Lifecycle trait that every HOMECORE integration must implement.
40///
41/// Implementing types are passed to [`PluginRuntime::load`]; the runtime
42/// calls these methods at the appropriate lifecycle points.
43///
44/// # Async
45/// Both methods are `async` to allow network / IO initialisation without
46/// blocking the Tokio runtime. The `async_trait` macro erases the `impl`
47/// return type so it works in trait objects.
48#[async_trait]
49pub trait HomeCorePlugin: Send + Sync + 'static {
50 /// Called once when the plugin's config entry is being set up.
51 ///
52 /// The plugin receives a reference to the `HomeCore` runtime and should
53 /// register its entities, services, and event subscriptions here.
54 async fn setup(&self, hc: HomeCore) -> Result<(), PluginError>;
55
56 /// Receive a committed state change. The default is a no-op so built-in
57 /// plugins only opt into event work they need.
58 async fn state_changed(&self, _event: &StateChangedEventJson) -> Result<(), PluginError> {
59 Ok(())
60 }
61
62 /// Called when the plugin is being removed from the registry.
63 ///
64 /// The plugin should clean up subscriptions and deregister its entities.
65 async fn unload(&self) -> Result<(), PluginError>;
66}