Add Mqtt item
This commit is contained in:
parent
0fd50eb227
commit
eb60f85604
19 changed files with 901 additions and 48 deletions
|
|
@ -32,7 +32,11 @@ add_subdirectory(tests)
|
||||||
|
|
||||||
# Create executable
|
# Create executable
|
||||||
add_executable(SHinterface
|
add_executable(SHinterface
|
||||||
src/sensors/mqttsensorsource.h src/sensors/mqttsensorsource.cpp)
|
src/sensors/mqttsensorsource.h src/sensors/mqttsensorsource.cpp
|
||||||
|
src/items/mqttitem.h src/items/mqttitem.cpp
|
||||||
|
src/mqttclient.h src/mqttclient.cpp
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
# Add sources to executable
|
# Add sources to executable
|
||||||
target_sources(SHinterface
|
target_sources(SHinterface
|
||||||
|
|
@ -139,6 +143,8 @@ target_sources(SHinterface
|
||||||
src/ui/itemsettingswidgets/relayitemsettingswidget.cpp
|
src/ui/itemsettingswidgets/relayitemsettingswidget.cpp
|
||||||
src/ui/itemsettingswidgets/systemitemsettingswidget.h
|
src/ui/itemsettingswidgets/systemitemsettingswidget.h
|
||||||
src/ui/itemsettingswidgets/systemitemsettingswidget.cpp
|
src/ui/itemsettingswidgets/systemitemsettingswidget.cpp
|
||||||
|
src/ui/itemsettingswidgets/mqttitemsettingswidget.h
|
||||||
|
src/ui/itemsettingswidgets/mqttitemsettingswidget.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add UI files
|
# Add UI files
|
||||||
|
|
@ -159,6 +165,7 @@ target_sources(SHinterface
|
||||||
src/ui/itemsettingswidgets/messageitemsettingswidget.ui
|
src/ui/itemsettingswidgets/messageitemsettingswidget.ui
|
||||||
src/ui/itemsettingswidgets/relayitemsettingswidget.ui
|
src/ui/itemsettingswidgets/relayitemsettingswidget.ui
|
||||||
src/ui/itemsettingswidgets/systemitemsettingswidget.ui
|
src/ui/itemsettingswidgets/systemitemsettingswidget.ui
|
||||||
|
src/ui/itemsettingswidgets/mqttitemsettingswidget.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add resource file
|
# Add resource file
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "auxitem.h"
|
#include "auxitem.h"
|
||||||
#include "poweritem.h"
|
#include "poweritem.h"
|
||||||
#include "rgbitem.h"
|
#include "rgbitem.h"
|
||||||
|
#include "mqttitem.h"
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
|
@ -338,6 +339,8 @@ std::shared_ptr<Item> Item::loadItem(const QJsonObject& json)
|
||||||
newItem = std::shared_ptr<PowerItem>(new PowerItem);
|
newItem = std::shared_ptr<PowerItem>(new PowerItem);
|
||||||
else if(json["Type"].toString("") == "Rgb")
|
else if(json["Type"].toString("") == "Rgb")
|
||||||
newItem = std::shared_ptr<RgbItem>(new RgbItem);
|
newItem = std::shared_ptr<RgbItem>(new RgbItem);
|
||||||
|
else if(json["Type"].toString("") == "Mqtt")
|
||||||
|
newItem = std::shared_ptr<MqttItem>(new MqttItem);
|
||||||
else
|
else
|
||||||
qWarning()<<"Unable to load unkown item type: "<<json["Type"].toString();
|
qWarning()<<"Unable to load unkown item type: "<<json["Type"].toString();
|
||||||
if(newItem)
|
if(newItem)
|
||||||
|
|
|
||||||
164
src/items/mqttitem.cpp
Normal file
164
src/items/mqttitem.cpp
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
#include "mqttitem.h"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QtMqtt/QMqttClient>
|
||||||
|
|
||||||
|
#include "mqttclient.h"
|
||||||
|
|
||||||
|
MqttItem::MqttItem(QString name, uint8_t value, QObject *parent)
|
||||||
|
: Item(0, name, value, parent),
|
||||||
|
topic_(""),
|
||||||
|
valueKey_("state"),
|
||||||
|
valueOn_("ON"),
|
||||||
|
valueOff_("OFF")
|
||||||
|
{
|
||||||
|
hashId();
|
||||||
|
std::shared_ptr<MqttClient> workClient = client.lock();
|
||||||
|
assert(workClient);
|
||||||
|
|
||||||
|
connect(workClient->getClient().get(), &QMqttClient::stateChanged, this, &MqttItem::onClientStateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttItem::~MqttItem()
|
||||||
|
{
|
||||||
|
qDebug()<<__func__;
|
||||||
|
std::shared_ptr<MqttClient> workClient = client.lock();
|
||||||
|
if(!workClient || topic_.isEmpty() || !subscription)
|
||||||
|
return;
|
||||||
|
|
||||||
|
workClient->unsubscribe(subscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::onClientStateChanged(QMqttClient::ClientState state)
|
||||||
|
{
|
||||||
|
if(state == QMqttClient::Connected)
|
||||||
|
refreshSubscription();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::refreshSubscription()
|
||||||
|
{
|
||||||
|
std::shared_ptr<MqttClient> workClient = client.lock();
|
||||||
|
if(!workClient || topic_.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(workClient->getClient()->state() != QMqttClient::Connected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(subscription)
|
||||||
|
{
|
||||||
|
disconnect(subscription->subscription, &QMqttSubscription::messageReceived, this, &MqttItem::onMessageReceived);
|
||||||
|
workClient->unsubscribe(subscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
subscription = workClient->subscribe(workClient->getBaseTopic() + "/" + getTopic());
|
||||||
|
connect(subscription->subscription, &QMqttSubscription::messageReceived, this, &MqttItem::onMessageReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::onMessageReceived(const QMqttMessage& message)
|
||||||
|
{
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(message.payload());
|
||||||
|
if(doc.isObject())
|
||||||
|
{
|
||||||
|
QJsonObject obj = doc.object();
|
||||||
|
if(obj.contains(getValueKey()))
|
||||||
|
{
|
||||||
|
QString value = obj[getValueKey()].toString();
|
||||||
|
ItemUpdateRequest req = createValueUpdateRequest(ITEM_UPDATE_BACKEND);
|
||||||
|
req.changes.value = true;
|
||||||
|
if(value == getValueOn())
|
||||||
|
req.payload.setValueData(true);
|
||||||
|
else
|
||||||
|
req.payload.setValueData(false);
|
||||||
|
requestUpdate(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::hashId()
|
||||||
|
{
|
||||||
|
QString hashString = topic_ + "/" + valueKey_;
|
||||||
|
itemId_ = qHash(hashString.toLatin1());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::setTopic(const QString& topic)
|
||||||
|
{
|
||||||
|
topic_ = topic;
|
||||||
|
hashId();
|
||||||
|
refreshSubscription();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::setValueKey(const QString& valueKey)
|
||||||
|
{
|
||||||
|
valueKey_ = valueKey;
|
||||||
|
hashId();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::setValueOn(const QString& valueOn)
|
||||||
|
{
|
||||||
|
valueOn_ = valueOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::setValueOff(const QString& valueOff)
|
||||||
|
{
|
||||||
|
valueOff_ = valueOff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttItem::getTopic() const
|
||||||
|
{
|
||||||
|
return topic_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttItem::getValueKey() const
|
||||||
|
{
|
||||||
|
return valueKey_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttItem::getValueOn() const
|
||||||
|
{
|
||||||
|
return valueOn_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttItem::getValueOff() const
|
||||||
|
{
|
||||||
|
return valueOff_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::store(QJsonObject& json)
|
||||||
|
{
|
||||||
|
Item::store(json);
|
||||||
|
json["Type"] = "Mqtt";
|
||||||
|
json["Topic"] = topic_;
|
||||||
|
json["ValueKey"] = valueKey_;
|
||||||
|
json["ValueOn"] = valueOn_;
|
||||||
|
json["ValueOff"] = valueOff_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::load(const QJsonObject& json, const bool preserve)
|
||||||
|
{
|
||||||
|
Item::load(json, preserve);
|
||||||
|
topic_ = json["Topic"].toString();
|
||||||
|
valueKey_ = json["ValueKey"].toString("state");
|
||||||
|
valueOn_ = json["ValueOn"].toString("ON");
|
||||||
|
valueOff_ = json["ValueOff"].toString("OFF");
|
||||||
|
hashId();
|
||||||
|
refreshSubscription();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItem::enactValue(uint8_t value)
|
||||||
|
{
|
||||||
|
std::shared_ptr<MqttClient> workClient = client.lock();
|
||||||
|
if(!workClient || topic_.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString fullTopic = workClient->getBaseTopic() + "/" + topic_ + "/set";
|
||||||
|
QJsonObject payload;
|
||||||
|
|
||||||
|
payload[valueKey_] = value ? valueOn_ : valueOff_;
|
||||||
|
|
||||||
|
QJsonDocument doc(payload);
|
||||||
|
QByteArray data = doc.toJson(QJsonDocument::Compact);
|
||||||
|
|
||||||
|
qDebug() << "MqttItem publishing to" << fullTopic << ":" << data;
|
||||||
|
workClient->getClient()->publish(fullTopic, data);
|
||||||
|
}
|
||||||
53
src/items/mqttitem.h
Normal file
53
src/items/mqttitem.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef MQTTITEM_H
|
||||||
|
#define MQTTITEM_H
|
||||||
|
|
||||||
|
#include "item.h"
|
||||||
|
#include "mqttclient.h"
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
class MqttItem : public Item
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
inline static std::weak_ptr<MqttClient> client;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString topic_;
|
||||||
|
QString valueKey_;
|
||||||
|
QString valueOn_;
|
||||||
|
QString valueOff_;
|
||||||
|
|
||||||
|
MqttClient::Subscription* subscription = nullptr;
|
||||||
|
|
||||||
|
void hashId();
|
||||||
|
void refreshSubscription();
|
||||||
|
void onMessageReceived(const QMqttMessage& message);
|
||||||
|
void onClientStateChanged(QMqttClient::ClientState state);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MqttItem(QString name = "MqttItem",
|
||||||
|
uint8_t value = 0,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
virtual ~MqttItem() override;
|
||||||
|
|
||||||
|
void setTopic(const QString& topic);
|
||||||
|
void setValueKey(const QString& valueKey);
|
||||||
|
void setBaseTopic(const QString& baseTopic);
|
||||||
|
void setValueOn(const QString& valueOn);
|
||||||
|
void setValueOff(const QString& valueOff);
|
||||||
|
|
||||||
|
QString getTopic() const;
|
||||||
|
QString getValueKey() const;
|
||||||
|
QString getBaseTopic() const;
|
||||||
|
QString getValueOn() const;
|
||||||
|
QString getValueOff() const;
|
||||||
|
|
||||||
|
virtual void store(QJsonObject& json) override;
|
||||||
|
virtual void load(const QJsonObject& json, const bool preserve = false) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void enactValue(uint8_t value) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MQTTITEM_H
|
||||||
205
src/items/mqttitemsource.cpp
Normal file
205
src/items/mqttitemsource.cpp
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
#include "mqttitemsource.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
MqttItemSource::MqttItemSource(QObject *parent)
|
||||||
|
: ItemSource(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttItemSource::~MqttItemSource()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSource::start(const QJsonObject& settings)
|
||||||
|
{
|
||||||
|
baseTopicName_ = settings["BaseTopic"].toString("zigbee2mqtt");
|
||||||
|
|
||||||
|
connect(&client_, &QMqttClient::stateChanged, this, &MqttItemSource::onClientStateChanged);
|
||||||
|
connect(&client_, &QMqttClient::errorChanged, this, &MqttItemSource::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 itemsArray = settings["Items"].toArray();
|
||||||
|
|
||||||
|
for(QJsonValueRef itemRef : itemsArray)
|
||||||
|
{
|
||||||
|
QJsonObject itemObject = itemRef.toObject();
|
||||||
|
if(!itemObject.contains("Topic"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MqttItemConfig config;
|
||||||
|
config.topic = itemObject["Topic"].toString();
|
||||||
|
config.name = itemObject.contains("Name") ? itemObject["Name"].toString() : config.topic;
|
||||||
|
config.valueKey = itemObject["ValueKey"].toString("state");
|
||||||
|
config.valueOn = itemObject["ValueOn"].toString("ON");
|
||||||
|
config.valueOff = itemObject["ValueOff"].toString("OFF");
|
||||||
|
|
||||||
|
// Determine value type
|
||||||
|
QString valueTypeStr = itemObject["ValueType"].toString("bool");
|
||||||
|
if(valueTypeStr == "uint")
|
||||||
|
config.valueType = ITEM_VALUE_UINT;
|
||||||
|
else
|
||||||
|
config.valueType = ITEM_VALUE_BOOL;
|
||||||
|
|
||||||
|
config.id = qHash(baseTopicName_ + "/" + config.topic);
|
||||||
|
|
||||||
|
// Create the item
|
||||||
|
config.item = std::make_shared<MqttItem>(config.id, config.name, 0);
|
||||||
|
config.item->setTopic(config.topic);
|
||||||
|
config.item->setValueKey(config.valueKey);
|
||||||
|
config.item->setBaseTopic(baseTopicName_);
|
||||||
|
config.item->setValueOn(config.valueOn);
|
||||||
|
config.item->setValueOff(config.valueOff);
|
||||||
|
config.item->setMqttClient(&client_);
|
||||||
|
|
||||||
|
items_.push_back(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttItemSource::MqttItemConfig* MqttItemSource::findConfig(const QString& topic)
|
||||||
|
{
|
||||||
|
for(MqttItemConfig& config : items_)
|
||||||
|
{
|
||||||
|
if(baseTopicName_ + "/" + config.topic == topic)
|
||||||
|
return &config;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSource::onClientError(QMqttClient::ClientError error)
|
||||||
|
{
|
||||||
|
qWarning() << "MqttItemSource Client error:" << error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSource::onClientStateChanged(QMqttClient::ClientState state)
|
||||||
|
{
|
||||||
|
if(state == QMqttClient::ClientState::Connected)
|
||||||
|
{
|
||||||
|
qInfo() << "MqttItemSource connected to MQTT broker at " << client_.hostname() << client_.port();
|
||||||
|
for(MqttItemConfig& config : items_)
|
||||||
|
{
|
||||||
|
// Subscribe to state topic to receive updates from devices
|
||||||
|
QString stateTopic = baseTopicName_ + "/" + config.topic;
|
||||||
|
qDebug() << "MqttItemSource subscribing to" << stateTopic;
|
||||||
|
QMqttSubscription* subscription = client_.subscribe(stateTopic);
|
||||||
|
if(subscription)
|
||||||
|
{
|
||||||
|
connect(subscription, &QMqttSubscription::messageReceived, this, &MqttItemSource::onMessageReceived);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(state == QMqttClient::ClientState::Disconnected)
|
||||||
|
{
|
||||||
|
qWarning() << "MqttItemSource lost connection to MQTT broker";
|
||||||
|
}
|
||||||
|
else if(state == QMqttClient::ClientState::Connecting)
|
||||||
|
{
|
||||||
|
qInfo() << "MqttItemSource connecting to MQTT broker at " << client_.hostname() << client_.port();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSource::onMessageReceived(const QMqttMessage& message)
|
||||||
|
{
|
||||||
|
MqttItemConfig* config = findConfig(message.topic().name());
|
||||||
|
if(!config)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(message.payload());
|
||||||
|
if(doc.isObject())
|
||||||
|
{
|
||||||
|
QJsonObject obj = doc.object();
|
||||||
|
QString valueKey = config->valueKey;
|
||||||
|
|
||||||
|
if(obj.contains(valueKey))
|
||||||
|
{
|
||||||
|
uint8_t newValue = 0;
|
||||||
|
|
||||||
|
// Handle different value types
|
||||||
|
if(config->valueType == ITEM_VALUE_UINT)
|
||||||
|
{
|
||||||
|
// Numeric value (brightness, etc.)
|
||||||
|
newValue = obj[valueKey].toInt(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Binary value (state on/off, etc.)
|
||||||
|
QString value = obj[valueKey].toString();
|
||||||
|
if(value == config->valueOn || value == "ON" || value == "true")
|
||||||
|
newValue = 1;
|
||||||
|
else if(value == config->valueOff || value == "OFF" || value == "false")
|
||||||
|
newValue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only update if value changed
|
||||||
|
if(config->item->getValue() != newValue)
|
||||||
|
{
|
||||||
|
ItemUpdateRequest update;
|
||||||
|
update.type = ITEM_UPDATE_BACKEND;
|
||||||
|
update.payload = *config->item;
|
||||||
|
update.payload.setValueData(newValue);
|
||||||
|
update.changes.value = true;
|
||||||
|
config->item->requestUpdate(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSource::refresh()
|
||||||
|
{
|
||||||
|
std::vector<ItemAddRequest> requests;
|
||||||
|
|
||||||
|
for(MqttItemConfig& config : items_)
|
||||||
|
{
|
||||||
|
ItemAddRequest request;
|
||||||
|
request.type = ITEM_UPDATE_BACKEND;
|
||||||
|
request.payload = config.item;
|
||||||
|
request.changes = ItemFieldChanges(true);
|
||||||
|
requests.push_back(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
gotItems(requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSource::store(QJsonObject& json)
|
||||||
|
{
|
||||||
|
json["Host"] = client_.hostname();
|
||||||
|
json["Port"] = client_.port();
|
||||||
|
json["BaseTopic"] = baseTopicName_;
|
||||||
|
if(client_.username() != "")
|
||||||
|
json["User"] = client_.username();
|
||||||
|
if(client_.password() != "")
|
||||||
|
json["Password"] = client_.password();
|
||||||
|
|
||||||
|
QJsonArray itemsArray;
|
||||||
|
for(const MqttItemConfig& config : items_)
|
||||||
|
{
|
||||||
|
QJsonObject itemObject;
|
||||||
|
itemObject["Name"] = config.name;
|
||||||
|
itemObject["Topic"] = config.topic;
|
||||||
|
itemObject["ValueKey"] = config.valueKey;
|
||||||
|
itemObject["ValueOn"] = config.valueOn;
|
||||||
|
itemObject["ValueOff"] = config.valueOff;
|
||||||
|
if(config.valueType == ITEM_VALUE_UINT)
|
||||||
|
itemObject["ValueType"] = "uint";
|
||||||
|
else
|
||||||
|
itemObject["ValueType"] = "bool";
|
||||||
|
itemsArray.append(itemObject);
|
||||||
|
}
|
||||||
|
json["Items"] = itemsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMqttClient* MqttItemSource::getClient()
|
||||||
|
{
|
||||||
|
return &client_;
|
||||||
|
}
|
||||||
34
src/items/mqttitemsource.h
Normal file
34
src/items/mqttitemsource.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef MQTTITEMSOURCE_H
|
||||||
|
#define MQTTITEMSOURCE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QtMqtt/QMqttClient>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "itemsource.h"
|
||||||
|
#include "mqttitem.h"
|
||||||
|
|
||||||
|
class MqttItemSource : public ItemSource
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
QString baseTopicName_;
|
||||||
|
QMqttClient client_;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onClientStateChanged(QMqttClient::ClientState state);
|
||||||
|
void onMessageReceived(const QMqttMessage& message);
|
||||||
|
void onClientError(QMqttClient::ClientError error);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MqttItemSource(QObject *parent = nullptr);
|
||||||
|
virtual ~MqttItemSource() override;
|
||||||
|
virtual void refresh() override;
|
||||||
|
void start(const QJsonObject& settings);
|
||||||
|
void store(QJsonObject& json);
|
||||||
|
QMqttClient* getClient();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MQTTITEMSOURCE_H
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
#include<QJsonArray>
|
#include<QJsonArray>
|
||||||
#include<QMessageBox>
|
#include<QMessageBox>
|
||||||
|
|
||||||
|
#include "mqttclient.h"
|
||||||
|
#include "items/mqttitem.h"
|
||||||
#include "items/itemstore.h"
|
#include "items/itemstore.h"
|
||||||
|
|
||||||
MainObject::MainObject(QObject *parent) :
|
MainObject::MainObject(QObject *parent) :
|
||||||
|
|
@ -74,9 +76,12 @@ PrimaryMainObject::PrimaryMainObject(QIODevice* microDevice, const QString& sett
|
||||||
micro(microDevice),
|
micro(microDevice),
|
||||||
tcpServer(new TcpServer(this)),
|
tcpServer(new TcpServer(this)),
|
||||||
webServer(new WebSocketServer("shinterface", this)),
|
webServer(new WebSocketServer("shinterface", this)),
|
||||||
|
mqttClient(new MqttClient),
|
||||||
sunSensorSource(49.824972, 8.702194),
|
sunSensorSource(49.824972, 8.702194),
|
||||||
fixedItems(µ)
|
fixedItems(µ)
|
||||||
{
|
{
|
||||||
|
MqttItem::client = mqttClient;
|
||||||
|
|
||||||
//connect sensors subsystem
|
//connect sensors subsystem
|
||||||
connect(&globalSensors, &SensorStore::sensorChangedState, tcpServer, &TcpServer::sensorEvent);
|
connect(&globalSensors, &SensorStore::sensorChangedState, tcpServer, &TcpServer::sensorEvent);
|
||||||
connect(tcpServer, &TcpServer::gotSensor, &globalSensors, &SensorStore::sensorGotState);
|
connect(tcpServer, &TcpServer::gotSensor, &globalSensors, &SensorStore::sensorGotState);
|
||||||
|
|
@ -98,7 +103,8 @@ PrimaryMainObject::PrimaryMainObject(QIODevice* microDevice, const QString& sett
|
||||||
loadFromDisk(settingsPath);
|
loadFromDisk(settingsPath);
|
||||||
|
|
||||||
QJsonObject mqttJson = settings["Mqtt"].toObject();
|
QJsonObject mqttJson = settings["Mqtt"].toObject();
|
||||||
mqttSensorSource.start(mqttJson);
|
mqttClient->start(mqttJson);
|
||||||
|
mqttSensorSource.start(mqttClient, mqttJson);
|
||||||
|
|
||||||
tcpServer->launch(QHostAddress(host), port);
|
tcpServer->launch(QHostAddress(host), port);
|
||||||
webServer->launch(QHostAddress(host), port+1);
|
webServer->launch(QHostAddress(host), port+1);
|
||||||
|
|
@ -115,6 +121,7 @@ void PrimaryMainObject::store(QJsonObject &json)
|
||||||
{
|
{
|
||||||
globalItems.store(json);
|
globalItems.store(json);
|
||||||
QJsonObject mqttJson = json["Mqtt"].toObject();
|
QJsonObject mqttJson = json["Mqtt"].toObject();
|
||||||
|
mqttClient->store(mqttJson);
|
||||||
mqttSensorSource.store(mqttJson);
|
mqttSensorSource.store(mqttJson);
|
||||||
json["Mqtt"] = mqttJson;
|
json["Mqtt"] = mqttJson;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ public:
|
||||||
Microcontroller micro;
|
Microcontroller micro;
|
||||||
TcpServer* tcpServer;
|
TcpServer* tcpServer;
|
||||||
WebSocketServer* webServer;
|
WebSocketServer* webServer;
|
||||||
|
std::shared_ptr<MqttClient> mqttClient;
|
||||||
|
|
||||||
//sensors
|
//sensors
|
||||||
SunSensorSource sunSensorSource;
|
SunSensorSource sunSensorSource;
|
||||||
|
|
|
||||||
110
src/mqttclient.cpp
Normal file
110
src/mqttclient.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
#include "mqttclient.h"
|
||||||
|
|
||||||
|
MqttClient::MqttClient():
|
||||||
|
client(new QMqttClient)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::start(const QJsonObject& settings)
|
||||||
|
{
|
||||||
|
baseTopicName = settings["BaseTopic"].toString("zigbee2mqtt");
|
||||||
|
|
||||||
|
QMqttClient* cl = client.get();
|
||||||
|
connect(cl, &QMqttClient::stateChanged, this, &MqttClient::onClientStateChanged);
|
||||||
|
connect(cl, &QMqttClient::errorChanged, this, &MqttClient::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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::onClientError(QMqttClient::ClientError error)
|
||||||
|
{
|
||||||
|
qWarning()<<"MQTT Client error:"<<error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::onClientStateChanged(QMqttClient::ClientState state)
|
||||||
|
{
|
||||||
|
if(state == QMqttClient::ClientState::Connected)
|
||||||
|
qInfo()<<"Connected to MQTT broker at "<<client->hostname()<<client->port();
|
||||||
|
else if (state == QMqttClient::ClientState::Disconnected)
|
||||||
|
qWarning()<<"Lost connection to MQTT broker";
|
||||||
|
else if(state == QMqttClient::ClientState::Connecting)
|
||||||
|
qInfo()<<"Connecting to MQTT broker at "<<client->hostname()<<client->port();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::store(QJsonObject& json)
|
||||||
|
{
|
||||||
|
json["Host"] = client->hostname();
|
||||||
|
json["Port"] = client->port();
|
||||||
|
json["BaseTopic"] = baseTopicName;
|
||||||
|
if(client->username() != "")
|
||||||
|
json["User"] = client->username();
|
||||||
|
if(client->password() != "")
|
||||||
|
json["Password"] = client->password();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<QMqttClient> MqttClient::getClient()
|
||||||
|
{
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttClient::Subscription* MqttClient::subscribe(QString topic)
|
||||||
|
{
|
||||||
|
if(subscriptions.contains(topic))
|
||||||
|
{
|
||||||
|
MqttClient::Subscription* sub = subscriptions[topic];
|
||||||
|
++sub->ref;
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug()<<"MqttClient: subscibeing to"<<topic;
|
||||||
|
MqttClient::Subscription* sub = new Subscription;
|
||||||
|
sub->subscription = client->subscribe(topic);
|
||||||
|
sub->ref = 1;
|
||||||
|
subscriptions.insert({topic, sub});
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::unsubscribe(MqttClient::Subscription* subscription)
|
||||||
|
{
|
||||||
|
QString topic = subscription->subscription->topic().filter();
|
||||||
|
unsubscribe(topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttClient::unsubscribe(QString topic)
|
||||||
|
{
|
||||||
|
assert(!subscriptions.contains(topic));
|
||||||
|
MqttClient::Subscription* sub = subscriptions[topic];
|
||||||
|
|
||||||
|
if(--sub->ref > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug()<<"MqttClient: unsubscibeing"<<sub->subscription->topic();
|
||||||
|
client->unsubscribe(sub->subscription->topic());
|
||||||
|
subscriptions.erase(topic);
|
||||||
|
delete sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttClient::getBaseTopic()
|
||||||
|
{
|
||||||
|
return baseTopicName;
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttClient::~MqttClient()
|
||||||
|
{
|
||||||
|
for(const std::pair<QString, Subscription*> sub : subscriptions)
|
||||||
|
{
|
||||||
|
qWarning()<<sub.first<<"not unregistered at exit!";
|
||||||
|
client->unsubscribe(sub.second->subscription->topic());
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/mqttclient.h
Normal file
40
src/mqttclient.h
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef MQTTCLIENT_H
|
||||||
|
#define MQTTCLIENT_H
|
||||||
|
|
||||||
|
#include <QMqttClient>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
class MqttClient: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
struct Subscription
|
||||||
|
{
|
||||||
|
int ref;
|
||||||
|
QMqttSubscription* subscription;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString baseTopicName;
|
||||||
|
std::shared_ptr<QMqttClient> client;
|
||||||
|
std::map<QString, Subscription*> subscriptions;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onClientStateChanged(QMqttClient::ClientState state);
|
||||||
|
void onClientError(QMqttClient::ClientError error);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MqttClient();
|
||||||
|
~MqttClient();
|
||||||
|
void start(const QJsonObject& settings);
|
||||||
|
void store(QJsonObject& json);
|
||||||
|
std::shared_ptr<QMqttClient> getClient();
|
||||||
|
Subscription* subscribe(QString topic);
|
||||||
|
void unsubscribe(Subscription* subscription);
|
||||||
|
void unsubscribe(QString topic);
|
||||||
|
QString getBaseTopic();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MQTTCLIENT_H
|
||||||
|
|
@ -7,22 +7,11 @@ MqttSensorSource::MqttSensorSource(QObject *parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSensorSource::start(const QJsonObject& settings)
|
void MqttSensorSource::start(std::shared_ptr<MqttClient> client, const QJsonObject& settings)
|
||||||
{
|
{
|
||||||
baseTopicName = settings["BaseTopic"].toString("zigbee2mqtt");
|
this->client = client;
|
||||||
|
|
||||||
connect(&client, &QMqttClient::stateChanged, this, &MqttSensorSource::onClientStateChanged);
|
connect(client->getClient().get(), &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();
|
QJsonArray sensorsArray = settings["Sensors"].toArray();
|
||||||
|
|
||||||
|
|
@ -37,21 +26,16 @@ void MqttSensorSource::start(const QJsonObject& settings)
|
||||||
sensor.name = sensor.topic;
|
sensor.name = sensor.topic;
|
||||||
else
|
else
|
||||||
sensor.name = sensorObject["Name"].toString();
|
sensor.name = sensorObject["Name"].toString();
|
||||||
sensor.id = qHash(baseTopicName + "/" + sensor.topic);
|
sensor.id = qHash(client->getBaseTopic() + "/" + sensor.topic);
|
||||||
sensors.push_back(sensor);
|
sensors.push_back(sensor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSensorSource::onClientError(QMqttClient::ClientError error)
|
|
||||||
{
|
|
||||||
qWarning()<<"MQTT Client error:"<<error;
|
|
||||||
}
|
|
||||||
|
|
||||||
MqttSensorSource::SensorSubscription& MqttSensorSource::findSubscription(const QString& topic)
|
MqttSensorSource::SensorSubscription& MqttSensorSource::findSubscription(const QString& topic)
|
||||||
{
|
{
|
||||||
for(SensorSubscription& sensor : sensors)
|
for(SensorSubscription& sensor : sensors)
|
||||||
{
|
{
|
||||||
if(baseTopicName + "/" + sensor.topic == topic)
|
if(client->getBaseTopic() + "/" + sensor.topic == topic)
|
||||||
return sensor;
|
return sensor;
|
||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
|
|
@ -61,30 +45,24 @@ void MqttSensorSource::onClientStateChanged(QMqttClient::ClientState state)
|
||||||
{
|
{
|
||||||
if(state == QMqttClient::ClientState::Connected)
|
if(state == QMqttClient::ClientState::Connected)
|
||||||
{
|
{
|
||||||
qInfo()<<"Connected to MQTT broker at "<<client.hostname()<<client.port();
|
|
||||||
for(SensorSubscription& sensor : sensors)
|
for(SensorSubscription& sensor : sensors)
|
||||||
{
|
{
|
||||||
qDebug()<<"MQTT subscribeing to"<<baseTopicName + "/" + sensor.topic;
|
qDebug()<<"MQTT subscribeing to"<<client->getBaseTopic() + "/" + sensor.topic;
|
||||||
sensor.subscription = client.subscribe(baseTopicName + "/" + sensor.topic);
|
sensor.subscription = client->subscribe(client->getBaseTopic() + "/" + sensor.topic);
|
||||||
connect(sensor.subscription, &QMqttSubscription::messageReceived, this, &MqttSensorSource::onMessageReceived);
|
connect(sensor.subscription->subscription, &QMqttSubscription::messageReceived, this, &MqttSensorSource::onMessageReceived);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (state == QMqttClient::ClientState::Disconnected)
|
else if (state == QMqttClient::ClientState::Disconnected)
|
||||||
{
|
{
|
||||||
qWarning()<<"Lost connection to MQTT broker";
|
|
||||||
for(SensorSubscription& sensor : sensors)
|
for(SensorSubscription& sensor : sensors)
|
||||||
{
|
{
|
||||||
if(sensor.subscription)
|
if(sensor.subscription)
|
||||||
{
|
{
|
||||||
client.unsubscribe(sensor.topic);
|
client->unsubscribe(sensor.topic);
|
||||||
sensor.subscription = nullptr;
|
sensor.subscription = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(state == QMqttClient::ClientState::Connecting)
|
|
||||||
{
|
|
||||||
qInfo()<<"Connecting to MQTT broker at "<<client.hostname()<<client.port();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSensorSource::onMessageReceived(const QMqttMessage& message)
|
void MqttSensorSource::onMessageReceived(const QMqttMessage& message)
|
||||||
|
|
@ -107,6 +85,14 @@ void MqttSensorSource::onMessageReceived(const QMqttMessage& message)
|
||||||
stateChanged(sensor);
|
stateChanged(sensor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(obj.contains("local_temperature"))
|
||||||
|
{
|
||||||
|
sensor.name = baseName + " Temperature";
|
||||||
|
sensor.type = Sensor::TYPE_TEMPERATURE;
|
||||||
|
sensor.field = obj["local_temperature"].toDouble(0);
|
||||||
|
stateChanged(sensor);
|
||||||
|
}
|
||||||
|
|
||||||
if(obj.contains("humidity"))
|
if(obj.contains("humidity"))
|
||||||
{
|
{
|
||||||
sensor.name = baseName + " Humidity";
|
sensor.name = baseName + " Humidity";
|
||||||
|
|
@ -167,13 +153,6 @@ void MqttSensorSource::onMessageReceived(const QMqttMessage& message)
|
||||||
|
|
||||||
void MqttSensorSource::store(QJsonObject& json)
|
void MqttSensorSource::store(QJsonObject& json)
|
||||||
{
|
{
|
||||||
json["Host"] = client.hostname();
|
|
||||||
json["Port"] = client.port();
|
|
||||||
json["BaseTopic"] = baseTopicName;
|
|
||||||
if(client.username() != "")
|
|
||||||
json["User"] = client.username();
|
|
||||||
if(client.password() != "")
|
|
||||||
json["Password"] = client.password();
|
|
||||||
QJsonArray sensorsArray;
|
QJsonArray sensorsArray;
|
||||||
for(const SensorSubscription& sensor : sensors)
|
for(const SensorSubscription& sensor : sensors)
|
||||||
{
|
{
|
||||||
|
|
@ -185,3 +164,9 @@ void MqttSensorSource::store(QJsonObject& json)
|
||||||
json["Sensors"] = sensorsArray;
|
json["Sensors"] = sensorsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MqttSensorSource::~MqttSensorSource()
|
||||||
|
{
|
||||||
|
for(SensorSubscription& sub : sensors)
|
||||||
|
client->unsubscribe(sub.topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "sensor.h"
|
#include "sensor.h"
|
||||||
|
#include "mqttclient.h"
|
||||||
|
|
||||||
class MqttSensorSource : public QObject
|
class MqttSensorSource : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -17,12 +18,11 @@ class MqttSensorSource : public QObject
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
QString topic;
|
QString topic;
|
||||||
QString name;
|
QString name;
|
||||||
QMqttSubscription* subscription = nullptr;
|
MqttClient::Subscription* subscription = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString baseTopicName;
|
|
||||||
std::vector<SensorSubscription> sensors;
|
std::vector<SensorSubscription> sensors;
|
||||||
QMqttClient client;
|
std::shared_ptr<MqttClient> client;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SensorSubscription& findSubscription(const QString& topic);
|
SensorSubscription& findSubscription(const QString& topic);
|
||||||
|
|
@ -30,11 +30,11 @@ private:
|
||||||
private slots:
|
private slots:
|
||||||
void onClientStateChanged(QMqttClient::ClientState state);
|
void onClientStateChanged(QMqttClient::ClientState state);
|
||||||
void onMessageReceived(const QMqttMessage& message);
|
void onMessageReceived(const QMqttMessage& message);
|
||||||
void onClientError(QMqttClient::ClientError error);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MqttSensorSource(QObject *parent = nullptr);
|
explicit MqttSensorSource(QObject *parent = nullptr);
|
||||||
void start(const QJsonObject& settings);
|
~MqttSensorSource();
|
||||||
|
void start(std::shared_ptr<MqttClient> client, const QJsonObject& settings);
|
||||||
void store(QJsonObject& json);
|
void store(QJsonObject& json);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "itemsettingswidgets/messageitemsettingswidget.h"
|
#include "itemsettingswidgets/messageitemsettingswidget.h"
|
||||||
#include "itemsettingswidgets/systemitemsettingswidget.h"
|
#include "itemsettingswidgets/systemitemsettingswidget.h"
|
||||||
|
#include "itemsettingswidgets/mqttitemsettingswidget.h"
|
||||||
|
#include "items/mqttitem.h"
|
||||||
|
|
||||||
ItemCreationDialog::ItemCreationDialog(QWidget *parent) :
|
ItemCreationDialog::ItemCreationDialog(QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
|
|
@ -41,6 +43,13 @@ void ItemCreationDialog::itemTypeChanged(const QString& type)
|
||||||
widget = new SystemItemSettingsWidget(systemItem, this);
|
widget = new SystemItemSettingsWidget(systemItem, this);
|
||||||
ui->verticalLayout->addWidget(widget);
|
ui->verticalLayout->addWidget(widget);
|
||||||
}
|
}
|
||||||
|
if(type == "Mqtt")
|
||||||
|
{
|
||||||
|
std::shared_ptr<MqttItem> mqttItem(new MqttItem);
|
||||||
|
item = mqttItem;
|
||||||
|
widget = new MqttItemSettingsWidget(mqttItem, this);
|
||||||
|
ui->verticalLayout->addWidget(widget);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemCreationDialog::itemNameChanged(const QString& name)
|
void ItemCreationDialog::itemNameChanged(const QString& name)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>400</width>
|
||||||
<height>140</height>
|
<height>274</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
|
@ -39,6 +39,11 @@
|
||||||
<string>System</string>
|
<string>System</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Mqtt</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
@ -64,13 +69,26 @@
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
#include "itemsettingswidgets/messageitemsettingswidget.h"
|
#include "itemsettingswidgets/messageitemsettingswidget.h"
|
||||||
#include "itemsettingswidgets/systemitemsettingswidget.h"
|
#include "itemsettingswidgets/systemitemsettingswidget.h"
|
||||||
#include "itemsettingswidgets/relayitemsettingswidget.h"
|
#include "itemsettingswidgets/relayitemsettingswidget.h"
|
||||||
|
#include "itemsettingswidgets/mqttitemsettingswidget.h"
|
||||||
|
#include "../items/mqttitem.h"
|
||||||
#include<memory>
|
#include<memory>
|
||||||
|
|
||||||
ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr<Item> item, bool noGroup, QWidget *parent) :
|
ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr<Item> item, bool noGroup, QWidget *parent) :
|
||||||
|
|
@ -47,6 +49,10 @@ ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr<Item> item, bool noGroup,
|
||||||
{
|
{
|
||||||
itemSpecificWidget_ = new SystemItemSettingsWidget(sysItem);
|
itemSpecificWidget_ = new SystemItemSettingsWidget(sysItem);
|
||||||
}
|
}
|
||||||
|
else if(std::shared_ptr<MqttItem> mqttItem = std::dynamic_pointer_cast<MqttItem>(item_))
|
||||||
|
{
|
||||||
|
itemSpecificWidget_ = new MqttItemSettingsWidget(mqttItem);
|
||||||
|
}
|
||||||
|
|
||||||
if(itemSpecificWidget_)
|
if(itemSpecificWidget_)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
62
src/ui/itemsettingswidgets/mqttitemsettingswidget.cpp
Normal file
62
src/ui/itemsettingswidgets/mqttitemsettingswidget.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include "mqttitemsettingswidget.h"
|
||||||
|
#include "ui_mqttitemsettingswidget.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
MqttItemSettingsWidget::MqttItemSettingsWidget(std::weak_ptr<MqttItem> item, QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
item_(item),
|
||||||
|
ui(new Ui::MqttItemSettingsWidget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
if(auto workingItem = item_.lock())
|
||||||
|
{
|
||||||
|
ui->lineEdit_topic->setText(workingItem->getTopic());
|
||||||
|
ui->lineEdit_valueKey->setText(workingItem->getValueKey());
|
||||||
|
ui->lineEdit_valueOn->setText(workingItem->getValueOn());
|
||||||
|
ui->lineEdit_valueOff->setText(workingItem->getValueOff());
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ui->lineEdit_topic, &QLineEdit::textChanged, this, &MqttItemSettingsWidget::setTopic);
|
||||||
|
connect(ui->lineEdit_valueKey, &QLineEdit::textChanged, this, &MqttItemSettingsWidget::setValueKey);
|
||||||
|
connect(ui->lineEdit_valueOn, &QLineEdit::textChanged, this, &MqttItemSettingsWidget::setValueOn);
|
||||||
|
connect(ui->lineEdit_valueOff, &QLineEdit::textChanged, this, &MqttItemSettingsWidget::setValueOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSettingsWidget::setTopic(const QString& topic)
|
||||||
|
{
|
||||||
|
if(auto workingItem = item_.lock())
|
||||||
|
{
|
||||||
|
workingItem->setTopic(topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSettingsWidget::setValueKey(const QString& valueKey)
|
||||||
|
{
|
||||||
|
if(auto workingItem = item_.lock())
|
||||||
|
{
|
||||||
|
workingItem->setValueKey(valueKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSettingsWidget::setValueOn(const QString& valueOn)
|
||||||
|
{
|
||||||
|
if(auto workingItem = item_.lock())
|
||||||
|
{
|
||||||
|
workingItem->setValueOn(valueOn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttItemSettingsWidget::setValueOff(const QString& valueOff)
|
||||||
|
{
|
||||||
|
if(auto workingItem = item_.lock())
|
||||||
|
{
|
||||||
|
workingItem->setValueOff(valueOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttItemSettingsWidget::~MqttItemSettingsWidget()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
32
src/ui/itemsettingswidgets/mqttitemsettingswidget.h
Normal file
32
src/ui/itemsettingswidgets/mqttitemsettingswidget.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef MQTTITEMSETTINGSWIDGET_H
|
||||||
|
#define MQTTITEMSETTINGSWIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <memory>
|
||||||
|
#include "../../items/mqttitem.h"
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class MqttItemSettingsWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MqttItemSettingsWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
std::weak_ptr<MqttItem> item_;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void setTopic(const QString& topic);
|
||||||
|
void setValueKey(const QString& valueKey);
|
||||||
|
void setValueOn(const QString& valueOn);
|
||||||
|
void setValueOff(const QString& valueOff);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MqttItemSettingsWidget(std::weak_ptr<MqttItem> item, QWidget *parent = nullptr);
|
||||||
|
~MqttItemSettingsWidget();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MqttItemSettingsWidget *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MQTTITEMSETTINGSWIDGET_H
|
||||||
108
src/ui/itemsettingswidgets/mqttitemsettingswidget.ui
Normal file
108
src/ui/itemsettingswidgets/mqttitemsettingswidget.ui
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MqttItemSettingsWidget</class>
|
||||||
|
<widget class="QWidget" name="MqttItemSettingsWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>216</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_topic">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_topic">
|
||||||
|
<property name="text">
|
||||||
|
<string>Topic:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit_topic">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>e.g., kitchen/light</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_valueKey">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_valueKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>Value Key:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit_valueKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>state</string>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>e.g., state, brightness</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_valueOn">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_valueOn">
|
||||||
|
<property name="text">
|
||||||
|
<string>Value On:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit_valueOn">
|
||||||
|
<property name="text">
|
||||||
|
<string>ON</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_valueOff">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_valueOff">
|
||||||
|
<property name="text">
|
||||||
|
<string>Value Off:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit_valueOff">
|
||||||
|
<property name="text">
|
||||||
|
<string>OFF</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
|
|
@ -48,6 +48,9 @@ set(COMMON_TEST_SOURCES
|
||||||
../src/items/itemstore.cpp
|
../src/items/itemstore.cpp
|
||||||
../src/items/itemloadersource.h
|
../src/items/itemloadersource.h
|
||||||
../src/items/itemloadersource.cpp
|
../src/items/itemloadersource.cpp
|
||||||
|
../src/items/mqttitem.h
|
||||||
|
../src/items/mqttitem.cpp
|
||||||
|
../src/mqttclient.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add test executables - compile all needed sources into each test
|
# Add test executables - compile all needed sources into each test
|
||||||
|
|
@ -73,6 +76,7 @@ target_link_libraries(test_item
|
||||||
Qt6::Gui
|
Qt6::Gui
|
||||||
Qt6::Widgets
|
Qt6::Widgets
|
||||||
Qt6::Multimedia
|
Qt6::Multimedia
|
||||||
|
Qt6::Mqtt
|
||||||
Qt6::Test
|
Qt6::Test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -88,6 +92,7 @@ target_link_libraries(test_sensor
|
||||||
Qt6::Gui
|
Qt6::Gui
|
||||||
Qt6::Widgets
|
Qt6::Widgets
|
||||||
Qt6::Multimedia
|
Qt6::Multimedia
|
||||||
|
Qt6::Mqtt
|
||||||
Qt6::Test
|
Qt6::Test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -103,6 +108,7 @@ target_link_libraries(test_actor
|
||||||
Qt6::Gui
|
Qt6::Gui
|
||||||
Qt6::Widgets
|
Qt6::Widgets
|
||||||
Qt6::Multimedia
|
Qt6::Multimedia
|
||||||
|
Qt6::Mqtt
|
||||||
Qt6::Test
|
Qt6::Test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -118,6 +124,7 @@ target_link_libraries(test_itemstore
|
||||||
Qt6::Gui
|
Qt6::Gui
|
||||||
Qt6::Widgets
|
Qt6::Widgets
|
||||||
Qt6::Multimedia
|
Qt6::Multimedia
|
||||||
|
Qt6::Mqtt
|
||||||
Qt6::Test
|
Qt6::Test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -133,6 +140,7 @@ target_link_libraries(test_itemloadersource
|
||||||
Qt6::Gui
|
Qt6::Gui
|
||||||
Qt6::Widgets
|
Qt6::Widgets
|
||||||
Qt6::Multimedia
|
Qt6::Multimedia
|
||||||
|
Qt6::Mqtt
|
||||||
Qt6::Test
|
Qt6::Test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -150,6 +158,7 @@ target_link_libraries(test_tcp
|
||||||
Qt6::Multimedia
|
Qt6::Multimedia
|
||||||
Qt6::Network
|
Qt6::Network
|
||||||
Qt6::WebSockets
|
Qt6::WebSockets
|
||||||
|
Qt6::Mqtt
|
||||||
Qt6::Test
|
Qt6::Test
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue