40#include "ESP8266WiFi.h"
55#include <PubSubClient.h>
63#include <sys/socket.h>
83 typedef std::function<void(
CBORObject &)> CBORExtender;
93 : mpCredentials(&credentials),
95 mInfoExtender(infoExtender),
96 mPubSubClient(mWiFiClient),
97 mNetworkConnected(false),
99 mMqttConnected(false) {
100 mPubSubClient.setCallback([
this](
char *topic, uint8_t *payload,
unsigned int length) {
109 if (_readCOSEMessage(
Bytes(payload, length), decoded)) {
110 device->
handle(topic, decoded);
140 mPubSubClient.setServer(domain, port);
151 if (mDevices.pushUnique(&device)) {
153 device.topics()->
forEach([
this](String topic) {
154 mPubSubClient.subscribe(topic.c_str());
167 if (mDevices.removeOne(&device)) {
169 device.topics()->
forEach([
this](String topic) {
170 mPubSubClient.unsubscribe(topic.c_str());
202 scheduler.
push(
"mqtt", mTaskMQTT);
203 scheduler.
push(
"ntp_retry", mTaskNtpRetry);
226 mNetworkConnected =
true;
229 if (!mTaskNtpRetry->isAttached()) {
230 mTaskNtpRetry->once(1);
240 mNetworkConnected =
false;
241 mMqttConnected.store(
false);
243 mTaskNtpRetry->detach();
251 mTaskNtpRetry->detach();
252 if (!mTaskMQTT->isAttached()) {
253 mTaskMQTT->attach(10);
261 mTaskNtpRetry->once(3000);
286 Bytes packetExtention;
289 mInfoExtender(packet);
290 packetExtention = packet.
build();
293 _prepareOfflinePacket(offlineCBOR);
294 auto offlinePacket = _buildCOSEMessage(offlineCBOR.
build());
295 mPubSubClient.publish(
296 mPath.buildDevicePath(
"status").c_str(),
298 offlinePacket.size(),
300 mPubSubClient.disconnect();
301 mMqttConnected.store(
false);
310 return &mPubSubClient;
317 inline void _initTasks() {
321 if (mNetworkConnected) {
328 if (!mNetworkConnected) {
332 if (!mPubSubClient.connected()) {
333 mMqttConnected.store(
false);
335 Bytes packetExtention;
338 mInfoExtender(packet);
339 packetExtention = packet.build();
342 CBORObject offlineCBOR(packetExtention);
343 _prepareOfflinePacket(offlineCBOR);
344 auto offlinePacket = _buildCOSEMessage(offlineCBOR.build());
345 auto password = _getUserPassword();
346 if (mPubSubClient.connect(
347 _getClientId().c_str(),
348 _getUserLogin().c_str(),
349 (
const char *)password.raw(),
351 mPath.buildDevicePath(
"status").c_str(),
354 (
const char *)offlinePacket.raw(),
355 offlinePacket.size(),
357 CBORObject onlineCBOR(packetExtention);
358 _prepareOnlinePacket(onlineCBOR);
359 auto onlinePacket = _buildCOSEMessage(onlineCBOR.build());
360 mPubSubClient.publish(
361 mPath.buildDevicePath(
"status").c_str(),
366 device->topics()->forEach([
this](String topic) {
367 mPubSubClient.subscribe(topic.c_str());
370 _applyTcpKeepalive();
371 mMqttConnected.store(
true);
374 mMqttConnected.store(
false);
378 mPubSubClient.loop();
389 void _applyTcpKeepalive() {
391 int fd = mWiFiClient.fd();
396 static constexpr int kEnable = 1;
397 static constexpr int kIdle = 10;
398 static constexpr int kIntvl = 5;
399 static constexpr int kCount = 3;
400 setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &kEnable,
sizeof(kEnable));
401 setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &kIdle,
sizeof(kIdle));
402 setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &kIntvl,
sizeof(kIntvl));
403 setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &kCount,
sizeof(kCount));
404 UNIOT_LOG_DEBUG(
"TCP keepalive applied, idle: %ds, interval: %ds, count: %d", kIdle, kIntvl, kCount);
414 Bytes _buildCOSEMessage(
const Bytes &payload,
bool sign =
false) {
416 obj.setPayload(payload);
417 auto kid = mpCredentials->keyId();
419 obj.sign(*mpCredentials);
420 obj.setUnprotectedKid(kid);
432 bool _readCOSEMessage(
const Bytes &message, Bytes &outPayload) {
433 COSEMessage obj(message);
434 if (obj.wasReadSuccessful()) {
435 outPayload = obj.getPayload();
448 void _prepareOnlinePacket(CBORObject &packet) {
451 .put(
"connection_id", mConnectionId++);
461 void _prepareOfflinePacket(CBORObject &packet) {
464 .put(
"connection_id", mConnectionId);
471 String _getClientId() {
472 return _getUserLogin();
479 String _getUserLogin() {
480 return "device:" + mpCredentials->getOwnerId() +
"@" + mpCredentials->getDeviceId();
491 Bytes _getUserPassword() {
493 claims.put(
"device", mpCredentials->getDeviceId().c_str());
494 claims.put(
"owner", mpCredentials->getOwnerId().c_str());
495 claims.put(
"creator", mpCredentials->getCreatorId().c_str());
496 claims.put(
"timestamp",
static_cast<int64_t
>(
Date::now()));
498 COSEMessage password;
499 auto kid = mpCredentials->keyId();
500 password.setUnprotectedKid(kid);
501 password.setPayload(claims.build());
502 password.sign(*mpCredentials);
503 return password.build();
506 const Credentials *mpCredentials;
509 CBORExtender mInfoExtender;
510 PubSubClient mPubSubClient;
512 bool mNetworkConnected;
514 std::atomic<bool> mMqttConnected;
516 WiFiClient mWiFiClient;
518 ClearQueue<MQTTDevice *> mDevices;
MQTT event definitions for the Uniot event system.
Network event definitions for the Uniot event system.
Complete WiFi network management and configuration system.
void forEach(VoidCallback callback) const
Executes a callback function on each element in the queue.
Definition ClearQueue.h:325
Definition CBORObject.h:40
Bytes build() const
Build the CBOR data into binary format.
Definition CBORObject.h:509
Manages device identity and cryptographic credentials for Uniot devices.
Definition Credentials.h:61
void forceSync()
Forces immediate NTP time synchronization.
Definition Date.h:154
static time_t now()
Returns the current Unix timestamp.
Definition Date.h:75
void emitEvent(unsigned int topic, int msg)
EventListener * stopListeningToEvent(unsigned int topic)
EventListener * listenToEvent(unsigned int topic)
Interface for connecting components to the TaskScheduler.
Definition ISchedulerConnectionKit.h:35
virtual void syncSubscriptions()=0
Reconstructs subscriptions after reconnection or credential changes.
bool isSubscribed(const String &topic)
Checks if the device is subscribed to a given topic.
Definition MQTTDevice.cpp:126
void unsubscribeFromAll()
Unsubscribes from all subscribed topics.
Definition MQTTDevice.cpp:55
virtual void handle(const String &topic, const Bytes &payload)=0
Handles incoming MQTT messages.
void forceDisconnect()
Publishes the retained offline status and closes the connection.
Definition MQTTKit.h:285
bool isMqttConnected() const
Returns true if the MQTT broker connection is currently established.
Definition MQTTKit.h:277
virtual void pushTo(TaskScheduler &scheduler) override
Registers MQTT tasks with the provided scheduler.
Definition MQTTKit.h:201
friend class MQTTDevice
Definition MQTTKit.h:84
~MQTTKit()
Destructor - cleans up event listeners.
Definition MQTTKit.h:128
void setServer(const char *domain, uint16_t port)
Sets the MQTT broker server address and port.
Definition MQTTKit.h:139
void renewSubscriptions()
Renews all device subscriptions.
Definition MQTTKit.h:189
const MQTTPath & getPath()
Gets the MQTT path helper object.
Definition MQTTKit.h:179
virtual void attach() override
Attaches this kit (empty implementation)
Definition MQTTKit.h:210
void addDevice(MQTTDevice &device)
Adds a device to be managed by this MQTT kit.
Definition MQTTKit.h:150
MQTTKit(const Credentials &credentials, CBORExtender infoExtender=nullptr)
Constructs an MQTTKit instance.
Definition MQTTKit.h:92
PubSubClient * client()
Gets access to the underlying PubSubClient.
Definition MQTTKit.h:309
virtual void onEventReceived(unsigned int topic, int msg) override
Handles network and time events.
Definition MQTTKit.h:222
void removeDevice(MQTTDevice &device)
Removes a device from this MQTT kit.
Definition MQTTKit.h:166
Definition TaskScheduler.h:67
static Date & getInstance()
Definition Singleton.h:73
Definition TaskScheduler.h:183
EventListener< unsigned int, int, Bytes > CoreEventListener
Type alias for the common EventListener configuration used in the core system.
Definition EventListener.h:100
#define UNIOT_LOG_WARN(...)
Log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:294
#define UNIOT_LOG_DEBUG(...)
Log an DEBUG level message Used for general information about system operation. Only compiled if UNIO...
Definition Logger.h:340
#define UNIOT_LOG_ERROR(...)
Log an ERROR level message Used for critical errors that may prevent normal operation....
Definition Logger.h:273
SharedPointer< SchedulerTask > TaskPtr
Shared pointer type for scheduler tasks.
Definition TaskScheduler.h:190
static TaskPtr make(SchedulerTask::SchedulerTaskCallback callback)
Static factory method to create a task with a callback.
Definition TaskScheduler.h:212
TaskScheduler & push(const char *name, TaskPtr task)
Add a named task to the scheduler.
Definition TaskScheduler.h:233
@ TIME
Time synchronization and date-related operations.
Definition DateEvents.h:61
@ SYNCED
System time has been successfully synchronized with time source.
Definition DateEvents.h:72
@ SYNC_FAILED
Forced NTP synchronization attempt failed (no response from any server)
Definition DateEvents.h:73
@ CONNECTION
MQTT connection state changes and operations.
Definition MQTTEvents.h:61
@ SUCCESS
MQTT connection established successfully.
Definition MQTTEvents.h:73
@ FAILED
MQTT connection failed or was lost.
Definition MQTTEvents.h:72
@ DISCONNECTING
Currently disconnecting from network.
Definition NetworkEvents.h:88
@ SUCCESS
Network operation completed successfully.
Definition NetworkEvents.h:86
@ ACCESS_POINT
Device is operating in access point mode.
Definition NetworkEvents.h:90
@ AVAILABLE
Configured network is available for connection.
Definition NetworkEvents.h:91
@ DISCONNECTED
Network connection has been lost or terminated.
Definition NetworkEvents.h:89
@ FAILED
Network operation failed (connection, scan, etc.)
Definition NetworkEvents.h:85
@ CONNECTING
Currently attempting to connect to network.
Definition NetworkEvents.h:87
@ CONNECTION
WiFi connection state changes and operations.
Definition NetworkEvents.h:74
Contains all classes and functions related to the Uniot Core.