diff --git a/CMakeLists.txt b/CMakeLists.txt index 11d7928..5c24e80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_compile_options(-Wall) # Find Qt packages -find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia SerialPort REQUIRED) +find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia SerialPort Mqtt REQUIRED) # Find dependencies using pkg-config find_package(PkgConfig REQUIRED) @@ -25,7 +25,8 @@ set(CMAKE_AUTOUIC ON) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) # Create executable -add_executable(SHinterface) +add_executable(SHinterface + src/sensors/mqttsensorsource.h src/sensors/mqttsensorsource.cpp) # Add sources to executable target_sources(SHinterface @@ -159,6 +160,7 @@ target_link_libraries(SHinterface Qt6::Network Qt6::Multimedia Qt6::SerialPort + Qt6::Mqtt ${PIPEWIRE_LIBRARIES} ${LIBNL3_LIBRARIES} ) diff --git a/src/mainobject.cpp b/src/mainobject.cpp index 4f31e78..0f41d19 100644 --- a/src/mainobject.cpp +++ b/src/mainobject.cpp @@ -81,6 +81,7 @@ PrimaryMainObject::PrimaryMainObject(QIODevice* microDevice, const QString& sett connect(tcpServer, &TcpServer::gotSensor, &globalSensors, &SensorStore::sensorGotState); connect(&sunSensorSource, &SunSensorSource::stateChanged, &globalSensors, &SensorStore::sensorGotState); connect(µ, &Microcontroller::gotSensorState, &globalSensors, &SensorStore::sensorGotState); + connect(&mqttSensorSource, &MqttSensorSource::stateChanged, &globalSensors, &SensorStore::sensorGotState); sunSensorSource.run(); @@ -93,6 +94,9 @@ PrimaryMainObject::PrimaryMainObject(QIODevice* microDevice, const QString& sett loadFromDisk(settingsPath); + QJsonObject mqttJson = settings["Mqtt"].toObject(); + mqttSensorSource.start(mqttJson); + tcpServer->launch(QHostAddress(host), port); connect(&globalItems, &ItemStore::itemUpdated, tcpServer, &TcpServer::itemUpdated); } @@ -105,6 +109,9 @@ PrimaryMainObject::~PrimaryMainObject() void PrimaryMainObject::store(QJsonObject &json) { globalItems.store(json); + QJsonObject mqttJson = json["Mqtt"].toObject(); + mqttSensorSource.store(mqttJson); + json["Mqtt"] = mqttJson; } void PrimaryMainObject::load(const QJsonObject& json) diff --git a/src/mainobject.h b/src/mainobject.h index 05b8140..79f5ed2 100644 --- a/src/mainobject.h +++ b/src/mainobject.h @@ -14,6 +14,7 @@ #include "microcontroller.h" #include "ui/mainwindow.h" #include "sensors/sunsensor.h" +#include "sensors/mqttsensorsource.h" #include "items/fixeditemsource.h" #include "items/itemloadersource.h" #include "tcpserver.h" @@ -45,6 +46,7 @@ public: //sensors SunSensorSource sunSensorSource; + MqttSensorSource mqttSensorSource; //item sources FixedItemSource fixedItems; diff --git a/src/sensors/mqttsensorsource.cpp b/src/sensors/mqttsensorsource.cpp new file mode 100644 index 0000000..4202ab8 --- /dev/null +++ b/src/sensors/mqttsensorsource.cpp @@ -0,0 +1,187 @@ +#include "mqttsensorsource.h" + +#include + +MqttSensorSource::MqttSensorSource(QObject *parent) + : QObject{parent} +{ +} + +void MqttSensorSource::start(const QJsonObject& settings) +{ + baseTopicName = settings["BaseTopic"].toString("zigbee2mqtt"); + + connect(&client, &QMqttClient::stateChanged, this, &MqttSensorSource::onClientStateChanged); + connect(&client, &QMqttClient::errorChanged, this, &MqttSensorSource::onClientError); + + client.setHostname(settings["Host"].toString("127.0.0.1")); + client.setPort(settings["Port"].toInt(1883)); + if(settings.contains("User")) + client.setUsername(settings["User"].toString()); + if(settings.contains("Password")) + client.setPassword(settings["Password"].toString()); + client.setProtocolVersion(QMqttClient::MQTT_5_0); + + client.connectToHost(); + + QJsonArray sensorsArray = settings["Sensors"].toArray(); + + for(QJsonValueRef sensorRef : sensorsArray) + { + QJsonObject sensorObject = sensorRef.toObject(); + if(!sensorObject.contains("Topic")) + continue; + SensorSubscription sensor; + sensor.topic = sensorObject["Topic"].toString(); + if(!sensorObject.contains("Name")) + sensor.name = sensor.topic; + else + sensor.name = sensorObject["Name"].toString(); + sensor.id = qHash(baseTopicName + "/" + sensor.topic); + sensors.push_back(sensor); + } +} + +void MqttSensorSource::onClientError(QMqttClient::ClientError error) +{ + qWarning()<<"MQTT Client error:"< +#include +#include +#include + +#include "sensor.h" + +class MqttSensorSource : public QObject +{ + Q_OBJECT + + struct SensorSubscription + { + uint64_t id; + QString topic; + QString name; + QMqttSubscription* subscription = nullptr; + }; + + QString baseTopicName; + std::vector sensors; + QMqttClient client; + +private: + SensorSubscription& findSubscription(const QString& topic); + +private slots: + void onClientStateChanged(QMqttClient::ClientState state); + void onMessageReceived(const QMqttMessage& message); + void onClientError(QMqttClient::ClientError error); + +public: + explicit MqttSensorSource(QObject *parent = nullptr); + void start(const QJsonObject& settings); + void store(QJsonObject& json); + +signals: + void stateChanged(Sensor sensor); +}; + +#endif // MQTTSENSORSOURCE_H diff --git a/src/sensors/sensor.h b/src/sensors/sensor.h index 86c82b9..bc5e748 100644 --- a/src/sensors/sensor.h +++ b/src/sensors/sensor.h @@ -17,6 +17,10 @@ public: static constexpr uint8_t TYPE_BRIGHTNESS = 4; static constexpr uint8_t TYPE_BUTTON = 5; static constexpr uint8_t TYPE_ADC = 6; + static constexpr uint8_t TYPE_CO2 = 7; + static constexpr uint8_t TYPE_FORMALDEHYD= 8; + static constexpr uint8_t TYPE_PM25 = 9; + static constexpr uint8_t TYPE_TOTAL_VOC = 10; static constexpr uint8_t TYPE_LOWBATTERY = 128; static constexpr uint8_t TYPE_SHUTDOWN_IMMINENT = 251; static constexpr uint8_t TYPE_OCUPANCY = 252; @@ -25,13 +29,13 @@ public: static constexpr uint8_t TYPE_DUMMY = 255; uint8_t type; - uint8_t id; + uint64_t id; float field; QString name; QDateTime lastSeen; bool hidden; - Sensor(uint8_t typeIn, uint8_t idIn, float fieldIn = 0, QString nameIn = "", bool hiddenIn = false): type(typeIn), + Sensor(uint64_t typeIn, uint8_t idIn, float fieldIn = 0, QString nameIn = "", bool hiddenIn = false): type(typeIn), id(idIn), field(fieldIn), name(nameIn), hidden(hiddenIn) { lastSeen = QDateTime::currentDateTime(); diff --git a/src/ui/mainwindow.ui b/src/ui/mainwindow.ui index 2bb3874..0e6732c 100644 --- a/src/ui/mainwindow.ui +++ b/src/ui/mainwindow.ui @@ -42,7 +42,7 @@ false - +