Hey! I am working on a webassembly version of homeassistant at https://litehouse.arlyon.dev/docs. It is very much in its infancy but it's fun to hack on. My next goal is building a workflow engine to connect inputs (state conditions like 'switch pressed') to outputs (actions in the world like 'toggle light').
I am trying to figure out a good way to structure the plugins. The base plugin is implemented as a wit resource, and I am trying to find a decent pattern for modelling other interaction types (switch, notifier, media-player, etc). I would like the base plugin resource to be able to return a list of 'inputs' back to the host, which are other resources that it implements, such as 'turn switch on', 'turn switch off', 'play media'. Then the dynamic dispatch could be managed by the host rather than by the guest plugin.
Options:
call(string, string)
dispatch thing and pass serialized args back and forth. extra flexible but not great ergonomics when it comes to writing plugins, and discovering the plugin api.I would like to do #1, are there any examples of this kind of thing in the wild I could look at? In the 1st case, is it even possible for the guest to instantiate other resources? I could return some data and have the host do it, but I can imagine a plugin author may want to share resources between a base plugin implementation and one of the resources it implements (or, in rust terms, have the same struct impl many traits). Are there facilities for doing this? Are there perhaps APIs to transmute a handle into another Resource type? In regards to versioning these interfaces, what happens if I link 0.1.0 of some wit and 0.2.0 at the same time? presumably it 'just works'?
Thanks for taking the time to read :)
interface plugin {
// omitted
variant output {
switch(string),
media(string),
notify(string),
}
resource runner {
constructor(nickname: string, config: option<string>);
subscribe: func() -> result<list<subscription>, u32>;
update: func(events: list<event>) -> result<bool, u32>;
outputs: func() -> result<list<output>, u32>;
}
}
interface media {
// An additional interface for plugins that can play media
resource media {
constructor(nickname: string);
play: func() -> result<bool, u32>;
pause: func() -> result<bool, u32>;
stop: func() -> result<bool, u32>;
next: func() -> result<bool, u32>;
previous: func() -> result<bool, u32>;
}
}
interface switch {
// An additional interface for plugins that can be turned on and off
resource switch {
constructor(nickname: string);
on: func() -> result<bool, u32>;
off: func() -> result<bool, u32>;
toggle: func() -> result<bool, u32>;
}
}
interface notify {
// An additional interface for plugins that can notify the user
resource notify {
constructor(nickname: string);
notify: func(message: string) -> result<bool, u32>;
}
}
Hey that's really neat! As a current user of home assistant I've thought about doing this sort of thing for a long time!
perhaps these worlds could be pluggable themselves rather than bundled with the server.
Are you hoping for build-time plugins or runtime? I designed a system that does the former: Spin Factors. It's definitely a significant chunk of work but doable.
Runtime plugins are also possible but would require a much deeper understanding of the component model as you wouldn't be able to rely on bindings generation on the host (as it currently exists)
Right now the server and plugin use the same WIT, and I am ok with keeping it that way for now. There are few enough people writing plugins (1 :joy:) that I can update all the plugins after a breaking change to the WIT. so for now, lets assume the server knows all the possible additional resources at build time
Spin factors looks like it resolves the issue that the server needs to know of all the resources ahead of time so perhaps that is worth pursuing down the line!
So I guess to restate the immediate issue, the guests should have some facility to expose to the host what subset of these available resources it is capable of. I was hoping to do that via instantiating resources inside the guest (returning types that implement traits) for the sake of plugin author ergonomics.
Hey Alexander, this is a great project. If you stick to WIT, you'll undoubtedly end up with lots of implementations that interoperate. The "dynamic" part is in fact hard at the moment, of course. Pursuing Spin is one path, because it's oss and based on components......
Last updated: Nov 22 2024 at 16:03 UTC