prevent all lights having their state set on startup

+ some cleanup
This commit is contained in:
LordMZTE 2021-11-08 21:50:29 +01:00
parent c610f8b798
commit c02ff988c1
5 changed files with 114 additions and 43 deletions

View file

@ -23,13 +23,14 @@ pub enum HeaderBarMsg {
#[widget]
impl Widget for HeaderBar {
fn model(data: (UnboundedSender<RuntimeMsg>, Sender<Msg>, String, String)) -> HeaderBarModel {
fn model(data: (UnboundedSender<RuntimeMsg>, Sender<Msg>)) -> HeaderBarModel {
HeaderBarModel {
settings: relm::init::<Settings>((data.0.clone(), data.2, data.3))
settings: relm::init::<Settings>(data.0.clone())
.expect("failed to create settings window"),
rootwintx: data.1,
}
}
fn update(&mut self, msg: HeaderBarMsg) {
match msg {
HeaderBarMsg::OpenSettings => {

View file

@ -1,8 +1,9 @@
use relm::Relm;
use angular_units::Turns;
use gtk::{prelude::*, Orientation};
use log::debug;
use prisma::{Hsv, Rgb};
use relm::Widget;
use relm::{Channel, Sender, Widget};
use relm_derive::{widget, Msg};
use rhue::{
api::{Light, LightArchetype},
@ -14,6 +15,8 @@ use tokio::sync::mpsc::UnboundedSender;
use crate::runtime::RuntimeMsg;
pub struct LightEntryModel {
_channel: Channel<LightEntryMsg>,
tx: Sender<LightEntryMsg>,
id: String,
name: String,
runtime: UnboundedSender<RuntimeMsg>,
@ -21,12 +24,18 @@ pub struct LightEntryModel {
brightness: f64,
color: Rc<Cell<Rgb<f64>>>,
icon: String,
updates_active: bool,
}
#[derive(Clone, Msg)]
pub enum LightEntryMsg {
Update(StateUpdate),
PropertyChanged(EntryLightProperty),
/// Changing the values of the light's GUI elements before this message is
/// sent has no effect. This is to prevent light update requests being
/// sent to the bridge while their values are being set programatically
/// in the widget's initialization
ActivateUpdates,
}
#[derive(Clone, PartialEq, Eq)]
@ -37,8 +46,16 @@ pub enum EntryLightProperty {
#[widget]
impl Widget for LightEntry {
fn model(params: (String, Light, UnboundedSender<RuntimeMsg>)) -> LightEntryModel {
fn model(
relm: &Relm<Self>,
params: (String, Light, UnboundedSender<RuntimeMsg>),
) -> LightEntryModel {
let stream = relm.stream().clone();
let (ch, tx) = Channel::new(move |msg| stream.emit(msg));
LightEntryModel {
_channel: ch,
tx,
id: params.0,
name: params.1.name,
runtime: params.2,
@ -53,6 +70,7 @@ impl Widget for LightEntry {
.into(),
)),
icon: resource_of_archetype(params.1.config.archetype),
updates_active: false,
}
}
@ -80,11 +98,13 @@ impl Widget for LightEntry {
self.model.on = on;
}
let _ = self
.model
.runtime
.send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd));
},
if self.model.updates_active {
let _ = self
.model
.runtime
.send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd));
}
}
LightEntryMsg::PropertyChanged(prop) => {
debug!("updating light {}", &self.model.id);
@ -110,11 +130,15 @@ impl Widget for LightEntry {
..Default::default()
};
let _ = self
.model
.runtime
.send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd));
},
if self.model.updates_active {
let _ = self
.model
.runtime
.send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd));
}
}
LightEntryMsg::ActivateUpdates => self.model.updates_active = true,
}
}
@ -133,6 +157,12 @@ impl Widget for LightEntry {
Inhibit(false)
});
// we need to send this throught the channel, instead of just setting
// `self.model.updates_active`, since messages of the widget's stream
// are only handled once the view has been fully initialized.
// This would result in the updates being activated before the state is set.
let _ = self.model.tx.send(LightEntryMsg::ActivateUpdates);
}
view! {

View file

@ -52,7 +52,7 @@ impl Widget for Win {
Model {
_channel: ch,
headerbar: relm::init::<HeaderBar>((params.0.clone(), tx.clone(), params.1, params.2))
headerbar: relm::init::<HeaderBar>((params.0.clone(), tx.clone()))
.expect("header init"),
runtime: params.0,
tx,

View file

@ -23,28 +23,35 @@ pub enum SettingsMsg {
Discover,
Register,
Save,
SetAddr(Option<String>),
SetUsername(Result<String, String>),
SetAddr(SettingsDataResponse),
SetUsername(SettingsDataResponse),
}
#[derive(Clone)]
pub enum SettingsDataResponse {
FailedWithError(String),
FailedWithoutError,
Success(String),
Unset,
}
#[widget]
impl Widget for Settings {
fn model(
relm: &Relm<Settings>,
params: (UnboundedSender<RuntimeMsg>, String, String),
) -> SettingsModel {
fn model(relm: &Relm<Settings>, runtime: UnboundedSender<RuntimeMsg>) -> SettingsModel {
let stream = relm.stream().clone();
let (ch, tx) = Channel::new(move |msg| stream.emit(msg));
let _ = runtime.send(RuntimeMsg::RequestConfig(tx.clone()));
SettingsModel {
_channel: ch,
runtime: params.0,
runtime,
tx,
status: String::new(),
addr_spinning: false,
username_spinning: false,
addr: params.1,
username: params.2,
addr: String::new(),
username: String::new(),
}
}
@ -81,25 +88,31 @@ impl Widget for Settings {
}
},
SettingsMsg::SetAddr(addr) => {
SettingsMsg::SetAddr(res) => {
self.model.addr_spinning = false;
if let Some(addr) = addr {
self.model.addr = addr;
} else {
self.model.status = "Couldn't find a Bridge.".to_string();
match res {
SettingsDataResponse::FailedWithoutError => {
self.model.status = "Couldn't find a Bridge.".to_string()
},
SettingsDataResponse::FailedWithError(e) => {
self.model.status = format!("Couldn't find a Bridge: {}", e)
},
SettingsDataResponse::Success(addr) => self.model.addr = addr,
SettingsDataResponse::Unset => {},
}
},
SettingsMsg::SetUsername(username) => {
SettingsMsg::SetUsername(res) => {
self.model.username_spinning = false;
match username {
Ok(u) => {
self.model.username = u;
match res {
SettingsDataResponse::FailedWithoutError => {
self.model.status = "Failed to register.".to_string()
},
Err(e) => {
self.model.status = format!("Failed to register at bridge: {}", e);
SettingsDataResponse::FailedWithError(e) => {
self.model.status = format!("Failed to register: {}", e)
},
SettingsDataResponse::Success(username) => self.model.username = username,
SettingsDataResponse::Unset => {},
}
},
}

View file

@ -4,7 +4,10 @@ use tokio::sync::{mpsc, RwLock};
use crate::{
config::{Config, ConfigData},
gui::{settings::SettingsMsg, Msg},
gui::{
settings::{SettingsDataResponse, SettingsMsg},
Msg,
},
};
use log::{error, info};
use miette::{miette, Context, IntoDiagnostic};
@ -123,20 +126,27 @@ impl Runtime {
RuntimeMsg::DiscoverBridge(tx) => {
let addr = rhue::discover_bridge(Arc::clone(&http), Duration::from_secs(5)).await?;
tx.send(SettingsMsg::SetAddr(addr.map(|u| u.to_string())))
.into_diagnostic()?;
tx.send(SettingsMsg::SetAddr(match addr {
Some(a) => SettingsDataResponse::Success(a.to_string()),
None => SettingsDataResponse::FailedWithoutError,
}))
.into_diagnostic()?;
},
RuntimeMsg::Register(url, tx) => {
match Bridge::register(http, "GUE", url).await?.to_result() {
Ok(b) => {
let username = b.username.clone();
*bridge.write().await = Some(b);
tx.send(SettingsMsg::SetUsername(Ok(username)))
.into_diagnostic()?;
tx.send(SettingsMsg::SetUsername(SettingsDataResponse::Success(
username,
)))
.into_diagnostic()?;
},
Err(e) => {
tx.send(SettingsMsg::SetUsername(Err(e.description)))
.into_diagnostic()?;
tx.send(SettingsMsg::SetUsername(
SettingsDataResponse::FailedWithError(e.description),
))
.into_diagnostic()?;
},
}
},
@ -146,6 +156,22 @@ impl Runtime {
bridge.update_light(&id, upd).await?;
}
},
RuntimeMsg::RequestConfig(tx) => {
let cfg = config.read().await;
tx.send(SettingsMsg::SetAddr(match &cfg.data.bridge_addr {
Some(a) => SettingsDataResponse::Success(a.to_string()),
None => SettingsDataResponse::Unset,
}))
.into_diagnostic()?;
tx.send(SettingsMsg::SetUsername(match &cfg.data.bridge_username {
Some(a) => SettingsDataResponse::Success(a.clone()),
None => SettingsDataResponse::Unset,
}))
.into_diagnostic()?;
},
}
Ok(false)
@ -159,4 +185,5 @@ pub enum RuntimeMsg {
DiscoverBridge(Sender<SettingsMsg>),
Register(Url, Sender<SettingsMsg>),
UpdateLight(String, StateUpdate),
RequestConfig(Sender<SettingsMsg>),
}