Compare commits
No commits in common. "main" and "v1.0.0" have entirely different histories.
10 changed files with 694 additions and 1358 deletions
1887
Cargo.lock
generated
1887
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
31
Cargo.toml
31
Cargo.toml
|
@ -1,34 +1,35 @@
|
||||||
[package]
|
[package]
|
||||||
name = "hassliebe"
|
name = "hassliebe"
|
||||||
version = "1.1.1"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "BlueOak-1.0.0"
|
license = "BlueOak-1.0.0"
|
||||||
authors = ["Moritz Ruth <dev@moritzruth.de>"]
|
authors = ["Moritz Ruth <dev@moritzruth.de>"]
|
||||||
|
homepage = "https://git.moritzruth.de/moritzruth/Hassliebe"
|
||||||
repository = "https://git.moritzruth.de/moritzruth/Hassliebe"
|
repository = "https://git.moritzruth.de/moritzruth/Hassliebe"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dry_run = [] # will prevent some actions like shutting down
|
dry_run = [] # will prevent some actions like shutting down
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.88"
|
anyhow = "1.0.69"
|
||||||
base64 = "0.22.1"
|
base64 = "0.21.0"
|
||||||
env_logger = "0.11.5"
|
battery = "0.7.8"
|
||||||
|
env_logger = "0.10.0"
|
||||||
exitcode = "1.1.2"
|
exitcode = "1.1.2"
|
||||||
image = "0.25.2"
|
image = "0.24.5"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
notify-rust = { version = "4.11.3", features = ["images"] }
|
notify-rust = { version = "4.8.0", features = ["images"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
regex = "1.7.1"
|
regex = "1.7.1"
|
||||||
rumqttc = "0.24.0"
|
rumqttc = "0.20.0"
|
||||||
serde = { version = "1.0.152", features = ["derive"] }
|
serde = { version = "1.0.152", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.93"
|
||||||
sysinfo = { version = "0.31.4", default-features = false, features = ["system"] }
|
sysinfo = { version = "0.28.2", default-features = false }
|
||||||
tokio = { version = "1.25.0", features = ["full"] }
|
tokio = { version = "1.25.0", features = ["full"] }
|
||||||
toml = "0.8.19"
|
toml = "0.7.2"
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
validator = { version = "0.18.1", features = ["derive"] }
|
validator = { version = "0.16.0", features = ["derive"] }
|
||||||
|
void = "1.0.2"
|
||||||
[profile.release]
|
zbus = { version = "3.10.0", default-features = false, features = ["tokio"] }
|
||||||
strip = true # Automatically strip symbols from the binary.
|
|
||||||
|
|
36
README.md
36
README.md
|
@ -2,31 +2,27 @@
|
||||||
|
|
||||||
> Integrates any Linux machine into your Home Assistant ecosystem.
|
> Integrates any Linux machine into your Home Assistant ecosystem.
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- [x] Command buttons
|
- [x] Command buttons
|
||||||
- [x] Notifications
|
- [x] Notifications
|
||||||
- [x] Actions
|
- [x] Actions
|
||||||
- [x] System information reporting (CPU usage, RAM usage, battery status)
|
- [x] System information reporting (CPU usage, battery status, …)
|
||||||
|
- [ ] Media control (MPRIS)
|
||||||
The following sound like interesting ideas but as I don’t have a use case for them at the moment, I haven’t implemented them yet.
|
|
||||||
Feel free to [open an issue and describe your use-case](https://git.moritzruth.de/moritzruth/Hassliebe/issues/new).
|
|
||||||
|
|
||||||
- [ ] PipeWire control
|
- [ ] PipeWire control
|
||||||
- [ ] Exposing file contents as sensors
|
- [ ] File watcher
|
||||||
|
|
||||||
|
Ideas:
|
||||||
|
|
||||||
|
- Camera video stream
|
||||||
|
- Idle time (→ libseat)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Hassliebe is a [single executable](https://git.moritzruth.de/moritzruth/Hassliebe/releases). Copy it somewhere and execute it.
|
|
||||||
|
|
||||||
For systemd users, a unit file is provided.
|
|
||||||
|
|
||||||
### As a systemd _system_ service
|
### As a systemd _system_ service
|
||||||
|
|
||||||
- Download [the latest binary](https://git.moritzruth.de/moritzruth/Hassliebe/releases) and put it into `/usr/bin`.
|
- Download [the latest binary](https://git.moritzruth.de/moritzruth/Hassliebe/releases) and put it into `/usr/bin`.
|
||||||
- Download [the unit file](contrib/systemd/system/hassliebe.service) and put it into `/etc/systemd/system`.
|
- Download [the unit file](distrib/systemd/system/hassliebe.service) and put it into `/etc/systemd/system`.
|
||||||
- Create the configuration file (see below) at `/etc/hassliebe/config.toml`.
|
- Create the configuration file (see below) at `/etc/hassliebe/config.toml`.
|
||||||
- Enable and start the unit:
|
- Enable and start the unit:
|
||||||
|
|
||||||
|
@ -34,11 +30,10 @@ For systemd users, a unit file is provided.
|
||||||
systemctl enable hassliebe && systemctl start hassliebe
|
systemctl enable hassliebe && systemctl start hassliebe
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### As a systemd _user_ service
|
### As a systemd _user_ service
|
||||||
|
|
||||||
- Download [the latest binary](https://git.moritzruth.de/moritzruth/Hassliebe/releases) and put it into `~/.local/bin`.
|
- Download [the latest binary](https://git.moritzruth.de/moritzruth/Hassliebe/releases) and put it into `~/.local/bin`.
|
||||||
- Download [the unit file](contrib/systemd/user/hassliebe.service) and put it into `~/.local/share/systemd/user`.
|
- Download [the unit file](distrib/systemd/user/hassliebe.service) and put it into `~/.local/share/systemd/user`.
|
||||||
- Create the configuration file (see below) at `$XDG_CONFIG_HOME/hassliebe/config.toml`.
|
- Create the configuration file (see below) at `$XDG_CONFIG_HOME/hassliebe/config.toml`.
|
||||||
- Enable and start the unit:
|
- Enable and start the unit:
|
||||||
|
|
||||||
|
@ -46,13 +41,11 @@ systemctl enable hassliebe && systemctl start hassliebe
|
||||||
systemctl --user enable hassliebe && systemctl --user start hassliebe
|
systemctl --user enable hassliebe && systemctl --user start hassliebe
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Depending on whether Hassliebe is run as root or as a regular user, the configuration is read from
|
Depending on whether Hassliebe is run as root or as a regular user, the configuration is read from
|
||||||
`/etc/hassliebe/config.toml` or `$XDG_CONFIG_HOME/hassliebe/config.toml`.
|
`/etc/hassliebe/config.toml` or `$XDG_CONFIG_HOME/hassliebe/config.toml`.
|
||||||
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
@ -63,9 +56,6 @@ display_name = "My PC"
|
||||||
host = "127.0.0.1" # You probably need to change this
|
host = "127.0.0.1" # You probably need to change this
|
||||||
port = 1883
|
port = 1883
|
||||||
|
|
||||||
# You can remove the following line if your MQTT broker allows unauthenticated access
|
|
||||||
credentials = { user = "user", password = "password" }
|
|
||||||
|
|
||||||
[modules.buttons]
|
[modules.buttons]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
||||||
|
@ -84,7 +74,6 @@ battery = 60
|
||||||
enabled = true
|
enabled = true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Machine identification
|
### Machine identification
|
||||||
|
|
||||||
Hassliebe needs a way to uniquely identify a machine.
|
Hassliebe needs a way to uniquely identify a machine.
|
||||||
|
@ -94,7 +83,6 @@ If `/etc/machine-id` exists (as is the case with systemd-based systems), it will
|
||||||
Otherwise, a random ID will be generated on the first run and stored in `[data]/machine_id`.
|
Otherwise, a random ID will be generated on the first run and stored in `[data]/machine_id`.
|
||||||
The latter always takes precedence.
|
The latter always takes precedence.
|
||||||
|
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
### Buttons
|
### Buttons
|
||||||
|
@ -111,7 +99,6 @@ run_in_shell = true # defaults to false
|
||||||
|
|
||||||
When `run_in_shell` is set to `true`, the command will be run with `/bin/sh`.
|
When `run_in_shell` is set to `true`, the command will be run with `/bin/sh`.
|
||||||
|
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
|
|
||||||
Hassliebe currently supports reporting the following system stats:
|
Hassliebe currently supports reporting the following system stats:
|
||||||
|
@ -130,7 +117,6 @@ ram_usage = 0 # disabled
|
||||||
battery = 60 # updates every 60 seconds
|
battery = 60 # updates every 60 seconds
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Notifications
|
### Notifications
|
||||||
|
|
||||||
**Not available when running as a system service.**
|
**Not available when running as a system service.**
|
||||||
|
@ -166,7 +152,6 @@ Complex messages have these properties:
|
||||||
- `encoded_image` (optional) — Padded Base64-encoded image attached to the notification.
|
- `encoded_image` (optional) — Padded Base64-encoded image attached to the notification.
|
||||||
- `actions` (optional) — Object with the keys being IDs and the values being labels.
|
- `actions` (optional) — Object with the keys being IDs and the values being labels.
|
||||||
|
|
||||||
|
|
||||||
#### Actions
|
#### Actions
|
||||||
|
|
||||||
When a notification action is invoked, the ID of the action is sent to the MQTT topic with the following name:
|
When a notification action is invoked, the ID of the action is sent to the MQTT topic with the following name:
|
||||||
|
@ -174,7 +159,6 @@ When a notification action is invoked, the ID of the action is sent to the MQTT
|
||||||
|
|
||||||
When a notification is dismissed, `closed` is sent into the topic.
|
When a notification is dismissed, `closed` is sent into the topic.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is available under the permissive [Blue Oak Model License 1.0.0](https://blueoakcouncil.org/license/1.0.0).
|
Hassliebe is licensed under the [Blue Oak Model License 1.0.0](./LICENSE.md).
|
|
@ -15,7 +15,7 @@ pub struct Mqtt {
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[validate(nested)]
|
#[validate]
|
||||||
pub credentials: Option<MqttCredentials>,
|
pub credentials: Option<MqttCredentials>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,16 +33,16 @@ pub struct Internal {
|
||||||
|
|
||||||
#[derive(Deserialize, Validate)]
|
#[derive(Deserialize, Validate)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[validate(custom(function = "crate::util::validate_hass_id"))]
|
#[validate(custom = "crate::util::validate_hass_id")]
|
||||||
pub friendly_id: String,
|
pub friendly_id: String,
|
||||||
|
|
||||||
#[validate(length(min = 1))]
|
#[validate(length(min = 1))]
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
|
|
||||||
#[validate(nested)]
|
#[validate]
|
||||||
pub mqtt: Mqtt,
|
pub mqtt: Mqtt,
|
||||||
|
|
||||||
#[validate(nested)]
|
#[validate]
|
||||||
pub modules: modules::Config,
|
pub modules: modules::Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ pub fn load(config_file_path: &Path) -> Result<Option<Config>> {
|
||||||
file.read_to_string(&mut string_content).context("while reading the configuration file")?;
|
file.read_to_string(&mut string_content).context("while reading the configuration file")?;
|
||||||
|
|
||||||
let parsed = toml::from_str::<Config>(string_content.as_str()).context("while parsing the configuration file")?;
|
let parsed = toml::from_str::<Config>(string_content.as_str()).context("while parsing the configuration file")?;
|
||||||
Validate::validate(&parsed).context("while validating the configuration file")?;
|
parsed.validate().context("while validating the configuration file")?;
|
||||||
|
|
||||||
Ok(Some(parsed))
|
Ok(Some(parsed))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ const BUTTON_TRIGGER_TEXT: &str = "press";
|
||||||
|
|
||||||
#[derive(Deserialize, Validate, Clone)]
|
#[derive(Deserialize, Validate, Clone)]
|
||||||
pub struct ButtonConfig {
|
pub struct ButtonConfig {
|
||||||
#[validate(custom(function = "crate::util::validate_hass_id"))]
|
#[validate(custom = "crate::util::validate_hass_id")]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
||||||
#[validate(length(min = 1))]
|
#[validate(length(min = 1))]
|
||||||
|
|
|
@ -3,10 +3,10 @@ use std::time::Duration;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rumqttc::QoS;
|
use rumqttc::QoS;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use sysinfo::{CpuExt, System, SystemExt};
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
use tokio::time::MissedTickBehavior;
|
use tokio::time::MissedTickBehavior;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
use sysinfo::System;
|
|
||||||
|
|
||||||
use crate::modules::InitializationContext;
|
use crate::modules::InitializationContext;
|
||||||
|
|
||||||
|
@ -43,16 +43,14 @@ pub async fn init(context: &mut InitializationContext) -> Result<()> {
|
||||||
sub_id: "ram_usage",
|
sub_id: "ram_usage",
|
||||||
display_name: "RAM Usage",
|
display_name: "RAM Usage",
|
||||||
icon: "mdi:memory",
|
icon: "mdi:memory",
|
||||||
suggested_precision: Some(0),
|
suggested_precision: 0,
|
||||||
unit: Some("%"),
|
unit: Some("%"),
|
||||||
force_update: true,
|
|
||||||
device_class: None
|
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
config.ram_usage,
|
config.ram_usage,
|
||||||
move || {
|
move || {
|
||||||
sys.refresh_memory();
|
sys.refresh_memory();
|
||||||
Ok(format!("{:.2}", (sys.used_memory() as f64) / (sys.total_memory() as f64) * 100.0))
|
Ok(format!("{:.2}", (sys.available_memory() as f64) / (sys.total_memory() as f64) * 100.0))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -65,16 +63,14 @@ pub async fn init(context: &mut InitializationContext) -> Result<()> {
|
||||||
sub_id: "cpu_usage",
|
sub_id: "cpu_usage",
|
||||||
display_name: "CPU Usage",
|
display_name: "CPU Usage",
|
||||||
icon: "mdi:memory",
|
icon: "mdi:memory",
|
||||||
suggested_precision: Some(0),
|
suggested_precision: 0,
|
||||||
unit: Some("%"),
|
unit: Some("%"),
|
||||||
force_update: true,
|
|
||||||
device_class: None
|
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
config.cpu_usage,
|
config.cpu_usage,
|
||||||
move || {
|
move || {
|
||||||
sys.refresh_cpu_usage();
|
sys.refresh_cpu();
|
||||||
Ok(format!("{:.2}", sys.global_cpu_usage()))
|
Ok(format!("{:.2}", sys.global_cpu_info().cpu_usage()))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -90,7 +86,8 @@ pub async fn init(context: &mut InitializationContext) -> Result<()> {
|
||||||
})
|
})
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
})
|
})
|
||||||
.await?;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if let Some(dir) = battery_dirs.first() {
|
if let Some(dir) = battery_dirs.first() {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
@ -109,10 +106,8 @@ pub async fn init(context: &mut InitializationContext) -> Result<()> {
|
||||||
sub_id: "battery_level",
|
sub_id: "battery_level",
|
||||||
display_name: "Battery Level",
|
display_name: "Battery Level",
|
||||||
icon: "mdi:battery",
|
icon: "mdi:battery",
|
||||||
suggested_precision: Some(0),
|
suggested_precision: 0,
|
||||||
unit: Some("%"),
|
unit: Some("%"),
|
||||||
force_update: true,
|
|
||||||
device_class: Some("battery")
|
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
config.battery,
|
config.battery,
|
||||||
|
@ -125,10 +120,8 @@ pub async fn init(context: &mut InitializationContext) -> Result<()> {
|
||||||
sub_id: "battery_state",
|
sub_id: "battery_state",
|
||||||
display_name: "Battery State",
|
display_name: "Battery State",
|
||||||
icon: "mdi:battery",
|
icon: "mdi:battery",
|
||||||
suggested_precision: None,
|
suggested_precision: 0,
|
||||||
unit: None,
|
unit: None,
|
||||||
force_update: false,
|
|
||||||
device_class: Some("enum")
|
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
config.battery,
|
config.battery,
|
||||||
|
@ -147,10 +140,8 @@ struct InfoEntityOptions<'a> {
|
||||||
sub_id: &'a str,
|
sub_id: &'a str,
|
||||||
display_name: &'a str,
|
display_name: &'a str,
|
||||||
icon: &'a str,
|
icon: &'a str,
|
||||||
suggested_precision: Option<u64>,
|
suggested_precision: u64,
|
||||||
unit: Option<&'a str>,
|
unit: Option<&'a str>,
|
||||||
force_update: bool,
|
|
||||||
device_class: Option<&'a str>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init_info_value(
|
async fn init_info_value(
|
||||||
|
@ -176,10 +167,10 @@ async fn init_info_value(
|
||||||
let mut object = json::object! {
|
let mut object = json::object! {
|
||||||
"availability_topic": context.full.mqtt.availability_topic.clone(),
|
"availability_topic": context.full.mqtt.availability_topic.clone(),
|
||||||
"device": context.full.mqtt.discovery_device_object.clone(),
|
"device": context.full.mqtt.discovery_device_object.clone(),
|
||||||
"device_class": options.device_class,
|
"force_update": true,
|
||||||
"force_update": options.force_update,
|
|
||||||
"icon": options.icon,
|
"icon": options.icon,
|
||||||
"name": options.display_name,
|
"name": options.display_name,
|
||||||
|
"suggested_display_precision": options.suggested_precision,
|
||||||
"state_class": "measurement",
|
"state_class": "measurement",
|
||||||
"state_topic": state_topic.as_str(),
|
"state_topic": state_topic.as_str(),
|
||||||
"object_id": entity_id.as_str(),
|
"object_id": entity_id.as_str(),
|
||||||
|
@ -190,10 +181,6 @@ async fn init_info_value(
|
||||||
object.insert("unit_of_measurement", unit).unwrap();
|
object.insert("unit_of_measurement", unit).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(suggested_precision) = options.suggested_precision {
|
|
||||||
object.insert("suggested_display_precision", suggested_precision).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
object
|
object
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,13 +13,13 @@ mod notifications;
|
||||||
|
|
||||||
#[derive(Deserialize, Validate, Default)]
|
#[derive(Deserialize, Validate, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[validate(nested)]
|
#[validate]
|
||||||
pub buttons: Option<buttons::Config>,
|
pub buttons: Option<buttons::Config>,
|
||||||
|
|
||||||
#[validate(nested)]
|
#[validate]
|
||||||
pub info: Option<info::Config>,
|
pub info: Option<info::Config>,
|
||||||
|
|
||||||
#[validate(nested)]
|
#[validate]
|
||||||
pub notifications: Option<notifications::Config>,
|
pub notifications: Option<notifications::Config>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
src/mqtt.rs
41
src/mqtt.rs
|
@ -28,9 +28,9 @@ pub async fn create_client(config: &config::Config, machine_id: &str, availabili
|
||||||
usize::MAX,
|
usize::MAX,
|
||||||
);
|
);
|
||||||
|
|
||||||
config.mqtt.credentials.as_ref().map(|c| options.set_credentials(c.user.clone(), c.password.clone()));
|
let (mqtt_client, event_loop) = MqttClient::new(options, 100);
|
||||||
|
mqtt_client.publish(availability_topic, QoS::AtLeastOnce, true, "online").await?;
|
||||||
|
|
||||||
let (mqtt_client, event_loop) = MqttClient::new(options, 30);
|
|
||||||
Ok((mqtt_client, event_loop))
|
Ok((mqtt_client, event_loop))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +111,20 @@ pub async fn start_communication(context: &InitializationContext, mut event_loop
|
||||||
context.full.config.mqtt.port
|
context.full.config.mqtt.port
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if !context.message_handler_by_mqtt_topic.is_empty() {
|
||||||
|
context
|
||||||
|
.full
|
||||||
|
.mqtt
|
||||||
|
.client
|
||||||
|
.subscribe_many(
|
||||||
|
context
|
||||||
|
.message_handler_by_mqtt_topic
|
||||||
|
.keys()
|
||||||
|
.map(|k| SubscribeFilter::new(k.to_owned(), QoS::AtLeastOnce)),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut connection_state = ConnectionState::NotConnected;
|
let mut connection_state = ConnectionState::NotConnected;
|
||||||
let mut owned_topics_service = Some(owned_topics_service);
|
let mut owned_topics_service = Some(owned_topics_service);
|
||||||
|
|
||||||
|
@ -163,30 +177,9 @@ pub async fn start_communication(context: &InitializationContext, mut event_loop
|
||||||
if connection_state == ConnectionState::NotConnected {
|
if connection_state == ConnectionState::NotConnected {
|
||||||
log::info!("Connection established")
|
log::info!("Connection established")
|
||||||
} else {
|
} else {
|
||||||
log::info!("Connection restored");
|
log::info!("Connection restored")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !context.message_handler_by_mqtt_topic.is_empty() {
|
|
||||||
context
|
|
||||||
.full
|
|
||||||
.mqtt
|
|
||||||
.client
|
|
||||||
.subscribe_many(
|
|
||||||
context
|
|
||||||
.message_handler_by_mqtt_topic
|
|
||||||
.keys()
|
|
||||||
.map(|k| SubscribeFilter::new(k.to_owned(), QoS::AtLeastOnce)),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
context
|
|
||||||
.full
|
|
||||||
.mqtt
|
|
||||||
.client
|
|
||||||
.publish(&context.full.mqtt.availability_topic, QoS::AtLeastOnce, true, "online")
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(service) = owned_topics_service.take() {
|
if let Some(service) = owned_topics_service.take() {
|
||||||
service.clear_old_and_save_new(&context.full.mqtt.client, &context.owned_mqtt_topics).await?;
|
service.clear_old_and_save_new(&context.full.mqtt.client, &context.owned_mqtt_topics).await?;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue