Compare commits
8 Commits
187a8b3657
...
1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 5aaad54de3 | |||
| b3edfb05d9 | |||
| 45029f24cc | |||
| ec594c0e4d | |||
| fdfbdf093f | |||
| c789056ccf | |||
| f5f783698f | |||
| 823783f307 |
@@ -56,7 +56,7 @@ steps:
|
|||||||
- ls -lah target/release/central_backend
|
- ls -lah target/release/central_backend
|
||||||
|
|
||||||
- name: esp32_compile
|
- name: esp32_compile
|
||||||
image: espressif/idf:v5.2.2 # FIXME : upgrade to 5.3.1
|
image: espressif/idf:v5.3.1
|
||||||
commands:
|
commands:
|
||||||
- cd esp32_device
|
- cd esp32_device
|
||||||
- /opt/esp/entrypoint.sh idf.py build
|
- /opt/esp/entrypoint.sh idf.py build
|
||||||
@@ -67,4 +67,4 @@ volumes:
|
|||||||
- name: rust_registry
|
- name: rust_registry
|
||||||
temp: {}
|
temp: {}
|
||||||
- name: web_app
|
- name: web_app
|
||||||
temp: {}
|
temp: {}
|
||||||
|
|||||||
11
central_backend/Cargo.lock
generated
11
central_backend/Cargo.lock
generated
@@ -492,9 +492,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.89"
|
version = "1.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asn1"
|
name = "asn1"
|
||||||
@@ -670,6 +670,7 @@ dependencies = [
|
|||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"dotenvy",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"foreign-types-shared",
|
"foreign-types-shared",
|
||||||
"fs4",
|
"fs4",
|
||||||
@@ -997,6 +998,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenvy"
|
||||||
|
version = "0.15.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
dotenvy = "0.15.7"
|
||||||
clap = { version = "4.5.20", features = ["derive", "env"] }
|
clap = { version = "4.5.20", features = ["derive", "env"] }
|
||||||
anyhow = "1.0.89"
|
anyhow = "1.0.89"
|
||||||
thiserror = "1.0.64"
|
thiserror = "1.0.64"
|
||||||
|
|||||||
@@ -38,9 +38,13 @@ pub enum ConsumptionBackend {
|
|||||||
|
|
||||||
/// Fronius inverter consumption
|
/// Fronius inverter consumption
|
||||||
Fronius {
|
Fronius {
|
||||||
/// The origin of the domain where the webserver of the Fronius Symo can be reacher
|
/// The origin of the domain where the webserver of the Fronius Symo can be reached
|
||||||
|
#[clap(short, long, env)]
|
||||||
|
fronius_orig: String,
|
||||||
|
|
||||||
|
/// Use cURL instead of reqwest to perform request
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
origin: String,
|
curl: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +52,10 @@ pub enum ConsumptionBackend {
|
|||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
|
/// Read arguments from env file
|
||||||
|
#[clap(short, long, env)]
|
||||||
|
pub config: Option<String>,
|
||||||
|
|
||||||
/// Proxy IP, might end with a star "*"
|
/// Proxy IP, might end with a star "*"
|
||||||
#[clap(short, long, env)]
|
#[clap(short, long, env)]
|
||||||
pub proxy_ip: Option<String>,
|
pub proxy_ip: Option<String>,
|
||||||
@@ -114,6 +122,21 @@ lazy_static::lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
|
/// Parse environment variables from file, if requedst
|
||||||
|
pub fn parse_env_file() -> anyhow::Result<()> {
|
||||||
|
if let Some(c) = Self::parse().config {
|
||||||
|
log::info!("Load additional environment variables from {c}");
|
||||||
|
let conf_file = Path::new(&c);
|
||||||
|
|
||||||
|
if !conf_file.is_file() {
|
||||||
|
panic!("Specified configuration is not a file!");
|
||||||
|
}
|
||||||
|
dotenvy::from_path(conf_file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Get parsed command line arguments
|
/// Get parsed command line arguments
|
||||||
pub fn get() -> &'static AppConfig {
|
pub fn get() -> &'static AppConfig {
|
||||||
&ARGS
|
&ARGS
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ pub enum ConsumptionError {
|
|||||||
NonExistentFile,
|
NonExistentFile,
|
||||||
#[error("The file that should contain the consumption has an invalid content!")]
|
#[error("The file that should contain the consumption has an invalid content!")]
|
||||||
FileInvalidContent(#[source] ParseIntError),
|
FileInvalidContent(#[source] ParseIntError),
|
||||||
|
#[error("Failed to execute cURL request!")]
|
||||||
|
CurlReqFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EnergyConsumption = i32;
|
pub type EnergyConsumption = i32;
|
||||||
@@ -63,9 +65,21 @@ pub async fn get_curr_consumption() -> anyhow::Result<EnergyConsumption> {
|
|||||||
.map_err(ConsumptionError::FileInvalidContent)?)
|
.map_err(ConsumptionError::FileInvalidContent)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsumptionBackend::Fronius { origin } => {
|
ConsumptionBackend::Fronius { fronius_orig, curl } => {
|
||||||
let url = format!("{origin}/solar_api/v1/GetPowerFlowRealtimeData.fcgi");
|
let url = format!("{fronius_orig}/solar_api/v1/GetPowerFlowRealtimeData.fcgi");
|
||||||
let response = reqwest::get(url).await?.json::<FroniusResponse>().await?;
|
|
||||||
|
let response = match curl {
|
||||||
|
false => reqwest::get(url).await?.json::<FroniusResponse>().await?,
|
||||||
|
true => {
|
||||||
|
let res = std::process::Command::new("curl").arg(url).output()?;
|
||||||
|
|
||||||
|
if !res.status.success() {
|
||||||
|
return Err(ConsumptionError::CurlReqFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_json::from_slice::<FroniusResponse>(&res.stdout)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(response.body.data.site.grid_production as i32)
|
Ok(response.body.data.site.grid_production as i32)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use tokio_schedule::{every, Job};
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
|
// Load additional config from file, if requested
|
||||||
|
AppConfig::parse_env_file().expect("Failed to parse environment file!");
|
||||||
|
|
||||||
// Initialize OpenSSL
|
// Initialize OpenSSL
|
||||||
openssl_sys::init();
|
openssl_sys::init();
|
||||||
|
|
||||||
|
|||||||
@@ -45,4 +45,81 @@ The OTA update is then located in `build/main.bin`
|
|||||||
* DHCP configured on the network
|
* DHCP configured on the network
|
||||||
|
|
||||||
## Configure server
|
## Configure server
|
||||||
TODO
|
|
||||||
|
### Create a user dedicated to the central
|
||||||
|
```bash
|
||||||
|
sudo adduser --disabled-login central
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install binary
|
||||||
|
You can use `scp` to copy the binary to the target server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp central_backend/target/release/central_backend pierre@central:/home/pierre
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the executable must be installed system-wide:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mv central_backend /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create configuration file
|
||||||
|
Create a configuration file in `/home/central/config.yaml`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo touch /home/central/config.yaml
|
||||||
|
sudo chown central:central /home/central/config.yaml
|
||||||
|
sudo chmod 400 /home/central/config.yaml
|
||||||
|
sudo nano /home/central/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample configuration:
|
||||||
|
|
||||||
|
```conf
|
||||||
|
SECRET=RANDOM_VALUE
|
||||||
|
COOKIE_SECURE=true
|
||||||
|
LISTEN_ADDRESS=0.0.0.0:443
|
||||||
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_PASSWORD=FIXME
|
||||||
|
HOSTNAME=central.local
|
||||||
|
STORAGE=/home/central/storage
|
||||||
|
FRONIUS_ORIG=http://10.0.0.10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test configuration
|
||||||
|
Run the following command to check if the configuration is working:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -u central central_backend -c /home/central/config.yaml fronius -c
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create systemd unit file
|
||||||
|
Once you confirmed the configuration is working, you can configure a system service, in `/etc/systemd/system/central.service`:
|
||||||
|
|
||||||
|
```conf
|
||||||
|
[Unit]
|
||||||
|
Description=Central backend server
|
||||||
|
After=syslog.target
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
RestartSec=2s
|
||||||
|
Type=simple
|
||||||
|
User=central
|
||||||
|
Group=central
|
||||||
|
WorkingDirectory=/home/central
|
||||||
|
ExecStart=/usr/local/bin/central_backend -c /home/central/config.yaml fronius -c
|
||||||
|
Restart=always
|
||||||
|
Environment=USER=central
|
||||||
|
HOME=/home/central
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable & start service:
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable central
|
||||||
|
sudo systemctl start central
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user