Configuration

The main configuration of energy2mqtt is based on YAML, not only because Home Assistant uses it but because it’s a lot easier to read and write than JSON.

In this chapter you will see how to add a new configuration which can be used by the example protocol manager. For our system we will add a name and an enabled flag together with some other fields. You may look at all the code in src/config/ to see how you can add more flags. Always keep in mind that serde is used to serialize and deserialize the configuration. You may specify default values if the user did not specify the values themselves.

Details

Make it as easy for the user as possible

  • Do not ask for the same parameter multiple times with different names
  • Provide as many default as somehow possible
/*
    Add our struct in the middle of the rust code,
    ask for the right place if unsure.
*/
fn example_update_interval_default() -> u64 { 10 }

#[derive(Deserialize, Serialize, Clone)]
pub struct ExampleConfig {
    pub name: String,
    pub enabled: bool,
    #[serde(default = "example_update_interval_default")]
    pub update_interval: u64,
}

/*
    Add a default handler to provide an empty vector if
    user has not specified the example part in the configuration
*/
fn example_default() -> Vec<ExampleConfig> { return Vec::new(); }

/*
    Extend the Config struct
*/
#[derive(Deserialize, Serialize, Clone)]
pub struct Config {
    /* ... the stuff already there ... */
    #[serde(default = "example_default")]
    pub example: Vec<ExampleConfig>,
}

/* Extend ConfigBases struct */
pub enum ConfigBases {
    /* ... the stuff already there ... */
    Example(Vec<ExampleConfig>),
}

impl ConfigHolder {
    /*
        Extend update_config and get_copy with our protocol
    */
    pub fn update_config(&mut self, operation: ConfigOperation, new_data: ConfigBases) {
        let base: &str;
        match new_data {
            /* ... the stuff already there ... */
            Example(example_configs) => {
                self.config.example = example_configs;
                base = "example";
            },
        }

        self.dirty = true;
        let _ = self.callbacks.sender.send(ConfigChange { 
                                                    operation: operation,
                                                    base: base.to_string()
                                           });
    }

    pub fn get_copy(&self, base: &str) -> Result<ConfigBases, Box<dyn Error>> {
        /* Lock against modifications during copy */
        let _lock = self.lock.read().unwrap();

        match base {
            /* ... the stuff already there beside _ ... */
            "example" => Ok( Example(self.config.example.clone()) ),
            _ => { Err("Type not known")? }
        }
    }
}

The configuration part of that example component will look like this in e2m.yaml:

example:
  - name: Your first entry
    enabled: True
    # Update defaults here
  - name: A disabled entry
    enabled: False
    update_interval: 60

Refer to serde_yaml configuration about more magic for parsing this configuration.