Skip to main content

Overview

Plants represent solar power installations in the Growatt ecosystem. This guide covers how to retrieve plant information, access plant details, and work with plant data structures.

Data Structures

Plant

Represents basic information about a plant:
#[derive(Debug, Serialize, Deserialize)]
pub struct Plant {
    #[serde(rename = "id")]
    pub plant_id: String,
    #[serde(rename = "name", alias = "plantName")]
    pub plant_name: String,
    #[serde(rename = "plantAddress", default)]
    pub plant_address: Option<String>,
    #[serde(rename = "plantPower", default)]
    pub plant_watts: Option<f64>,
    #[serde(rename = "isShare", default)]
    pub is_share: Option<bool>,
}

PlantList

A collection of plants:
#[derive(Debug, Serialize, Deserialize)]
pub struct PlantList(pub Vec<Plant>);

PlantData

Detailed information about a specific plant:
#[derive(Debug, Serialize, Deserialize)]
pub struct PlantData {
    #[serde(rename = "plantName")]
    pub plant_name: Option<String>,
    #[serde(rename = "plantId")]
    pub plant_id: Option<String>,
    #[serde(rename = "capacity")]
    pub capacity: Option<f64>,
    #[serde(rename = "todayEnergy")]
    pub today_energy: Option<f64>,
    #[serde(rename = "totalEnergy")]
    pub total_energy: Option<f64>,
    #[serde(rename = "currentPower")]
    pub current_power: Option<f64>,
}

Getting All Plants

Retrieve a list of all plants associated with your account:
use growatt::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    client.login("your_username", "your_password").await?;
    
    // Get all plants
    let plants = client.get_plants().await?;
    
    println!("Found {} plants:", plants.0.len());
    for plant in plants.0 {
        println!("Plant ID: {}", plant.plant_id);
        println!("Plant Name: {}", plant.plant_name);
        
        if let Some(address) = plant.plant_address {
            println!("Address: {}", address);
        }
        
        if let Some(power) = plant.plant_watts {
            println!("Power (W): {}", power);
        }
        
        println!("-------------------");
    }
    
    client.logout().await?;
    Ok(())
}
The get_plants() method automatically handles authentication and session management. If your session has expired, it will re-authenticate using your stored credentials.

Getting Detailed Plant Information

Retrieve detailed information about a specific plant:
use growatt::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    client.login("your_username", "your_password").await?;
    
    let plant_id = "your_plant_id";
    
    // Get detailed plant data
    match client.get_plant(plant_id).await {
        Ok(plant_data) => {
            println!("Plant Details:");
            
            if let Some(name) = plant_data.plant_name {
                println!("Name: {}", name);
            }
            
            if let Some(capacity) = plant_data.capacity {
                println!("Capacity: {} kW", capacity);
            }
            
            if let Some(today_energy) = plant_data.today_energy {
                println!("Today's Energy: {} kWh", today_energy);
            }
            
            if let Some(total_energy) = plant_data.total_energy {
                println!("Total Energy: {} kWh", total_energy);
            }
            
            if let Some(current_power) = plant_data.current_power {
                println!("Current Power: {} W", current_power);
            }
        },
        Err(e) => eprintln!("Error getting plant data: {}", e),
    }
    
    client.logout().await?;
    Ok(())
}

Iterating Over Plants

Here’s a complete example showing how to iterate over all plants and retrieve detailed information:
use growatt::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    
    // Login
    match client.login("your_username", "your_password").await {
        Ok(true) => println!("Login successful!"),
        Ok(false) => {
            println!("Login failed! Check your credentials.");
            return Ok(());
        },
        Err(e) => {
            eprintln!("Error during login: {}", e);
            return Ok(());
        }
    }
    
    // Get the list of plants
    match client.get_plants().await {
        Ok(plants) => {
            println!("Found {} plants:", plants.0.len());
            
            // Display information about each plant
            for plant in plants.0 {
                println!("Plant ID: {}", plant.plant_id);
                println!("Plant Name: {}", plant.plant_name);
                
                if let Some(address) = plant.plant_address {
                    println!("Address: {}", address);
                }
                
                if let Some(power) = plant.plant_watts {
                    println!("Power (W): {}", power);
                }
                
                println!("-------------------");
                
                // Get more detailed data for each plant
                match client.get_plant(&plant.plant_id).await {
                    Ok(plant_data) => {
                        println!("Additional plant data:");
                        if let Some(capacity) = plant_data.capacity {
                            println!("Capacity: {}", capacity);
                        }
                        if let Some(today_energy) = plant_data.today_energy {
                            println!("Today's Energy: {}", today_energy);
                        }
                        if let Some(total_energy) = plant_data.total_energy {
                            println!("Total Energy: {}", total_energy);
                        }
                        println!("-------------------");
                    },
                    Err(e) => println!("Error getting detailed plant data: {}", e),
                }
            }
        },
        Err(e) => println!("Error getting plants: {}", e),
    }
    
    // Logout when done
    if let Err(e) = client.logout().await {
        println!("Error during logout: {}", e);
    } else {
        println!("Successfully logged out");
    }
    
    Ok(())
}

Handling Optional Fields

Many plant fields are optional. Here’s how to handle them safely:
use growatt::Growatt;

async fn display_plant_info(client: &mut Growatt, plant_id: &str) -> Result<(), Box<dyn std::error::Error>> {
    let plant_data = client.get_plant(plant_id).await?;
    
    // Using if let for individual fields
    if let Some(name) = plant_data.plant_name {
        println!("Plant: {}", name);
    }
    
    // Using unwrap_or for default values
    let capacity = plant_data.capacity.unwrap_or(0.0);
    println!("Capacity: {} kW", capacity);
    
    // Using match for multiple cases
    match plant_data.today_energy {
        Some(energy) if energy > 0.0 => println!("Generating {} kWh today", energy),
        Some(_) => println!("No energy generated today"),
        None => println!("Energy data not available"),
    }
    
    Ok(())
}

Getting Weather Information

Retrieve weather data for a plant:
use growatt::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    client.login("your_username", "your_password").await?;
    
    let plant_id = "your_plant_id";
    
    // Get weather information
    let weather = client.get_weather(plant_id).await?;
    println!("Weather data: {}", weather);
    
    client.logout().await?;
    Ok(())
}

Best Practices

Cache plant IDs: If you need to access the same plant multiple times, cache the plant ID to avoid repeated get_plants() calls:
let plants = client.get_plants().await?;
let plant_ids: Vec<String> = plants.0.iter()
    .map(|p| p.plant_id.clone())
    .collect();

// Use cached IDs for subsequent operations
for plant_id in plant_ids {
    let data = client.get_plant(&plant_id).await?;
    // Process data
}
Handle empty plant lists: Always check if the plant list is empty before processing:
let plants = client.get_plants().await?;

if plants.0.is_empty() {
    println!("No plants found for this account");
    return Ok(());
}

// Process plants
for plant in plants.0 {
    // ...
}
Use structured error handling: When iterating over multiple plants, collect errors instead of failing fast:
let plants = client.get_plants().await?;
let mut errors = Vec::new();

for plant in plants.0 {
    match client.get_plant(&plant.plant_id).await {
        Ok(data) => process_plant_data(data),
        Err(e) => errors.push((plant.plant_id.clone(), e)),
    }
}

if !errors.is_empty() {
    eprintln!("Failed to fetch {} plants", errors.len());
    for (id, err) in errors {
        eprintln!("  - {}: {}", id, err);
    }
}

Complete Working Example

Here’s a complete example from the source code that demonstrates all the concepts:
use growatt::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize the Growatt client
    let mut client = Growatt::new();
    
    // Replace these with your actual credentials
    let username = "your_username";
    let password = "your_password";
    
    // Login to the Growatt API
    match client.login(username, password).await {
        Ok(true) => {
            println!("Login successful!");
            
            // Get the list of plants
            match client.get_plants().await {
                Ok(plants) => {
                    println!("Found {} plants:", plants.0.len());
                    
                    // Display information about each plant
                    for plant in plants.0 {
                        println!("Plant ID: {}", plant.plant_id);
                        println!("Plant Name: {}", plant.plant_name);
                        
                        if let Some(address) = plant.plant_address {
                            println!("Address: {}", address);
                        }
                        
                        if let Some(power) = plant.plant_watts {
                            println!("Power (W): {}", power);
                        }
                        
                        println!("-------------------");
                        
                        // Optional: Get more detailed data for each plant
                        match client.get_plant(&plant.plant_id).await {
                            Ok(plant_data) => {
                                println!("Additional plant data:");
                                if let Some(capacity) = plant_data.capacity {
                                    println!("Capacity: {}", capacity);
                                }
                                if let Some(today_energy) = plant_data.today_energy {
                                    println!("Today's Energy: {}", today_energy);
                                }
                                if let Some(total_energy) = plant_data.total_energy {
                                    println!("Total Energy: {}", total_energy);
                                }
                                println!("-------------------");
                            },
                            Err(e) => println!("Error getting detailed plant data: {}", e),
                        }
                    }
                },
                Err(e) => println!("Error getting plants: {}", e),
            }
            
            // Logout when done
            if let Err(e) = client.logout().await {
                println!("Error during logout: {}", e);
            } else {
                println!("Successfully logged out");
            }
        },
        Ok(false) => println!("Login failed! Check your credentials."),
        Err(e) => println!("Error during login: {}", e),
    }
    
    Ok(())
}

Build docs developers (and LLMs) love