denki 0.1.2

CLI for controlling TP-Link smart home devices — denki (電気) means electricity in Japanese
Documentation
use super::common::{format_energy_lines, format_wday, sort_energy_entries};
use crate::output::terminal_text;
use crate::tapo::TapoEnergyUsage;
use colored::Colorize;

pub fn print_tapo_energy(usage: &TapoEnergyUsage) {
    if let Some(power) = usage.current_power {
        crate::output::println!("Power:         {:.3} W", power as f64 / 1000.0);
    }
    if let Some(today) = usage.today_energy {
        crate::output::println!("Today:         {today} Wh");
    }
    if let Some(month) = usage.month_energy {
        crate::output::println!("This month:    {month} Wh");
    }
    if let Some(minutes) = usage.today_runtime {
        crate::output::println!("Runtime today: {}m", minutes);
    }
    if usage.current_power.is_none() && usage.today_energy.is_none() && usage.month_energy.is_none()
    {
        crate::output::println!(
            "{}",
            "No energy measurements were returned by this Tapo device.".yellow()
        );
    }
}

pub fn print_schedules(json: &serde_json::Value) {
    if let Some(rules) = json
        .pointer("/schedule/get_rules/rule_list")
        .and_then(|v| v.as_array())
    {
        if rules.is_empty() {
            crate::output::println!("No schedules configured.");
            return;
        }
        crate::output::println!("{}", "Schedules:".bold());
        for r in rules {
            let enabled = r
                .get("enable")
                .and_then(serde_json::Value::as_u64)
                .unwrap_or(0)
                == 1;
            let name = r
                .get("name")
                .and_then(serde_json::Value::as_str)
                .unwrap_or("(unnamed)");
            let smin = r
                .get("smin")
                .and_then(serde_json::Value::as_u64)
                .unwrap_or(0);
            let sact = r
                .get("sact")
                .and_then(serde_json::Value::as_i64)
                .unwrap_or(1);
            let action = if sact == 1 { "on".green() } else { "off".red() };
            let time = format!("{:02}:{:02}", smin / 60, smin % 60);
            let days = format_wday(r.get("wday").and_then(|v| v.as_array()));
            let status = if enabled {
                "".normal()
            } else {
                " (disabled)".dimmed()
            };
            crate::output::println!(
                "  {} at {}  {}  {}{}",
                action,
                time,
                days,
                terminal_text(name).bold(),
                status
            );
        }
    }
}

pub fn print_energy_realtime(json: &serde_json::Value) {
    let data = json
        .pointer("/emeter/get_realtime")
        .or_else(|| json.pointer("/smartlife.iot.common.emeter/get_realtime"));

    if let Some(d) = data {
        if d.get("err_code")
            .and_then(serde_json::Value::as_i64)
            .unwrap_or(0)
            != 0
        {
            let msg = d
                .get("err_msg")
                .and_then(|v| v.as_str())
                .unwrap_or("unknown error");
            crate::output::println!(
                "{}",
                format!("Energy not supported: {}", terminal_text(msg)).yellow()
            );
            return;
        }
        for line in format_energy_lines(d) {
            crate::output::println!("{line}");
        }
    } else {
        crate::output::println!(
            "{}",
            "No energy data — device may not support energy monitoring.".yellow()
        );
    }
}

pub fn print_energy_daily(json: &serde_json::Value, month: &str) {
    let days = json
        .pointer("/emeter/get_daystat/day_list")
        .or_else(|| json.pointer("/smartlife.iot.common.emeter/get_daystat/day_list"))
        .and_then(|v| v.as_array());

    if let Some(list) = days {
        crate::output::println!(
            "{}",
            format!("Daily energy usage for {}:", terminal_text(month)).bold()
        );
        for (day, wh) in sort_energy_entries(list, "day") {
            let bar = "#".repeat((wh / 10).min(40) as usize);
            crate::output::println!("  Day {:2}: {:4} Wh  {}", day, wh, bar.yellow());
        }
    }
}

pub fn print_energy_monthly(json: &serde_json::Value, year: u16) {
    let months = json
        .pointer("/emeter/get_monthstat/month_list")
        .or_else(|| json.pointer("/smartlife.iot.common.emeter/get_monthstat/month_list"))
        .and_then(|v| v.as_array());

    if let Some(list) = months {
        crate::output::println!("{}", format!("Monthly energy usage for {year}:").bold());
        for (month, wh) in sort_energy_entries(list, "month") {
            let bar = "#".repeat((wh / 100).min(40) as usize);
            crate::output::println!("  Month {:2}: {:5} Wh  {}", month, wh, bar.yellow());
        }
    }
}