Bootstrap

轻松集成:使用 .NET 在 Home Assistant 中添加自定义设备,实现电脑远程关机

本文介绍如何使用.NET为Home Assistant添加自定义设备,而无需进行额外的配置。通过使用Home Assistant的自动发现功能,我们可以让Home Assistant自动识别和添加新设备,从而简化设备管理流程。通过本文,你将学习如何编写一个简单的.NET程序,将其接入Home Assistant,并实现自定义设备的自动发现和远程关机。

1. 背景

通过前面的文章《搭建EMQX MQTT服务器并接入Home Assistant和.NET程序》,我们已经了解了如何部署EMQX MQTT服务器并接入Home Assistant。在这个基础上,我们可以通过MQTT协议实现设备之间的通信,包括设备的发现、状态更新等功能。Home Assistant提供了自动发现功能,可以自动识别和添加新设备,无需手动配置。这为我们添加自定义设备提供了便利。

在本文中,我们将介绍如何使用.NET为Home Assistant添加自定义设备,实现电脑远程关机的功能。我们将编写一个简单的.NET程序,将其接入Home Assistant,并实现自定义设备的自动发现和远程关机功能。通过这个例子,你将学习如何使用.NET和Home Assistant实现设备集成,为智能家居系统增加新的功能。

2. MQTT 发现消息格式

MQTT 发现消息需要遵循特定的格式:

<discovery_prefix>/<component>/[<node_id>/]<object_id>/config
  • <discovery_prefix>:发现前缀,默认为 homeassistant,可以在 Home Assistant 配置中更改。
  • <component>:支持的 MQTT 集成组件之一,例如 binary_sensor
  • <node_id> (可选):提供主题的节点 ID,不被 Home Assistant 使用,但可以用于结构化 MQTT 主题。
  • <object_id>:设备的 ID,用于区分不同的设备。

对于一个开关设备,我们可以使用以下格式:

homeassistant/button/pc/<pc_mac>/config

这里的 <pc_mac> 是电脑的 MAC 地址,这里通过这种简单的方式区分不同的设备。通过这个格式,我们可以实现设备的自动发现和配置。对于 button 组件,我们需要提供一些配置信息,例如设备的名称、唯一标识、状态主题、命令主题等。这些信息将帮助 Home Assistant 识别和管理设备,我们可以通过官网的设备介绍文档查看更多信息。

对于 MAC 地址的获取,我们可以通过下面的代码,例如:

/// <summary>
/// 获取第一个可用的网卡的MAC地址
/// </summary>
/// <returns></returns>
static string? GetMacAddress()
{
    return NetworkInterface.GetAllNetworkInterfaces()
        .Where(nic => nic.OperationalStatus == OperationalStatus.Up)
        .Select(nic => nic.GetPhysicalAddress().ToString())
        .FirstOrDefault();
}

3. 发布设备配置信息

在.NET程序中,我们在程序启动时需要发布该设备的配置信息,让Home Assistant能够识别和添加该设备。在前面的一篇文章中,我们已经了解了如何使用MQTTnet库来实现MQTT客户端,这里我们可以使用MQTTnet库来发布设备配置信息。

var topic = $"{iot_discovery_prefix}/{mac}";

// 发布发现消息
var discoveryMessage = new
{
    name = "我的电脑",
    command_topic = $"{topic}/commands",
    unique_id = $"pc_{mac}",
    availability = new
    {
        topic = $"{topic}/availability",
    }
};
var payload = Encoding.UTF8.GetBytes(System.Text.Json.JsonSerializer.Serialize(discoveryMessage));
Console.WriteLine($"Discovery message: {Encoding.UTF8.GetString(payload)}");

var message = new MqttApplicationMessageBuilder()
    .WithTopic($"{topic}/config")
    .WithPayload(payload)
    .Build();

这里我们使用了一个简单的JSON格式来表示设备的配置信息,包括设备的名称、命令主题、唯一标识、可用性主题等。这些信息将帮助Home Assistant识别和管理设备。我们将这个JSON消息发布到<discovery_prefix>/<component>/[<node_id>/]<object_id>/config主题,让Home Assistant能够识别和添加该设备。

4. 调整设备可用性

上一节的配置消息发布后,我们的设备将被Home Assistant自动识别和添加,无需手动配置。这为我们添加自定义设备提供了便利,同时也提高了设备的可管理性和易用性。但是需要注意的一点是,设备添加成功后是不可用的状态,需要我们调整设备的可用性,让Home Assistant能够正常使用该设备。

请添加图片描述

关于设备的可用性,我们需要在设备启动时发布可用性消息,表示设备已经启动并可用。这样Home Assistant才能正常使用该设备。我们可以在设备启动时发布一个可用性消息,例如:

// 发布在线消息
await mqttClient.PublishAsync(new MqttApplicationMessageBuilder()
    .WithTopic($"{topic}/availability")
    .WithPayload("online")
    .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce)
    .WithRetainFlag()
    .Build());

这里我们发布了一个online的消息,表示设备已经启动并可用。这样Home Assistant就能够正常使用该设备了。

请添加图片描述

当然,可用性的调整也需要考虑到设备的异常情况,例如设备的断线、关机等情况。在这种情况下,我们需要发布一个offline的消息,表示设备不可用。在设备开始连接配置时,我们可以配置一个遗嘱消息,当设备异常断线时,MQTT服务器会自动发布这个遗嘱消息,表示设备不可用。这样Home Assistant就能够及时发现设备的异常情况,提高设备的可靠性和稳定性。

var options = new MqttClientOptionsBuilder()
            .WithClientId(mac)
            .WithTcpServer("MQTT服务地址", 1883)
            .WithCredentials("你的客户端登录用户名", "你的密码")
            // 遗嘱消息
            .WithWillTopic($"{topic}/availability")
            .WithWillPayload("offline")
            .WithWillQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce)
            .WithCleanSession()
            .Build();

5. 实现远程关机功能

在设备添加和可用性调整后,我们的设备已经可以被Home Assistant正常使用了。接下来我们可以实现设备的功能,当监听到Home Assistant的命令时执行相应的操作,例如远程关机。这实现这一功能之前我们还需要再启动的时候订阅命令主题,例如:

// 订阅命令主题
await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic($"{topic}/commands").Build());

这里我们订阅了命令主题,当Home Assistant发送命令时我们就能够接收到。接收到命令后我们可以执行相应的操作,例如远程关机:

private static Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
{
    var topic = arg.ApplicationMessage.Topic;
    var payload = Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment);
    
    Console.WriteLine($"Message received on topic: {topic}.");
    Console.WriteLine($"Payload: {payload}");

    if (topic.EndsWith("commands"))
    {
        // 按钮按下
        if (payload == "PRESS")
        {
            Console.WriteLine("Shutting down...");
            // System.Diagnostics.Process.Start("shutdown", "/s /t 0");
            System.Diagnostics.Process.Start("shutdown", "/s");
            Environment.Exit(0);
            // 可以取消关机 shutdown /a
        }
    }

    return Task.CompletedTask;
}

当然这里的指令我没有使用立马关机,而是使用了弹窗的方式提醒即将关机,这样可以避免误操作。如果正在使用而出现了误操作,在调试时,我们可以使用命令行 shutdown -a 来取消关机。

6. 最后

通过这个例子,我们学习了如何使用.NET为Home Assistant添加自定义设备,实现电脑远程关机的功能。以上只是一个简单的使用案例,如果你需要具体的实现并使用这个工具,则还需要考虑更多的细节,例如信息的配置,开机启动等。通过这个例子,你可以学习如何使用.NET和Home Assistant实现设备集成,为智能家居系统增加新的功能。

;