#api-bindings #iot #api #cli-tool #cli #control-system

bin+lib yeelight

Rust CLI 和 API 绑定,用于 yeelight WiFi 灯互操作

10 个版本

0.4.1 2023 年 5 月 23 日
0.4.0 2022 年 6 月 30 日
0.4.0-rc.12021 年 12 月 30 日
0.3.0 2020 年 6 月 28 日
0.1.0 2020 年 6 月 16 日

#160命令行界面

Download history 2/week @ 2024-03-09 20/week @ 2024-03-30 2/week @ 2024-04-06 6/week @ 2024-04-27 1/week @ 2024-05-25

101 每月下载
用于 yeelight-cli

MIT 许可证

82KB
1.5K SLoC

Yeelight

built with nix build

本项目为 Yeelight Wi-Fi 灯互操作规范提供 Rust 绑定。

规范中指定的所有方法都已实现,并且名称与上述规范相同。

本项目可以作为二进制 crate 或带有 API 绑定的库供开发者使用。二进制 crate 提供一个 CLI 工具来控制灯光(这代替了 yeelight-cli

目录

小米警告

!!! 不要更新小米品牌产品的灯泡固件 !!!

从 2021 年 1 月开始,小米品牌智能灯泡在更新固件时丢失了 LAN 控制功能,并且没有回滚选项。查看

CLI 使用

您可以通过安装并使用 cargo 或 cargo run 运行一个 CLI 来控制灯泡。程序名称将为 yeelight

cargo install yeelight
yeelight --help # or cargo run -- --help

有所有 yeelight API 规范的命令

yeelight 0.4.0
A CLI to control your Yeelight smart lights.

USAGE:
    yeelight [OPTIONS] [address] <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -p, --port <port>           [env: YEELIGHT_PORT=]  [default: 55443]
    -t, --timeout <timeout>     [env: YEELIGHT_TIMEOUT=]  [default: 5000]

ARGS:
    <address>     [env: YEELIGHT_ADDR=]  [default: NULL]

SUBCOMMANDS:
    adjust            Adjust properties (Bright/CT/Color) (increase/decrease/circle)
    adjust-percent    Adjust properties (Bright/CT/Color) with percentage (-100~100)
    discover          
    flow              Start color flow
    flow-stop         Stop color flow
    get               Get properties
    help              Prints this message or the help of the given subcommand(s)
    listen            Listen to notifications from lamp
    music-connect     Connect to music TCP stream
    music-stop        Stop music mode
    off               Turn off light
    on                Turn on light
    preset            Presets
    set               Set values
    timer             Start timer
    timer-clear       Clear current timer
    timer-get         Get remaining minutes for timer
    toggle            Toggle light

指定灯具

唯一必需的参数是 <address>,可以是 IP 地址、灯泡名称或值 all

  • 如果提供了 IP 地址,yeelight 将尝试连接到该地址上的灯泡;如果 timeout 毫秒内无法建立连接,则将失败。
  • 如果提供了名称,yeelight 将发送发现消息并等待具有给定名称的灯泡响应。如果在 timeout 毫秒内找不到此类灯泡,则操作将失败。
  • 如果给出了特殊关键字 all,则命令将在 timeout 窗口内响应网络中所有发现消息的灯泡上运行。

此外,您可以设置环境变量 YEELIGHT_ADDR 来指定默认地址,如果未提供。

运行 discovery 命令时,无需指定地址,在其他所有情况下,必须提供地址。

子命令

有关每个子命令的功能和选项的详细信息,可以在每个子命令上发出 --help

库使用

用法非常直接,您可以使用内置的灯泡发现方法来定位灯泡并与它们连接。如果您事先知道地址,也可以直接连接。连接到灯泡后,您可以调用各种方法来更改其状态。

灯泡

Bulb 对象表示对单个灯的活跃连接。所有操作都是通过在这个对象上调用方法来执行的。

发现

连接

从发现的灯泡

您可以通过调用 discover::DiscoveredBulb 来将一个 Bulb 升级为一个 discover::DiscoveredBulb,方法是调用 discover::DiscoveredBulb::connect

从地址

您可以使用 Bulb::connect 或使用 Bulb::attachBulb::attach_tokio 从活动 TCP 连接创建一个 Bulb 来通过地址和端口进行连接。

基本操作

您可以通过查看 Bulb 对象文档来查看所有可用的方法和它们的参数。

音乐模式

音乐模式实际上将现有连接升级为反向连接(灯泡连接到库),允许您发送命令而不会受到速率限制。

启动音乐模式将启动一个新的监听套接字,告诉灯泡连接到该套接字,然后关闭旧连接。使用库/您的项目运行的机器的 IP 地址作为主机。例如,192.168.5.23

注意

请确保使用 1.X 版本的 Tokio 才能正常工作。

实现细节

此 crate 功能齐全,可以使用惯用的 Rust 方法控制 Yeelight 智能灯泡。

异步

此 crate 使用 tokio 来管理与 LED 的所有连接。

背景光

背景光方法通过在方法名前添加前缀“bg_”与前景方法分开,例如在yeelight规范中。这意味着前景光有Bulb::set_power,而背景对应方法有Bulb::bg_set_power,依此类推。

未来可能会通过在支持的方法中添加isBackground参数来改变这一情况。

功能

默认情况下,此crate使用所有功能。在某些情况下,如果空间最为关键,您可以在编译时省略一些功能以减小其影响。

目前只有2个不同的功能

  • "from-str":此功能使能够从字符串中解析灯泡的响应和地址。
  • "discovery":此功能使灯泡发现成为可能。

未来可能会添加另一个功能,完全移除tokio,使得在最小系统中也能使用此crate。然而,您可以使用不带async的0.2版本。

示例

所有示例也可以在examples目录中找到。

柯拉茨

use std::{thread, time::Duration};

use yeelight::{Bulb, Effect, Mode, Power, Properties, Property};

// This program is meant to demonstrate some examples of commands and how to read the results turns
// on the bulb, changes the brightness following the collatz sequence (mod 100) 10 times waiting 1
// second each, and then sets the color to red over 10 seconds.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    env_logger::init();

    let mut bulb = Bulb::connect("192.168.1.204", 55443).await?;

    // Turn on the bulb
    println!(
        "Response: {:?}",
        bulb.set_power(
            Power::On,
            Effect::Sudden,
            Duration::from_secs(1),
            Mode::Normal
        )
        .await?
    );

    // Define flow array
    let props = Properties(vec![
        Property::Power,
        Property::Bright,
        Property::CT,
        Property::RGB,
    ]);

    for _ in 1..10u8 {
        let response = bulb.get_prop(&props).await?.unwrap();
        let brightness = response[1].parse::<u32>()?;

        // Change brightness following collatz sequence
        let brightness = if brightness % 2 == 0 {
            brightness / 2
        } else {
            brightness * 3 + 1
        };

        // Make sure brightness is between 1 and 100.
        let brightness = (brightness % 100 + 1) as u8;
        println!("Setting brightness to {}", brightness);

        // Change brightness
        let response = bulb
            .set_bright(brightness, Effect::Smooth, Duration::from_secs(1))
            .await?;
        eprintln!("Response: {:?}", response);

        thread::sleep(Duration::from_secs(1));
    }

    // Set bulb to pure red over 10 seconds
    bulb.set_rgb(0xff_00_00, Effect::Smooth, Duration::from_secs(1))
        .await?;
    Ok(())
}

use std::time::Duration;

use yeelight::{Bulb, CfAction, Effect, FlowExpresion, FlowTuple, Mode, Power};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    env_logger::init();

    let my_bulb_ip = "192.168.1.204";
    let mut bulb = Bulb::connect(my_bulb_ip, 55443).await?;

    // Turn on the bulb
    let response = bulb
        .set_power(Power::On, Effect::Sudden, Duration::new(0, 0), Mode::Normal)
        .await?;
    println!("response: {:?}", response);

    // Define flow array
    let flow = FlowExpresion(vec![
        FlowTuple::ct(Duration::from_millis(500), 3000, 100),
        FlowTuple::sleep(Duration::from_millis(1500)),
        FlowTuple::ct(Duration::from_millis(500), 5000, 100),
        FlowTuple::sleep(Duration::from_millis(1500)),
        FlowTuple::ct(Duration::from_millis(500), 2600, 100),
        FlowTuple::sleep(Duration::from_millis(1500)),
    ]);
    // Send flow command
    let response = bulb.start_cf(10, CfAction::Stay, flow).await?;
    println!("response: {:?}", response);
    Ok(())
}

音乐

use std::time::Duration;

use yeelight::{Bulb, Effect, Power, Mode};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    env_logger::init();

    let my_bulb_ip = "192.168.1.200";
    let my_computer_ip = "192.168.1.23";

    let mut bulb = Bulb::connect(my_bulb_ip, 0).await?;
    let mut music_conn = bulb.start_music(my_computer_ip).await?;

    let sleep_duration = Duration::from_millis(300);
    let no_duration = Duration::from_millis(0);

    bulb.set_power(Power::On, Effect::Sudden, no_duration, Mode::Normal).await?;

    for _ in 0..60 { 
        std::thread::sleep(sleep_duration);
        music_conn.set_rgb(0x00ff00, Effect::Sudden, no_duration).await?;
        std::thread::sleep(sleep_duration);
        music_conn.set_rgb(0x0000ff, Effect::Sudden, no_duration).await?;
        std::thread::sleep(sleep_duration);
        music_conn.set_rgb(0xff0000, Effect::Sudden, no_duration).await?;
    }

    drop(music_conn);

    Ok(())
}

通知

use yeelight::Bulb;

#[tokio::main]
async fn main() {

    env_logger::init();

    let my_bulb_ip = "192.168.1.200";
    let mut bulb = Bulb::connect(my_bulb_ip, 55443)
        .await
        .expect("Connection failed");
    if let Some(response) = bulb.toggle().await.expect("Error") {
        for v in response.iter() {
            println!("{}", v);
        }
    }
}

属性

use std::time::Duration;

use yeelight::{Bulb, Effect, Mode, Power, Properties, Property};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    env_logger::init();

    let my_bulb_ip = "192.168.1.204";
    let mut bulb = Bulb::connect(my_bulb_ip, 55443).await?;

    // Turn on the bulb
    println!(
        "Response: {:?}",
        bulb.set_power(
            Power::On,
            Effect::Sudden,
            Duration::from_millis(0),
            Mode::Normal
        )
        .await?
    );

    // Define flow array
    let props = Properties(vec![
        Property::Power,
        Property::Bright,
        Property::CT,
        Property::RGB,
    ]);
    // Send flow command
    println!("Response: {:?}", bulb.get_prop(&props).await?);
    println!(
        "Response: {:?}",
        bulb.set_rgb(122, Effect::Smooth, Duration::from_millis(500))
            .await?
    );
    Ok(())
}

切换

use yeelight::Bulb;

#[tokio::main]
async fn main() {

    env_logger::init();

    let mut bulb = Bulb::connect(my_bulb_ip, 55443)
        .await
        .expect("Connection failed");
    if let Some(response) = bulb.toggle().await.expect("Error") {
        for v in response.iter() {
            println!("{}", v);
        }
    }
}

路线图

目前所有主要API功能都已实现,仅计划对API的可用性进行更改。不过,计划中的更改可能会引入破坏性更改。

API 功能

  • 实现所有API函数
  • 处理灯泡响应
  • 在网络中发现灯泡
  • 监听灯泡通知

生活质量

  • 移除[CronType]?
  • 灯泡响应超时
  • 更改处理背景LED的方式
  • 合并[Effect]和Duration参数
  • 使音乐工作流程更易用
  • 处理灯泡组

测试

  • 涵盖所有主要方法

故障排除

连接被拒绝

错误:操作系统{代码: 111,类型:连接被拒绝,消息: "连接拒绝" }

  • 请确保关闭正在运行的VPN。

无效参数

错误:响应错误(-5001, "无效参数")

  • 在Yeelight/Mi Home应用上关闭音乐流。

速率限制

依赖项

~5–14MB
~167K SLoC