bffh/src/modules/shelly.rs

85 lines
2.7 KiB
Rust
Raw Normal View History

2020-09-14 10:37:51 +02:00
use slog::Logger;
2020-09-15 14:31:10 +02:00
use crate::config::Settings;
2020-09-17 15:34:35 +02:00
use crate::registries::{Registries, Actuator, ActBox, StatusSignal};
use crate::error::Result;
2020-09-17 15:45:43 +02:00
use crate::machine::Status;
use std::pin::Pin;
use futures::prelude::*;
use futures::ready;
use futures::task::{Poll, Context, Waker};
2020-09-17 15:34:35 +02:00
use futures_signals::signal::Signal;
2020-09-14 10:37:51 +02:00
use paho_mqtt as mqtt;
2020-09-14 10:37:51 +02:00
// TODO: Late config parsing. Right now the config is validated at the very startup in its
// entirety. This works reasonably enough for this static modules here but if we do dynamic loading
// via dlopen(), lua API, python API etc it will not.
2020-09-15 14:31:10 +02:00
pub async fn run(log: Logger, config: Settings, registries: Registries) {
2020-09-17 14:32:53 +02:00
let shelly = Shelly::new(config).await;
2020-09-17 14:32:53 +02:00
let r = registries.actuators.register("shelly".to_string(), shelly).await;
2020-09-14 10:37:51 +02:00
}
/// An actuator for all Shellies connected listening on one MQTT broker
///
/// This actuator can power toggle an arbitrariy named shelly on the broker it is connected to. If
/// you need to toggle shellies on multiple brokers you need multiple instanced of this actuator.
struct Shelly {
2020-09-17 15:34:35 +02:00
signal: Option<StatusSignal>,
waker: Option<Waker>,
2020-09-17 15:34:35 +02:00
name: String,
client: mqtt::AsyncClient,
}
impl Shelly {
2020-09-17 14:34:57 +02:00
// Can't use Error, it's not Send. fabinfra/fabaccess/bffh#7
2020-09-17 14:32:53 +02:00
pub async fn new(config: Settings) -> ActBox {
let client = mqtt::AsyncClient::new(config.shelly.unwrap().mqtt_url).unwrap();
2020-09-17 14:32:53 +02:00
client.connect(mqtt::ConnectOptions::new()).await.unwrap();
2020-09-17 15:34:35 +02:00
let name = "test".to_string();
let signal: Option<StatusSignal> = None;
let waker = None;
2020-09-17 15:34:35 +02:00
Box::new(Shelly { signal, waker, name, client })
}
2020-09-14 10:37:51 +02:00
}
2020-09-14 10:37:51 +02:00
impl Actuator for Shelly {
2020-09-17 16:05:46 +02:00
fn subscribe(&mut self, signal: StatusSignal) {
2020-09-17 15:36:42 +02:00
self.signal.replace(signal);
if let Some(waker) = self.waker.take() {
waker.wake();
}
}
2020-09-14 10:37:51 +02:00
}
2020-09-17 15:34:35 +02:00
impl Stream for Shelly {
type Item = future::BoxFuture<'static, ()>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
let unpin = Pin::into_inner(self);
if let Some(ref mut s) = unpin.signal {
2020-09-17 15:45:43 +02:00
if let Some(status) = ready!(Signal::poll_change(Pin::new(s), cx)) {
let topic = format!("shellies/{}/relay/0/command", unpin.name);
let pl = match status {
2020-09-17 15:47:41 +02:00
Status::Free | Status::Blocked => "off",
2020-09-17 15:45:43 +02:00
Status::Occupied => "on",
};
let msg = mqtt::Message::new(topic, pl, 0);
let f = unpin.client.publish(msg).map(|_| ());
return Poll::Ready(Some(Box::pin(f)));
}
} else {
unpin.waker.replace(cx.waker().clone());
2020-09-17 15:34:35 +02:00
}
2020-09-17 15:45:43 +02:00
Poll::Pending
2020-09-17 15:34:35 +02:00
}
}