Skip to main content

Method Signature

pub async fn get_weather(&mut self, plant_id: &str) -> Result<serde_json::Value>

Description

Retrieves weather and environmental data for the specified plant location. This includes information from environmental monitoring devices such as temperature sensors, irradiance meters, and other weather monitoring equipment installed at the plant site. This method automatically handles authentication by checking the current session and re-authenticating if necessary.

Parameters

plant_id
&str
required
The unique identifier of the plant. This ID can be obtained from the get_plants() method.

Return Type

Returns Result<serde_json::Value> where:
  • Success: serde_json::Value - JSON object containing weather and environmental data
  • Error: GrowattError - See error cases below

Response Structure

The response is returned as a raw serde_json::Value because the structure may vary depending on the environmental devices installed at the plant. Common fields include:
obj
Object
Container object for the environment list
datas
Array
Array of environmental monitoring devices and their readings
deviceSn
String
Serial number of the environmental monitoring device
deviceType
String
Type of environmental device (e.g., “ENV”, “WEATHER”)
temperature
Number
Current temperature in degrees Celsius (if available)
irradiance
Number
Solar irradiance in W/m² (if available)
windSpeed
Number
Wind speed in m/s (if available)
humidity
Number
Relative humidity percentage (if available)

Code Examples

Basic Usage

use growatt_api::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    
    // Login first
    client.login("username", "password").await?;
    
    // Get weather data
    let weather = client.get_weather("1234567").await?;
    
    // Print the raw JSON response
    println!("Weather data: {}", serde_json::to_string_pretty(&weather)?);
    
    Ok(())
}

Extract Specific Fields

use growatt_api::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    client.login("username", "password").await?;
    
    let weather = client.get_weather("1234567").await?;
    
    // Extract environmental data
    if let Some(obj) = weather.get("obj") {
        if let Some(datas) = obj.get("datas").and_then(|d| d.as_array()) {
            for device in datas {
                if let Some(sn) = device.get("deviceSn").and_then(|s| s.as_str()) {
                    println!("\nDevice: {}", sn);
                }
                
                if let Some(temp) = device.get("temperature").and_then(|t| t.as_f64()) {
                    println!("  Temperature: {:.1}°C", temp);
                }
                
                if let Some(irr) = device.get("irradiance").and_then(|i| i.as_f64()) {
                    println!("  Irradiance: {:.1} W/m²", irr);
                }
                
                if let Some(humid) = device.get("humidity").and_then(|h| h.as_f64()) {
                    println!("  Humidity: {:.1}%", humid);
                }
            }
        }
    }
    
    Ok(())
}

Check Weather for All Plants

use growatt_api::Growatt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    client.login("username", "password").await?;
    
    // Get all plants
    let plants = client.get_plants().await?;
    
    // Check weather for each plant
    for plant in plants.0.iter() {
        println!("\n=== {} ===", plant.plant_name);
        
        match client.get_weather(&plant.plant_id).await {
            Ok(weather) => {
                // Process weather data
                if let Some(obj) = weather.get("obj") {
                    if let Some(datas) = obj.get("datas").and_then(|d| d.as_array()) {
                        if datas.is_empty() {
                            println!("No environmental devices found");
                        } else {
                            println!("Environmental devices: {}", datas.len());
                        }
                    }
                }
            }
            Err(e) => {
                eprintln!("Error fetching weather: {}", e);
            }
        }
    }
    
    Ok(())
}

Create Custom Weather Struct

use growatt_api::Growatt;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct EnvironmentalDevice {
    #[serde(rename = "deviceSn")]
    device_sn: Option<String>,
    temperature: Option<f64>,
    irradiance: Option<f64>,
    humidity: Option<f64>,
    #[serde(rename = "windSpeed")]
    wind_speed: Option<f64>,
}

#[derive(Debug, Deserialize)]
struct WeatherResponse {
    obj: Option<WeatherObj>,
}

#[derive(Debug, Deserialize)]
struct WeatherObj {
    datas: Option<Vec<EnvironmentalDevice>>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    client.login("username", "password").await?;
    
    let weather_json = client.get_weather("1234567").await?;
    
    // Deserialize into custom struct
    let weather: WeatherResponse = serde_json::from_value(weather_json)?;
    
    if let Some(obj) = weather.obj {
        if let Some(devices) = obj.datas {
            for device in devices {
                println!("Device: {:?}", device.device_sn);
                println!("  Temp: {:?}°C", device.temperature);
                println!("  Irradiance: {:?} W/m²", device.irradiance);
            }
        }
    }
    
    Ok(())
}

Monitoring Loop with Weather Data

use growatt_api::Growatt;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Growatt::new();
    client.login("username", "password").await?;
    
    let plant_id = "1234567";
    
    loop {
        // Get plant data and weather simultaneously
        let plant_data = client.get_plant(plant_id).await?;
        let weather = client.get_weather(plant_id).await?;
        
        println!("\n[{}]", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"));
        
        if let Some(power) = plant_data.current_power {
            println!("Power: {:.2} W", power);
        }
        
        if let Some(obj) = weather.get("obj") {
            if let Some(datas) = obj.get("datas").and_then(|d| d.as_array()) {
                for device in datas {
                    if let Some(temp) = device.get("temperature").and_then(|t| t.as_f64()) {
                        println!("Temperature: {:.1}°C", temp);
                    }
                    if let Some(irr) = device.get("irradiance").and_then(|i| i.as_f64()) {
                        println!("Irradiance: {:.1} W/m²", irr);
                    }
                }
            }
        }
        
        sleep(Duration::from_secs(300)).await; // 5 minutes
    }
}

Error Cases

The method may return the following errors:

GrowattError::NotLoggedIn

Thrown when the session is invalid and no credentials are stored for automatic re-authentication.
GrowattError::NotLoggedIn
Solution: Call login() with valid credentials before calling this method.

GrowattError::InvalidResponse

Thrown when the API returns an empty response or null data.
GrowattError::InvalidResponse("Empty response. Please ensure you are logged in.")
Common causes:
  • Invalid plant ID
  • Session expired during the request
  • Plant does not have any environmental monitoring devices installed
  • Plant does not exist or is not accessible to the authenticated user

GrowattError::RequestError

Thrown when the HTTP request fails due to network issues or server problems.
GrowattError::RequestError(reqwest::Error)
Common causes:
  • Network connectivity issues
  • Growatt server is down or unreachable
  • Request timeout

GrowattError::JsonError

Thrown when the response cannot be parsed as valid JSON.
GrowattError::JsonError(serde_json::Error)
Common causes:
  • Corrupted response data
  • Server returned non-JSON content

Implementation Details

API Endpoint

POST {base_url}/device/getEnvList
Default base URL: https://server.growatt.com

Request Parameters

The method sends a form-encoded POST request with:
  • plantId: The plant identifier
  • currPage: Set to “1” (pagination support)

Response Format

The API response structure varies based on the environmental devices installed. A typical response:
{
  "obj": {
    "datas": [
      {
        "deviceSn": "ENV12345",
        "deviceType": "ENV",
        "temperature": 25.5,
        "irradiance": 850.0,
        "humidity": 65.0,
        "windSpeed": 3.2
      }
    ],
    "count": 1
  }
}
If no environmental devices are installed, the response may be:
{
  "obj": {
    "datas": [],
    "count": 0
  }
}

Authentication

This method automatically calls check_login() before making the API request, which:
  1. Checks if the current session is valid
  2. Re-authenticates using stored credentials if the session expired
  3. Returns NotLoggedIn error if no credentials are available

Why serde_json::Value?

The method returns serde_json::Value instead of a strongly-typed struct because:
  • Environmental device configurations vary significantly between installations
  • The API may return different field sets based on device capabilities
  • This provides maximum flexibility for parsing device-specific data
  • Users can deserialize into custom structs that match their specific equipment

Build docs developers (and LLMs) love