受欢迎的博客标签

实现MQTT协议 - 实现MQTTnet Clinet并接入Home Assistant  - MqttAutoDiscovery

Published

实现MQTT协议 - 实现MQTTnet Clinet并接入Home Assistant  - MqttAutoDiscovery

 

在发布自动发现消息(PublishSwitchDiscovery)后,需要订阅命令主题(command_topic)

在命令主题的消息处理事件程序中,根据接收到的命令("ON"或"OFF")更新设备状态,并将更新后的状态发布到状态主题(state_topic)。

实时发布设备的当前可用性消息(例如,在连接时发布"online",在断开连接时发布"offline")

在Home Assistant 打开开关

在Home Assistant 打开开关后,会向命令主题(command_topic)发送一个消息("ON" 或 "OFF")。

我们要在 MQTT 客户端中订阅这个命令主题,以便接收开关状态变化的指令。


当收到指令时,我们需要更新开关的状态,并将状态发布到状态主题(state_topic)),这样Home Assistant就会知道开关的当前状态

构建完整主题-topic

 var uniqueId = "my_csharp_switch";
 var discoveryTopic = $"homeassistant/switch/{uniqueId}/config";

 state_topic = $"homeassistant/switch/{uniqueId}/state",
 command_topic = $"homeassistant/switch/{uniqueId}/command",

availability 

  new { topic = $"homeassistant/switch/{uniqueId}/availability" }

使用 MQTT 客户端工具(如 MQTT Explorer)订阅:

使用以下主题:

配置主题:homeassistant/switch/my_csharp_switch/config        设备配置信息,也是自动发现主题
状态主题:homeassistant/switch/my_csharp_switch/state           状态数据:
命令主题:homeassistant/switch/my_csharp_switch/command   可写控制命令,如开关:开、关
有效主题:homeassistant/switch/my_csharp_switch/availability   设备在线状态。如开关:离线,在线

 

构建发现消息-Home Assistant automatically discovers MQTT devices

Create virtual devices

Adding devices to Home Assistant

#region example Mqtt Switch Discovery  


/// <summary>
/// run ok
/// Home Assistant automatically discovers MQTT devices if you send a discovery payload:
/// </summary>
/// <param name="mqttClient"></param>
/// <returns></returns>
public async Task PublishSwitchDiscovery(IMqttClient mqttClient)
{
    var uniqueId = "my_csharp_switch";
    var discoveryTopic = $"homeassistant/switch/{uniqueId}/config";

    var payload = new
    {
        name = "My C# MQTT Switch",
        unique_id = uniqueId,
        state_topic = $"homeassistant/switch/{uniqueId}/state",
        command_topic = $"homeassistant/switch/{uniqueId}/command",
        payload_on = "ON",
        payload_off = "OFF",
        availability = new[]
        {
          new { topic = $"homeassistant/switch/{uniqueId}/availability" }    // for PublishAvailabilityAsync,online or offline
        },
        retain = true, // Crucial for discovery persistence
        device = new // Optional: Group with other entities  come from:https://www.home-assistant.io/integrations/switch.mqtt/
        {
            identifiers = new[] { "my_csharp_device" },
            name = "My C# Device",
            manufacturer = "MyCompany",
            model_id = "dx29",
            sw_version = "1.0.0",

        }
    };

    var jsonPayload = JsonSerializer.Serialize(payload);
    var message = new MqttApplicationMessageBuilder()
        .WithTopic(discoveryTopic)
        .WithPayload(jsonPayload)
        .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
        .WithRetainFlag(true)
        .Build();

    await mqttClient.PublishAsync(message);
}
#endregion

 

订阅消息

 

 

 

 

当连上mqtt server后,会收到这几个主题

[ 收到消息: 主题=homeassistant/switch/my_csharp_switch/command, 内容=ON
Received on homeassistant/switch/my_csharp_switch/command: ON
[ 收到消息: 主题=homeassistant/switch/my_csharp_switch/state, 内容=OFF
Received on homeassistant/switch/my_csharp_switch/state: OFF
[ 收到消息: 主题=homeassistant/switch/my_csharp_switch/availability, 内容=online

 

状态同步

当收到命令后,必须更新状态主题(state_topic)
使用保留消息(retain=true)确保状态持久化

 

    mqttClient.ApplicationMessageReceivedAsync += async e =>
    {
        var topic = e.ApplicationMessage.Topic;
        var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);

        // 根据 QoS 级别可定制 ACK 行为
        if (e.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
        {
            // 手动确认
            await e.AcknowledgeAsync(CancellationToken.None);
        }

        Console.WriteLine($"[处理事件(内嵌)]test function:Received Message on {topic}: {payload}");

        var uniqueId = "my_csharp_switch";

        string command = e.ApplicationMessage.ConvertPayloadToString();
        Console.WriteLine($"Received command from HA: {command}");
        var stateTopic = $"homeassistant/switch/{uniqueId}/state";

        if (e.ApplicationMessage.Topic == $"homeassistant/switch/{uniqueId}/command")
        {
            


            if (command == "ON")
            {
                // Your device logic to turn on goes here
                var state = "ON";

             
                //根据收到的命令发布状态主题,保持同步
                var msg = new MqttApplicationMessageBuilder()
.WithTopic(stateTopic)
.WithPayload(state)
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag(false) // 保持状态   false // 不保留消息
.Build();

               var result= await mqttClient.PublishAsync(msg);
                if (result.ReasonCode==MqttClientPublishReasonCode.Success)
                {
                    Console.WriteLine($"from pc to HA:状态发布成功 {result.IsSuccess}");
                }
                else
                {
                    Console.WriteLine($"from pc to HA:状态发布成功: {result.ReasonCode} {result.ReasonString}");
                }
            }
            else if (command == "OFF")
            {
                var state = "OFF";

                // Your device logic to turn off goes here

                var msg = new MqttApplicationMessageBuilder()
.WithTopic(stateTopic)
.WithPayload(state)
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag(true) // 保持状态
.Build();

                await mqttClient.PublishAsync(msg);
            }
        }


    };