using MQTTnet; using MQTTnet.Client; using MQTTnet.Client.Connecting; using MQTTnet.Client.Disconnecting; using MQTTnet.Client.Options; using MyCore.Interfaces.DTO; using MyCore.Interfaces.Models; using MyCore.Interfaces.Models.Providers.Zigbee.Zigbee2Mqtt; using MyCore.Service.Services; using MyCore.Services; using MyCore.Services.Devices; using MyCore.Services.MyControlPanel; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Mqtt.Client.AspNetCore.Services { public class MqttClientService : IMqttClientService { private static IMqttClient mqttClient; private IMqttClientOptions options; public static List devices = new List(); public static List devicesNew = new List(); public static List groups = new List(); public static string homeId; private DeviceDatabaseService _deviceDatabaseService; private GroupDatabaseService _groupDatabaseService; private ProviderDatabaseService _providerDatabaseService; private RoomDatabaseService _roomDatabaseService; private AutomationDatabaseService _automationDatabaseService; private ActionService _actionService; private HomeDatabaseService _homeDatabaseService; private AlarmDatabaseService _alarmDatabaseService; private EventDatabaseService _eventDatabaseService; public static string lastTopic; public static long lastTimeTopic; public MqttClientService(IMqttClientOptions options, HomeDatabaseService homeDatabaseService, DeviceDatabaseService deviceDatabaseService, GroupDatabaseService groupDatabaseService, ProviderDatabaseService providerDatabaseService, RoomDatabaseService roomDatabaseService, ActionService actionService, AutomationDatabaseService automationDatabaseService, AlarmDatabaseService alarmDatabaseService, EventDatabaseService eventDatabaseService) { this._homeDatabaseService = homeDatabaseService; this._deviceDatabaseService = deviceDatabaseService; this._groupDatabaseService = groupDatabaseService; this._providerDatabaseService = providerDatabaseService; this._roomDatabaseService = roomDatabaseService; this._automationDatabaseService = automationDatabaseService; this._alarmDatabaseService = alarmDatabaseService; this._eventDatabaseService = eventDatabaseService; this._actionService = actionService; Home home = this._homeDatabaseService.GetAll().ToList().Where(h => h.IsDefault).FirstOrDefault(); if (home != null) homeId = home.Id; System.Console.WriteLine($"Home Id in MQTT client service : {homeId}"); var server = "localhost"; var clientId = "ApiService"; #if DEBUG server = "192.168.31.140"; clientId = "ApiServiceTest"; #endif //this.options = options; this.options = new MqttClientOptionsBuilder() .WithClientId(clientId) .WithTcpServer(server) .WithCredentials("mqtt", "mqtt") .WithCleanSession() .Build(); mqttClient = new MqttFactory().CreateMqttClient(); ConfigureMqttClient(); } private void ConfigureMqttClient() { mqttClient.ConnectedHandler = this; mqttClient.DisconnectedHandler = this; mqttClient.ApplicationMessageReceivedHandler = this; } public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e) { var payload = ""; if (e.ApplicationMessage.Payload != null) { payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); } var topic = e.ApplicationMessage.Topic; var currentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); // For check if not doubled message (example : motion double true value) var test = currentTime - lastTimeTopic; var isWakeUpMessage = false; // if wakeup => Not drop wakeup message try { var deserialized = JsonConvert.DeserializeObject>(payload); if (deserialized != null) { isWakeUpMessage = (string)deserialized["action"] == "wakeup"; } } catch (Exception ex) { } var configMessage = topic == "zigbee2mqtt/bridge/event"; // Less than one second between two messages from a same device if (!(lastTopic == topic && test <= 500) || configMessage) { if (_actionService != null && homeId != null) { ActionService.HandleActionFromMQTTAsync(topic, payload, _deviceDatabaseService, _groupDatabaseService, _providerDatabaseService, _roomDatabaseService, _automationDatabaseService, _alarmDatabaseService, _eventDatabaseService, homeId); } } else { // TODO Check if not a valuable message (example state true to false => correct message..) System.Console.WriteLine($"Drop message - spam from {topic}"); } UpdateZigbee2MqttConfigOrStateAsync(topic, payload, homeId, _deviceDatabaseService, _groupDatabaseService, _providerDatabaseService, _roomDatabaseService); switch (topic) { case "zigbee2mqtt/bridge/config/devices": try { var devicesConvert = JsonConvert.DeserializeObject>(payload); devices = devicesConvert; } catch (Exception ex) { Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); } break; case "zigbee2mqtt/bridge/groups": try { var groupsConvert = JsonConvert.DeserializeObject>(payload); groups = groupsConvert; } catch (Exception ex) { Console.WriteLine($"Error during retrieving groups ! Exception: {ex}"); } break; case "zigbee2mqtt/bridge/devices": try { var devicesConvert = JsonConvert.DeserializeObject>(payload); devicesNew = devicesConvert; } catch (Exception ex) { Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); } break; } if (!isWakeUpMessage) { lastTimeTopic = DateTimeOffset.Now.ToUnixTimeMilliseconds(); lastTopic = topic; } return new Task(null); } public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs) { System.Console.WriteLine("connected"); await mqttClient.SubscribeAsync("#"); await PublishMessage("zigbee2mqtt/bridge/config/devices/get", ""); } public async Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs) { if (!mqttClient.IsConnected) { await mqttClient.ReconnectAsync(); } } public async Task StartAsync(CancellationToken cancellationToken) { await mqttClient.ConnectAsync(options); if (!mqttClient.IsConnected) { await mqttClient.ReconnectAsync(); } } public async Task StopAsync(CancellationToken cancellationToken) { if(cancellationToken.IsCancellationRequested) { var disconnectOption = new MqttClientDisconnectOptions { ReasonCode = MqttClientDisconnectReason.NormalDisconnection, ReasonString = "NormalDiconnection" }; await mqttClient.DisconnectAsync(disconnectOption, cancellationToken); } await mqttClient.DisconnectAsync(); } public static async Task PublishMessage(string topic, string message, bool retain = false) { var mqttMessage = new MqttApplicationMessageBuilder() .WithTopic(topic) .WithPayload(message) .WithExactlyOnceQoS() .WithRetainFlag(retain) .Build(); if (mqttClient.IsConnected) await mqttClient.PublishAsync(mqttMessage); } /*public static void SetServices(DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, RoomDatabaseService _RoomDatabaseService, ActionService _ActionService, AutomationDatabaseService _AutomationDatabaseService, string HomeId) { _deviceDatabaseService = _DeviceDatabaseService; _groupDatabaseService = _GroupDatabaseService; _providerDatabaseService = _ProviderDatabaseService; _roomDatabaseService = _RoomDatabaseService; _automationDatabaseService = _AutomationDatabaseService; _actionService = _ActionService; homeId = HomeId; }*/ public static async Task UpdateZigbee2MqttConfigOrStateAsync(string topic, string message, string homeId, DeviceDatabaseService _DeviceDatabaseService, GroupDatabaseService _GroupDatabaseService, ProviderDatabaseService _ProviderDatabaseService, RoomDatabaseService _RoomDatabaseService) { // update zigbee2mqqtt config switch (topic) { case "zigbee2mqtt/bridge/config/devices": try { var devices = JsonConvert.DeserializeObject>(message); var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(ProviderType.zigbee2mqtt); if (zigbee2mqttProvider != null) { // Retrieve existing devices List existingDevices = _DeviceDatabaseService.GetByProviderId(zigbee2mqttProvider.Id); var existingDevicesAddresses = existingDevices.Select(ed => ed.ServiceIdentification); // Filter devices and check if something new var filteredDevices = devices.Where(d => !existingDevicesAddresses.Contains(d.ieeeAddr)).ToList(); // Add new devices Dictionary> createdDevices = await DeviceService.CreateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _RoomDatabaseService, homeId, filteredDevices, zigbee2mqttProvider); } System.Console.WriteLine($"Devices updated for home {homeId}"); } catch (Exception ex) { Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); } break; case "zigbee2mqtt/bridge/devices": try { var devices = JsonConvert.DeserializeObject>(message); var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(ProviderType.zigbee2mqtt); if (zigbee2mqttProvider != null) { // Retrieve existing devices /*List existingDevices = _DeviceDatabaseService.GetByProviderId(zigbee2mqttProvider.Id); var existingDevicesAddresses = existingDevices.Select(ed => ed.ServiceIdentification);*/ System.Console.WriteLine($"Nbr of devices : {devices.Count}"); // Update supported operation Dictionary> createdDevices = await DeviceService.UpdateFromZigbeeAsync(_DeviceDatabaseService, _ProviderDatabaseService, _RoomDatabaseService, homeId, devices, zigbee2mqttProvider); } System.Console.WriteLine($"Devices updated for home {homeId} - more information"); } catch (Exception ex) { Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); } break; case "zigbee2mqtt/bridge/groups": try { var groupsConvert = JsonConvert.DeserializeObject>(message); var zigbee2mqttProvider = _ProviderDatabaseService.GetByType(ProviderType.zigbee2mqtt); if (zigbee2mqttProvider != null) { var groups = _GroupDatabaseService.GetByType(homeId, "zigbee2mqtt"); GroupService.CompareGroupsFromZigbee2Mqtt(homeId, groups, groupsConvert, _DeviceDatabaseService, _GroupDatabaseService); } } catch (Exception ex) { Console.WriteLine($"Error during retrieving groups ! Exception: {ex}"); } break; case "zigbee2mqtt/bridge/event": try { var eventConvert = JsonConvert.DeserializeObject(message); if (eventConvert.data?.ieee_address != null) { var zigbeeDevice = _DeviceDatabaseService.GetByServiceIdentification(eventConvert.data.ieee_address); if (zigbeeDevice != null && eventConvert.type == "device_announce") // Check if we can not hardcode that.. { var status = new List(); var state = new AutomationState(); state.Name = "state"; state.Value = "on"; status.Add(state); var buildRequest = new Dictionary(); buildRequest.Add(state.Name, state.Value); // Update device state ! zigbeeDevice.LastState = JsonConvert.SerializeObject(buildRequest); zigbeeDevice.LastStateDate = DateTime.Now; _DeviceDatabaseService.Update(zigbeeDevice); // Check if all group has the same state // Not needed as we test the first device of a group } } } catch (Exception ex) { Console.WriteLine($"Error during retrieving event ! Exception: {ex}"); } break; } } } }