Skip to content

Commit

Permalink
Remote falcon plugin updates (#5068)
Browse files Browse the repository at this point in the history
* Fix song transition

* Add HTTPSDelete

* Fix link URLs when using self hosted

* Add RemoteFalcon Commands

* update remote falcon purge queue on start
  • Loading branch information
dev8edss authored Dec 18, 2024
1 parent 4f703f4 commit 6bb725f
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 15 deletions.
98 changes: 98 additions & 0 deletions xLights/utils/Curl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -834,3 +834,101 @@ bool Curl::HTTPUploadFile(const std::string& url, const std::string& filename, c

return res;
}

std::string Curl::HTTPSDelete(const std::string& url, const wxString& body, const std::string& user, const std::string& password, const std::string& contentType, int timeout, const std::vector<std::pair<std::string, std::string>>& customHeaders, int* responseCode) {
static log4cpp::Category& logger_curl = log4cpp::Category::getInstance(std::string("log_curl"));
logger_curl.info("URL: %s", url.c_str());

CURL* curl = curl_easy_init();

if (curl != nullptr) {
#ifdef _DEBUG
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, CurlDebug);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
#endif
struct curl_slist* headerlist = nullptr;
static const char buf[] = "Expect:";
headerlist = curl_slist_append(headerlist, buf);

logger_curl.info("CONTENTTYPE: %s", contentType.c_str());
if (contentType == "JSON") {
static const char buf2[] = "Content-Type: application/json";
headerlist = curl_slist_append(headerlist, buf2);
} else if (contentType == "XML") {
static const char buf2[] = "Content-Type: application/xml";
headerlist = curl_slist_append(headerlist, buf2);
} else if (contentType == "TEXT XML") {
static const char buf2[] = "Content-Type: text/xml";
headerlist = curl_slist_append(headerlist, buf2);
} else if (contentType == "HTML") {
static const char buf2[] = "Content-Type: text/html";
headerlist = curl_slist_append(headerlist, buf2);
} else if (contentType == "TEXT") {
static const char buf2[] = "Content-Type: text/plain";
headerlist = curl_slist_append(headerlist, buf2);
} else {
static const char buf2[] = "Content-Type: application/x-www-form-urlencoded";
headerlist = curl_slist_append(headerlist, buf2);
}

logger_curl.info("HEADER START ----------");
for (const auto& it : customHeaders) {
std::string s = it.first + ": " + it.second;
headerlist = curl_slist_append(headerlist, s.c_str());
logger_curl.info(" %s", (const char*)s.c_str());
}
logger_curl.info("HEADER END ----------");

curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);

curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
if (user != "" || password != "") {
std::string sAuth = user + ":" + password;
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_USERPWD, sAuth.c_str());
}

// This prevents us verifying the remote site certificate ... not thrilled about that but without it https calls are failing on windows.
// This may be because of the library we are including ... really not sure. Right now RemoteFalcon will not work without this.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
std::string buffer = "";
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");

logger_curl.info("BODY START ----------");
logger_curl.info("%s", (const char*)body.c_str());
logger_curl.info("BODY END ----------");
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body.size());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (const char*)body.c_str());
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");

// #ifdef _DEBUG
// curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerFunction);
// #endif

CURLcode res = curl_easy_perform(curl);
if (responseCode) {
long rc = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
*responseCode = rc;
}
curl_easy_cleanup(curl);
if (headerlist != nullptr) {
curl_slist_free_all(headerlist);
}
if (res == CURLE_OK) {
logger_curl.debug("RESPONSE START ------");
logger_curl.debug("%s", (const char*)buffer.c_str());
logger_curl.debug("RESPONSE END ------");
return buffer;
} else {
logger_curl.error("Curl post failed: %d", res);
}
}

return "";
}
2 changes: 2 additions & 0 deletions xLights/utils/Curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class Curl {

static std::string HTTPSPost(const std::string& url, const std::vector<Var>& vars, const std::string& user = "", const std::string& password = "", int timeout = 10, const std::vector<std::pair<std::string, std::string>>& customHeaders = {});

static std::string HTTPSDelete(const std::string& url, const wxString& body, const std::string& user = "", const std::string& password = "", const std::string& contentType = "", int timeout = 10, const std::vector<std::pair<std::string, std::string>>& customHeaders = {}, int* responseCode = nullptr);

static std::string HTTPSGet(const std::string& s, const std::string& user = "", const std::string& password = "", int timeout = 10, const std::vector<std::pair<std::string, std::string>>& customHeaders = {}, int* responseCode = nullptr);

static bool HTTPSGetFile(const std::string& s, const std::string& filename, const std::string& user = "", const std::string& password = "", int timeout = 10, wxProgressDialog* prog = nullptr, bool keepProgress = false);
Expand Down
12 changes: 11 additions & 1 deletion xSchedule/RemoteFalcon/RemoteFalcon.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class RemoteFalcon

RemoteFalcon(const RemoteFalconOptions& options) {
__token = options.GetToken();
_URLBase = SpecialOptions::GetOption("RemoteFalconURL", "https://remotefalcon.com") + "/remotefalcon/api";
_URLBase = SpecialOptions::GetOption("RemoteFalconLocalIP", "https://" + SpecialOptions::GetOption("RemoteFalconURL", "remotefalcon.com")) + SpecialOptions::GetOption("RemoteFalconAPI", "/remotefalcon/api");
}

void SetToken(const std::string& token)
Expand Down Expand Up @@ -69,6 +69,16 @@ class RemoteFalcon
return Curl::HTTPSPost(_URLBase + "/updatePlaylistQueue", t, "", "", "JSON", 10, { {"remotetoken", __token} });
}

std::string PurgeQueue() {
std::string t = wxString::Format("{\"remoteToken\":\"%s\"}", __token);
return Curl::HTTPSDelete(_URLBase + "/purgeQueue", t, "", "", "JSON", 10, { { "remotetoken", __token } });
}

std::string EnableMangaedPSA(bool enable) {
std::string t = wxString::Format("{\"remoteToken\":\"%s\",\"managedPsaEnabled\":\"%s\"}", __token, enable ? _("Y") : _("N"));
return Curl::HTTPSPost(_URLBase + "/updateManagedPsa", t, "", "", "JSON", 10, { { "remotetoken", __token } });
}

std::string EnableViewerControl(bool enable)
{
std::string t = wxString::Format("{\"remoteToken\":\"%s\",\"viewerControlEnabled\":\"%s\"}", __token, enable ? _("Y") : _("N"));
Expand Down
110 changes: 96 additions & 14 deletions xSchedule/RemoteFalcon/RemoteFalconMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,18 +262,18 @@ RemoteFalconFrame::RemoteFalconFrame(wxWindow* parent, const std::string& showDi

if (_options.GetClearQueueOnStart()) {
AddMessage(MESSAGE_LEVEL::ML_INFO, "Clearing remote falcon list of songs.");
int tries = 100;
int tries = 10;
bool done = false;
do {
auto res = _remoteFalcon->UpdatePlaylistQueue();
auto res = _remoteFalcon->PurgeQueue();
AddMessage(MESSAGE_LEVEL::ML_INFO, " " + res);

wxJSONReader reader;
wxJSONValue val;
reader.Parse(res, &val);

if (!val.IsNull()) {
if (val["message"].AsString() == "Queue Empty") {
if (val["message"].AsString() == "Success") {
done = true;
}
else if (val["message"].AsString() == "Unauthorized") {
Expand Down Expand Up @@ -536,13 +536,13 @@ void RemoteFalconFrame::GetMode()

if (_subdomain == "") {
MenuItem_VisitorWebPage->Enable(false);
HyperlinkCtrl1->SetLabel("https://remotefalcon.com");
HyperlinkCtrl1->SetURL("https://remotefalcon.com");
HyperlinkCtrl1->SetLabel("https://" + SpecialOptions::GetOption("RemoteFalconURL", "remotefalcon.com"));
HyperlinkCtrl1->SetURL("https://" + SpecialOptions::GetOption("RemoteFalconURL", "remotefalcon.com"));
}
else {
MenuItem_VisitorWebPage->Enable();
HyperlinkCtrl1->SetLabel("https://" + _subdomain + ".remotefalcon.com");
HyperlinkCtrl1->SetURL("https://" + _subdomain + ".remotefalcon.com");
HyperlinkCtrl1->SetLabel("https://" + _subdomain + "." + SpecialOptions::GetOption("RemoteFalconURL", "remotefalcon.com"));
HyperlinkCtrl1->SetURL("https://" + _subdomain + "." + SpecialOptions::GetOption("RemoteFalconURL", "remotefalcon.com"));
AddMessage(MESSAGE_LEVEL::ML_INFO, "SUBDOMAIN: " + _subdomain);
}
}
Expand Down Expand Up @@ -581,12 +581,21 @@ void RemoteFalconFrame::GetAndPlaySong(const std::string& playing)
}

if (nextSong != "" && nextSong != "null" && playing != nextSong) {
AddMessage(MESSAGE_LEVEL::ML_DEBUG, "Asking xSchedule to play " + nextSong);
auto result = xSchedule::EnqueuePlaylistStep(_playlist, nextSong);
std::string songresult;
if (_options.GetImmediatelyInterrupt()) {
AddMessage(MESSAGE_LEVEL::ML_DEBUG, "Asking xSchedule to immediately play " + nextSong);
// xSchedule::NextStepinPlayList();
songresult = xSchedule::EnqueuePlaylistStep(_playlist, nextSong);
} else {
AddMessage(MESSAGE_LEVEL::ML_DEBUG, "Asking xSchedule to play at end of step: " + nextSong);
songresult = xSchedule::EnqueuePlaylistStepAtEndOfStep(_playlist, nextSong);
std::this_thread::sleep_for(std::chrono::milliseconds(_options.GetLeadTime() * 1000 + 200));
}

if (_mode != "voting") {
//auto result = xSchedule::PlayPlayListStep(_playlist, nextSong);
AddMessage(MESSAGE_LEVEL::ML_DEBUG, " " + result);
if (result == "{\"result\":\"ok\"}") {
AddMessage(MESSAGE_LEVEL::ML_DEBUG, " " + songresult);
if (songresult == "{\"result\":\"ok\"}") {
AddMessage(MESSAGE_LEVEL::ML_DEBUG, "Asking remote falcon to take the song off the queue as it is now playing.");
AddMessage(MESSAGE_LEVEL::ML_DEBUG, " " + _remoteFalcon->UpdatePlaylistQueue());
} else {
Expand Down Expand Up @@ -768,8 +777,81 @@ bool RemoteFalconFrame::SendCommand(const std::string& command, const std::strin
msg = "Remote Falcon: Unknown playlist when trying to set playlist to " + parameters;
return false;
}
// Interupt schedule
else if (command == "interrupt_schedule") {
if (parameters == "on") {

_options.SetImmediatelyInterrupt(true);
SaveOptions();
LoadOptions();
AddMessage(MESSAGE_LEVEL::ML_INFO, "Interrupt Schedule: " + parameters);

return true;
} else if (parameters == "off") {

_options.SetImmediatelyInterrupt(false);
SaveOptions();
LoadOptions();
AddMessage(MESSAGE_LEVEL::ML_INFO, "Interrupt Schedule: " + parameters);

return true;
} else {
msg = "Remote Falcon: Interrupt unknown: " + parameters;
return false;
}
}
// Viewer Control
else if (command == "viewer_control") {
if (parameters == "start") {
AddMessage(MESSAGE_LEVEL::ML_INFO, "Asking remote falcon to enable viewer control.");
AddMessage(MESSAGE_LEVEL::ML_INFO, " " + _remoteFalcon->EnableViewerControl(true));
_viewerControlEnabled = true;
return true;
} else if (parameters == "stop") {
AddMessage(MESSAGE_LEVEL::ML_INFO, "Asking remote falcon to disable viewer control.");
AddMessage(MESSAGE_LEVEL::ML_INFO, " " + _remoteFalcon->EnableViewerControl(false));
_viewerControlEnabled = false;
return true;
} else {
msg = "Remote Falcon: Viewer Control unknown: " + parameters;
return false;
}
// Listener enable
} else if (command == "enable") {
if (parameters == "start") {
AddMessage(MESSAGE_LEVEL::ML_INFO, "Asking remote falcon to start Listener.");
Start();
Button_Pause->SetLabel("Stop");
return true;
} else if (parameters == "stop") {
AddMessage(MESSAGE_LEVEL::ML_INFO, "Asking remote falcon to stop Listener.");
Stop();
Button_Pause->SetLabel("Start");
return true;
} else {
msg = "Remote Falcon: Listener unknown: " + parameters;
return false;
}
//Managed PSA
} else if (command == "managed_psa") {
if (parameters == "on") {
AddMessage(MESSAGE_LEVEL::ML_INFO, "Asking remote falcon to enable Managaed PSA." + _remoteFalcon->EnableMangaedPSA(true));
return true;
} else if (parameters == "off") {
AddMessage(MESSAGE_LEVEL::ML_INFO, "Asking remote falcon to disable Managed PSA." + _remoteFalcon->EnableMangaedPSA(false));
return true;
} else {
msg = "Remote Falcon: Managed PSA unknown: " + parameters;
return false;
}
//Purge Queue
} else if (command == "purge_queue") {
msg = "Remote Falcon: Purging Queue";
AddMessage(MESSAGE_LEVEL::ML_INFO, "Asking remote falcon to purge Queue." + _remoteFalcon->PurgeQueue());
return true;
}
else {
msg = "Remote Falcon: Unknown command";
msg = "Remote Falcon: Unknown command: " + command;
return false;
}
return true;
Expand Down Expand Up @@ -816,12 +898,12 @@ void RemoteFalconFrame::OnTimer_UpdatePlaylistTrigger(wxTimerEvent& event)

void RemoteFalconFrame::OnMenuItem_RFWebSelected(wxCommandEvent& event)
{
::wxLaunchDefaultBrowser(_("https://remotefalcon.com"));
::wxLaunchDefaultBrowser(_("https://" + SpecialOptions::GetOption("RemoteFalconURL", "remotefalcon.com")));
}

void RemoteFalconFrame::OnMenuItem_VisitorWebPageSelected(wxCommandEvent& event)
{
::wxLaunchDefaultBrowser("https://" + _subdomain + ".remotefalcon.com");
::wxLaunchDefaultBrowser("https://" + _subdomain + "." + SpecialOptions::GetOption("RemoteFalconURL", "remotefalcon.com"));
}

void RemoteFalconFrame::OnHyperlinkCtrl1Click(wxCommandEvent& event)
Expand Down
6 changes: 6 additions & 0 deletions xSchedule/RemoteFalcon/xSchedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ class xSchedule
{
return Action("Enqueue playlist step", playlist + "," + step);
}
static std::string EnqueuePlaylistStepAtEndOfStep(const std::string& playlist, const std::string& step) {
return Action("Run command at end of current step", "Enqueue playlist step," + playlist + "," + step);
}
static std::string NextStepinPlayList() {
return Action("Next step in current playlist");
}
static std::string PlayEffect(const std::string& playlist, const std::string& step, EFFECT_MODE mode) {
switch (mode) {
case EFFECT_MODE::EM_PLAY_IMMEDIATELY:
Expand Down

0 comments on commit 6bb725f

Please sign in to comment.