24个版本

0.15.2 2022年10月1日
0.15.0 2022年3月9日
0.14.0 2021年1月5日
0.13.2 2020年12月30日
0.7.0 2018年7月20日

#149 in HTTP服务器

Download history 1/week @ 2024-04-14 19/week @ 2024-04-21 1/week @ 2024-05-12 1/week @ 2024-05-26 50/week @ 2024-06-09 99/week @ 2024-06-16 62/week @ 2024-06-23 99/week @ 2024-06-30 123/week @ 2024-07-28

214 每月下载次数
用于 somfy

MPL-2.0 许可证

94KB
2K SLoC

webthing

Build Status Crates.io license

HTTP Web Thing的实现。

使用

如果你使用 Cargo,只需将 webthing 添加到你的 Cargo.toml

[dependencies]
webthing = "0.15"

TLS支持

如果你需要服务器端的TLS支持,你需要使用 ssl 功能集进行编译。

示例

在这个示例中,我们将设置一个可调光的灯和一个湿度传感器(当然,两者都使用假数据)。两个工作示例可以在 这里 找到。

可调光灯

想象一下,你有一个可以调光的灯,你想通过物联网API公开它。该灯可以开启/关闭,并且可以从0%到100%设置亮度。除了名称、描述和类型外,一个 Light 还需要公开两个属性

  • on: 灯的状态,是否开启或关闭
    • 通过REST API的PUT调用设置此属性,可以通过调用 PUT {"on": true/false} 来切换灯的开关。
  • brightness: 灯的亮度级别,从0-100%
    • 通过PUT调用REST API设置此属性可以设置该灯的亮度级别。

首先创建一个新的Thing

let mut light = BaseThing::new(
    "urn:dev:ops:my-lamp-1234".to_owned(),
    "My Lamp".to_owned(),
    Some(vec!["OnOffSwitch".to_owned(), "Light".to_owned()]),
    Some("A web connected lamp".to_owned()),
);

现在我们可以添加所需的属性。

on 属性报告并设置灯的开关状态。对我们来说,我们只想在灯打开/关闭时记录新状态。

struct OnValueForwarder;

impl ValueForwarder for OnValueForwarder {
    fn set_value(&mut self, value: serde_json::Value) -> Result<serde_json::Value, &'static str> {
        println!("On-State is now {}", value);
        Ok(value)
    }
}

let on_description = json!({
    "@type": "OnProperty",
    "title": "On/Off",
    "type": "boolean",
    "description": "Whether the lamp is turned on"
});
let on_description = on_description.as_object().unwrap().clone();
thing.add_property(Box::new(BaseProperty::new(
    "on".to_owned(),
    json!(true),
    Some(Box::new(OnValueForwarder)),
    Some(on_description),
)));

亮度 属性报告光的亮度级别并设置该级别。和以前一样,我们不是真正设置光的级别,而是记录该级别。

struct BrightnessValueForwarder;

impl ValueForwarder for BrightnessValueForwarder {
    fn set_value(&mut self, value: serde_json::Value) -> Result<serde_json::Value, &'static str> {
        println!("Brightness is now {}", value);
        Ok(value)
    }
}

let brightness_description = json!({
    "@type": "BrightnessProperty",
    "title": "Brightness",
    "type": "number",
    "description": "The level of light from 0-100",
    "minimum": 0,
    "maximum": 100,
    "unit": "percent"
});
let brightness_description = brightness_description.as_object().unwrap().clone();
thing.add_property(Box::new(BaseProperty::new(
    "brightness".to_owned(),
    json!(50),
    Some(Box::new(BrightnessValueForwarder)),
    Some(brightness_description),
)));

现在我们可以将我们新创建的事物添加到服务器并启动它

let mut things: Vec<Arc<RwLock<Box<dyn Thing + 'static>>>> = Vec::new();
things.push(Arc::new(RwLock::new(Box::new(light)));

// If adding more than one thing, use ThingsType::Multiple() with a name.
// In the single thing case, the thing's name will be broadcast.
let mut server = WebThingServer::new(
    ThingsType::Multiple(things, "LightAndTempDevice".to_owned()),
    Some(8888),
    None,
    None,
    Box::new(Generator),
    None,
    None,
);
let server_addr = server.create();
server.start();

这将启动服务器,通过 WoT REST API 使灯光可用,并通过 mDNS 在您的本地网络上宣布其为可发现资源。

传感器

现在让我们也将湿度传感器连接到我们为灯光设置的服务器。

MultiLevelSensor(返回级别而不是仅开/关的传感器)有一个必需的属性(除了名称、类型和可选描述之外):level。我们想监视这个属性,并在值更改时接收通知。

首先创建一个新的Thing

let mut thing = BaseThing::new(
    "urn:dev:ops:my-humidity-sensor-1234".to_owned(),
    "My Humidity Sensor".to_owned(),
    Some(vec!["MultiLevelSensor".to_owned()]),
    Some("A web connected humidity sensor".to_owned()),
);

然后我们创建并添加适当的属性

  • level:告诉我们传感器实际读取的内容

    • 与灯光相反,值不能通过 API 调用来设置,因为这没有太多意义,设置传感器正在读取的内容。因此,我们正在创建一个 只读 属性。
    let level_description = json!({
        "@type": "LevelProperty",
        "title": "Humidity",
        "type": "number",
        "description": "The current humidity in %",
        "minimum": 0,
        "maximum": 100,
        "unit": "percent",
        "readOnly": true
    });
    let level_description = level_description.as_object().unwrap().clone();
    thing.add_property(Box::new(BaseProperty::new(
        "level".to_owned(),
        json!(0),
        None,
        Some(level_description),
    )));
    

现在我们有一个传感器,它不断报告 0%。为了使其可用,我们需要一个线程或某种类型的输入,当传感器有新的读取可用时。为此,我们启动一个线程,每隔几秒钟查询物理传感器。对于我们的目的,它只是调用一个假方法。

let sensor = Arc::new(RwLock::new(Box::new(sensor))));
let cloned = sensor.clone();
thread::spawn(move || {
    let mut rng = rand::thread_rng();

    // Mimic an actual sensor updating its reading every couple seconds.
    loop {
        thread::sleep(time::Duration::from_millis(3000));
        let t = cloned.clone();
        let new_value = json!(
            70.0 * rng.gen_range::<f32>(0.0, 1.0) * (-0.5 + rng.gen_range::<f32>(0.0, 1.0))
        );

        {
            let mut t = t.write().unwrap();
            let prop = t.find_property("level".to_owned()).unwrap();
            let _ = prop.set_value(new_value.clone());
        }

        t.write()
            .unwrap()
            .property_notify("level".to_owned(), new_value);
    }
});

这将使用随机的传感器读取来更新我们的属性。然后,将新的属性值发送到所有 WebSocket 听众。

添加到网关

要将您的 Web 事物添加到 WebThings Gateway,请安装“Web Thing”插件并按照此处的说明操作。

依赖关系

~13–27MB
~467K SLoC