use dotenv::dotenv;
use hues::{
prelude::*,
service::{
CIEColor, ColorFeatureBasic, EffectType, LightAction, SceneAction, SceneBuilder,
SceneColorTempState, SceneEffectState, ScenePalette, ScenePaletteColor, SceneStatus,
Schedule, SignalType, SmartScene, TimeslotStart, Weekday, Zone, ZoneArchetype,
},
};
use rand::prelude::*;
use std::{net::IpAddr, time::Duration};
#[allow(dead_code)]
const ROOM_NAME: &'static str = "Office"; #[allow(dead_code)]
const ROOM_RENAME: &'static str = "Bat Cave";
#[allow(dead_code)]
const ZONE_NAME: &'static str = "Fun Zone"; #[allow(dead_code)]
const SCENE_NAME: &'static str = "Energize";
#[tokio::main]
async fn main() {
dotenv().ok();
let bridge = Bridge::new(
std::env::var("HUE_BRIDGE_IP")
.unwrap()
.parse::<IpAddr>()
.unwrap(),
std::env::var("HUE_APP_KEY").unwrap(),
)
.poll(Duration::from_secs(30))
.await;
let _ = bridge.refresh().await;
let _ = set_specific_light_colors(&bridge, "#bf07e8").await;
}
#[allow(dead_code)]
async fn toggle_room(bridge: &Bridge, name: &str) -> Result<(), HueAPIError> {
let room = bridge
.rooms()
.into_iter()
.find(|s| s.name() == name)
.unwrap();
room.toggle().await?;
Ok(())
}
#[allow(dead_code)]
async fn smart_scene_stuff(bridge: &Bridge) -> Result<(), HueAPIError> {
let _ = bridge
.delete_smart_scene("becae4e4-faa8-4a39-bc9d-cb16a30241db")
.await;
let scenes = bridge.scenes();
let galaxy = scenes.iter().find(|sc| sc.name() == "Galaxy").unwrap();
let mf = scenes
.iter()
.find(|sc| sc.name() == "Magic Forest")
.unwrap();
let diabs = scenes.iter().find(|sc| sc.name() == "Diabs").unwrap();
let _ss = bridge
.create_smart_scene(
SmartScene::builder("I AM SMORT", galaxy.data().group.clone()).schedule(
Schedule::new()
.on(&[Weekday::Saturday, Weekday::Sunday])
.at(TimeslotStart::time(&[0, 54, 0]), galaxy.rid())
.at(TimeslotStart::time(&[1, 27, 0]), mf.rid())
.at(TimeslotStart::time(&[1, 30, 0]), diabs.rid()),
),
)
.await;
Ok(())
}
#[allow(dead_code)]
async fn alert_lights(
bridge: &Bridge,
c1: impl Into<String>,
c2: impl Into<String>,
) -> Result<(), HueAPIError> {
let _ = bridge
.group("d14bacd9-a352-4f90-912b-6e6f272ff059")
.unwrap()
.send(&[GroupCommand::Signaling {
signal: SignalType::Alternating,
duration: 8000,
colors: Some(SignalColor::Two(
CIEColor::from_hex(c1).unwrap(),
CIEColor::from_hex(c2).unwrap(),
)),
}])
.await?;
Ok(())
}
#[allow(dead_code)]
async fn create_zone(
bridge: &Bridge,
name: impl Into<String>,
archetype: ZoneArchetype,
) -> Result<(), HueAPIError> {
let zone = bridge
.create_zone(Zone::builder(name.into(), archetype))
.await?;
dbg!(zone);
Ok(())
}
#[allow(dead_code)]
async fn change_room_type(
bridge: &Bridge,
name: impl Into<String>,
archetype: ZoneArchetype,
) -> Result<(), HueAPIError> {
let name = name.into();
for room in bridge.rooms() {
if &room.data.metadata.name == &name {
let _ = room
.send(&[ZoneCommand::Metadata {
name: None,
archetype: Some(archetype),
}])
.await?;
}
}
Ok(())
}
#[allow(dead_code)]
async fn rename_room(
bridge: &Bridge,
name: impl Into<String>,
other_name: impl Into<String>,
) -> Result<(), HueAPIError> {
let name = name.into();
if let Some(tr) = bridge
.rooms()
.iter()
.find(|room| &room.data.metadata.name == &name)
{
dbg!(&tr);
let res = tr
.send(&[ZoneCommand::Metadata {
name: Some(other_name.into()),
archetype: None,
}])
.await?;
dbg!(res);
} else {
eprintln!("No room '{}'", &name);
}
Ok(())
}
#[allow(dead_code)]
async fn recall_scene(bridge: &Bridge, name: impl Into<String>) -> Result<(), HueAPIError> {
let name = name.into();
if let Some(scene) = bridge.scenes().iter().find(|sc| sc.name() == &name) {
scene
.send(&[SceneCommand::Recall {
action: Some(SceneStatus::Active),
duration: Some(2000),
dimming: None,
}])
.await?;
} else {
eprintln!("No scene '{}'", &name);
}
Ok(())
}
#[allow(dead_code)]
async fn delete_scenes(bridge: &Bridge, name: impl Into<String>) -> Result<(), HueAPIError> {
let name = name.into();
let ids = bridge
.scenes()
.iter()
.filter_map(|sc| {
if sc.name() == &name {
Some(sc.id().to_owned())
} else {
None
}
})
.collect::<Vec<String>>();
dbg!(&ids);
for i in ids {
bridge.delete_scene(i).await?;
}
Ok(())
}
#[allow(dead_code)]
async fn create_scene(bridge: &Bridge, name: impl Into<String>) -> Result<(), HueAPIError> {
let room = bridge
.rooms()
.into_iter()
.find(|r| r.name() == ROOM_NAME)
.unwrap();
let my_scene = bridge
.create_scene(
SceneBuilder::new(name.into(), room.rid())
.actions(
room.lights()
.into_iter()
.map(|light| {
let action = if light.supports_color() {
LightAction {
color: Some(ColorFeatureBasic::xy(0.3, 0.4)),
..Default::default()
}
} else {
LightAction {
color_temperature: Some(SceneColorTempState {
mirek: Some(153),
}),
..Default::default()
}
};
SceneAction {
target: light.rid(),
action,
}
})
.collect::<Vec<_>>(),
)
.palette(ScenePalette {
color: vec![
ScenePaletteColor::xyb(0.1, 0.3, 70.0),
ScenePaletteColor::xyb(0.3, 0.5, 70.0),
],
effects: vec![SceneEffectState {
effect: Some(EffectType::Candle),
}],
..Default::default()
}),
)
.await?;
dbg!(my_scene);
Ok(())
}
#[allow(dead_code)]
async fn rename_scene(bridge: &Bridge) -> Result<Vec<ResourceIdentifier>, HueAPIError> {
let conc2 = bridge
.scene("ec1c5855-1c0f-462f-9703-479610af7204")
.unwrap();
conc2
.send(&[SceneCommand::Metadata {
name: Some("Conc2".into()),
appdata: None,
}])
.await
}
#[allow(dead_code)]
async fn identify_all_lights(bridge: &Bridge) -> Result<(), HueAPIError> {
for light in bridge.lights() {
light.identify().await?;
}
Ok(())
}
#[allow(dead_code)]
async fn randomize_all_lights(bridge: &Bridge) -> Result<(), HueAPIError> {
let mut rng = rand::thread_rng();
loop {
for light in bridge.lights() {
if light.data().color.is_some() {
let _ = light
.send(&[
LightCommand::Dim(rng.gen_range(70.0..=100.0)),
LightCommand::Color {
x: rng.gen_range(0.0..=0.5),
y: rng.gen_range(0.0..=0.4),
},
])
.await;
} else {
let _ = light
.send(&[
LightCommand::Dim(80.0),
LightCommand::ColorTemp(rng.gen_range(350..=500)),
])
.await;
}
}
std::thread::sleep(Duration::from_millis(2000));
}
}
#[allow(dead_code)]
async fn set_specific_light_colors(
bridge: &Bridge,
hex: impl Into<String>,
) -> Result<(), HueAPIError> {
let mut rng = rand::thread_rng();
let hex = hex.into();
for light in bridge.lights() {
if light.supports_color() {
let _ = light
.send(&[LightCommand::color_from_hex(&hex).unwrap()])
.await;
} else {
let _ = light
.send(&[
LightCommand::Dim(80.0),
LightCommand::ColorTemp(rng.gen_range(350..=500)),
])
.await;
}
}
Ok(())
}