Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config Updates and Fixes + More Documentation #20

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ tiny_http = "0.6"
url = "2.1.1"
tungstenite = "0.11.1"
percent-encoding = "2.1.0"
homeassistant = "0.3.0"
homeassistant = { git = "https://github.com/selfhostedshow/homeassistant-rs/", branch = "kylep-config-updates" }
32 changes: 32 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,37 @@ Halcyon is a Home Assistant app for Linux built in rust.
### Prerequisites

* Rust
* libssl-dev (or equivalent package for your distro)

To build Halcyon, run `cargo build`.

Only the setup command is working at the moment
To run it do the following
`cargo build` then run the halcyon executable
kylepotts marked this conversation as resolved.
Show resolved Hide resolved
`./target/debug/halcyon setup`
kylepotts marked this conversation as resolved.
Show resolved Hide resolved
or
`cargo run setup`


### Yaml Config

The setup process will create a config file for you if you don't have one.

Otherwise, the minimum config looks like
```yaml
---
ha:
host: "ip:port"
```


Once you run the set up command, a full config file will look like
```yaml
---
ha:
host: "ip:host"
long-lived-token: ...
device-id: ...
webhook-id: ...
```
The setup process should walk create the tokens and ids for you as you move through the process
49 changes: 43 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use std::error;
use std::io::Write;
use std::fs::OpenOptions;
use std::io::{self, Write};
use uuid::Uuid;

use tiny_http::{Response, Server};
Expand All @@ -15,6 +16,7 @@ use homeassistant::types::{GetAccessTokenResponse, RegisterDeviceResponse};
use homeassistant::HomeAssistantAPI;

use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use std::ops::Not;

const FRAGMENT: &AsciiSet = &CONTROLS.add(b':').add(b'/');

Expand All @@ -26,7 +28,7 @@ const LOCAL_SERVER_HOST: &str = "127.0.0.1:8000";

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct HaConfig {
pub host: String,
pub host: Option<String>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure i get why this is optional?

Copy link
Collaborator Author

@kylepotts kylepotts Apr 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I made adding the host a part of the setup process. So technically when we first go to parse the config file, the way we can tell if we need to add run the add the host logic is by checking if this is None or Some.

I agree its a bit weird because it for sure isn't optional for the app to run but not sure of another good mechanism to let us know the user hasn't setup a host yet.

#[serde(rename = "long-lived-token")]
pub long_lived_token: Option<String>,
#[serde(rename = "device-id")]
Expand Down Expand Up @@ -83,7 +85,7 @@ pub async fn wait_for_token(
utf8_percent_encode(&query_prams, FRAGMENT).collect();
println!(
"Open http://{}/auth/authorize?{} in your browser",
config.ha.host.as_str(),
config.ha.host.as_ref().unwrap().as_str(),
query_params_encoded
);
// blocks until the next request is received
Expand Down Expand Up @@ -143,7 +145,7 @@ fn get_long_lived_token_from_ws(
config: &YamlConfig,
access_token: String,
) -> Result<WsLongLivedAccessTokenResponse> {
let ws_url = format!("ws://{}/api/websocket", config.ha.host);
let ws_url = format!("ws://{}/api/websocket", config.ha.host.as_ref().unwrap());
let url = Url::parse(ws_url.as_str())?;
let (mut socket, _) = connect(url)?;

Expand Down Expand Up @@ -218,6 +220,27 @@ impl YamlConfig {
Ok(())
}

pub fn update_ha_host_if_needed(&mut self, file_name: &str) -> Result<()> {
match self.ha.host {
None => {
let mut input = String::new();
println!("Enter HomnAssisant host (host/ip:port)");
match io::stdin().read_line(&mut input) {
Ok(_) => {
self.ha.host = Some(input.trim().to_owned());
self.write_new_config(file_name)?;
Ok(())
}
Err(error) => {
println!("error reading in HA host: {}", error);
Err(Box::from("could not read from stdin"))
}
}
}
Some(_) => Ok(()),
}
}

pub fn update_device_id_if_needed(&mut self, file_name: &str) -> Result<()> {
match self.ha.device_id {
None => {
Expand Down Expand Up @@ -269,7 +292,21 @@ impl YamlConfig {
}

pub fn read_config_yml(file_name: &str) -> Result<YamlConfig> {
let f = std::fs::File::open(file_name)?;
let config: YamlConfig = serde_yaml::from_reader(f)?;
//let f = std::fs::File::open(file_name)?
let exists = std::path::Path::new(file_name).exists();
if exists.not() {
println!("config file does not exist creating one");
let mut f = std::fs::File::create(file_name)?;
write!(
f,
r#"
---
ha:
host: ~
"#
)?
};
let file = OpenOptions::new().read(true).write(true).open(file_name)?;
let config: YamlConfig = serde_yaml::from_reader(file)?;
Ok(config)
}
15 changes: 9 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use homeassistant::types::{
};
use homeassistant::HomeAssistantAPI;
use platform_info::{PlatformInfo, Uname};
use serde_json::Value;
use std::error;

const VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down Expand Up @@ -51,9 +52,11 @@ async fn command_setup(args: &clap::ArgMatches<'_>) -> Result<()> {
let platform_info = PlatformInfo::new()?;

let mut config = config::read_config_yml(config_file)?;
config.update_ha_host_if_needed(config_file)?;
config.update_device_id_if_needed(config_file)?;

let ha_api = HomeAssistantAPI::new(config.ha.host.clone(), OAUTH_CLIENT_ID.to_string());
let ha_api =
HomeAssistantAPI::new(config.ha.host.clone().unwrap(), OAUTH_CLIENT_ID.to_string());

if let Some(token) = config.ha.long_lived_token.clone() {
ha_api.write().unwrap().set_long_lived_token(token)
Expand All @@ -73,11 +76,11 @@ async fn command_setup(args: &clap::ArgMatches<'_>) -> Result<()> {
let states = rest_client.states().await?;
let name = platform_info.nodename().to_string();
let maybe_current_device_state = states.into_iter().find(|r| {
r.attributes
.get("friendly_name")
.unwrap_or(&String::from(""))
.as_str()
== name
let friendly_name = match r.attributes["friendly_name"] {
Value::String(ref s) => s,
_ => "",
};
friendly_name == name
});

match maybe_current_device_state {
Expand Down