A private doorbell project to serve an Android app with webcam pictures and microphone recordings when someone presses the button. Information is being spread via MQTT. https://www.taibsu.de
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
heimdall/mqttClient.cpp

231 lines
5.5 KiB

#include "mqttClient.h"
#include "log.h"
#include "threadLauncher.h"
#include "heimdall.h"
namespace tai
{
mqttClient::mqttClient(std::shared_ptr<tai::config> config)
: _conf(config)
{
_id = _conf->options("mqttClientId");
_topic = _conf->options("mqttTopic");
_host = _conf->options("mqttHost");
_port = std::atoi(_conf->options("mqttPort").c_str());
_user = _conf->options("mqttUser");
_pass = _conf->options("mqttPass");
_token = _conf->options("mqttToken");
mosqpp::lib_init();
username_pw_set(_user.c_str(), _pass.c_str());
connect(_host.c_str(), _port, _keepalive);
}
mqttClient::~mqttClient()
{
disconnect();
loop_stop();
_mqttLoop.join();
mosqpp::lib_cleanup();
}
/**
* @brief Publishes a given message to the MQTT client
* @author taiBsu
* @param message - The message to be sent.
* @return success or failure.
*/
bool mqttClient::send(std::string const& message)
{
int retval = publish(nullptr, _topic.c_str(), message.length(), message.c_str(), 1, false);
return MOSQ_ERR_SUCCESS == retval;
}
/**
* @brief Subscribes to a given topic.
* @author taiBsu
* @param topic - The topic to be subscribed to.
* @return success or failure.
*/
bool mqttClient::subscribeTo(std::string const& topic)
{
int retval = subscribe(nullptr, topic.c_str(), 1);
return MOSQ_ERR_SUCCESS == retval;
}
/**
* @brief Subscribes to a topic that was set on instantiation of the mqttClient object.
* @author taiBsu
* @return success or failure.
*/
bool mqttClient::subscribeToKnownTopic()
{
int retval = subscribe(nullptr, _topic.c_str(), 1);
return MOSQ_ERR_SUCCESS == retval;
}
/**
* @brief Connected callback.
* @author taiBsu
* @param rc - return code
*/
void mqttClient::on_connect(int rc)
{
if (0 == rc) {
LOG_INF("[MQTT] Connected (" + std::to_string(rc) + ").");
} else {
LOG_ERR("[MQTT] Connection failed (" + std::to_string(rc) + ")!");
}
}
/**
* @brief Disconnect callback.
* @author taibsu
* @param rc - return code
*/
void mqttClient::on_disconnect(int rc)
{
LOG_INF("[MQTT] Disconnected (" + std::to_string(rc) + ").");
}
/**
* @brief TODO implement
*/
void mqttClient::on_publish(int mid)
{
}
/**
* @brief Message callback.
* @author taiBsu
* @param message - The message to be reacted to.
* @TODO All this stuff should be called from another class.
*/
void mqttClient::on_message(const struct mosquitto_message* message)
{
std::string msg { reinterpret_cast<char*>(message->payload) };
LOG_INF("[MQTT] Message received: ");
LOG_INF(msg);
if (msg.find(_token) == std::string::npos) {
LOG_ERR("[MQTT] Unable to find token in message: " + msg);
LOG_ERR("[MQTT] Nothing happened.");
return;
}
// @TODO rework this
if (msg.find("onOpenDoor") != std::string::npos) {
std::string openDoorCmd { "python ./script/openDoor.py" };
LOG_INF("[MQTT] Triggering \"Open door\" procedure: " + openDoorCmd);
if (!send(_token + "triggeredOnOpenDoor")) {
LOG_WRN("[MQTT] Unable to send feedback message for trigger function.");
}
threadLauncher::inst().go(openDoorCmd);
}
if (msg.find("onPlayNotAtHome") != std::string::npos) {
std::string notAtHomeCmd { "aplay ./sounds/notAtHome.wav" };
LOG_INF("[MQTT] Triggering \"Not at home\" procedure: " + notAtHomeCmd);
if (!send(_token + "triggeredOnPlayNotAtHome")) {
LOG_WRN("[MQTT] Unable to send feedback message for trigger function.");
}
threadLauncher::inst().go(notAtHomeCmd);
}
if (msg.find("onPlayOnOurWay") != std::string::npos) {
std::string onOurWayCmd { "aplay ./sounds/onOurWay.wav" };
LOG_INF("[MQTT] Triggering \"On our way\" procedure: " + onOurWayCmd);
if (!send(_token + "triggeredOnPlayOnOurWay")) {
LOG_WRN("[MQTT] Unable to send feedback message for trigger function.");
}
threadLauncher::inst().go(onOurWayCmd);
}
if (msg.find("onTakePicture") != std::string::npos) {
std::string chatId { msg.substr(msg.find_last_of(':') + 1, msg.size() - 1) };
LOG_INF("[MQTT] Triggering picture record for chat ID " + chatId);
std::string pictureName { "door.jpg" };
std::string cmd {};
heimdall::PictureType type =
_conf->options("pictureType") == "raspistill"
? heimdall::PictureType::RASPISTILL
: heimdall::PictureType::MOTION;
switch (type) {
case heimdall::PictureType::RASPISTILL:
{
std::string quality{ "80" };
std::string delay{ "1s" };
std::string rotation{ "270" };
std::string iso{ "600" };
std::string width{ "600" };
std::string height{ "800" };
cmd = "raspistill -q " + quality +
" -t " + delay +
" --rotation " + rotation +
" -ISO " + iso +
" -w " + width +
" -h " + height +
" -o " + pictureName;
break;
}
case heimdall::PictureType::MOTION:
{
cmd = "curl -o door.jpg " + _conf->options("motion_webcamUrl") + "?api_key=" + _conf->options("motion_apiKey");
}
default:
break;
}
bool notifyOnTelegram = _conf->options("tg_notify") == "true";
if (notifyOnTelegram) {
std::string appCmd("curl -s --location --request GET 'https://api.telegram.org/" + _conf->options("tg_botId") + "/sendPhoto?chat_id=" + chatId + "' --form 'photo=@\"" + pictureName + "\"' > /dev/null");
cmd.append(" && " + appCmd);
}
threadLauncher::inst().go(cmd);
if (!send(_token + "triggeredOnTakePicture")) {
LOG_WRN("[MQTT] Unable to send feedback message for trigger function.");
}
}
}
/**
* @brief Subscription callback
* @author taiBsu
* @param mid - ?
* @param qos_count - ?
* @param granted_qos - ?
*/
void mqttClient::on_subscribe(int mid, int qos_count, const int* granted_qos)
{
LOG_INF("[MQTT] Subscription started for topic " + _topic + ".");
}
} // namespace tai