in Firmware/ExpressivePixelsCore/EPXApp.cpp [1080:1493]
void CExpressivePixelsApp::ExecuteTTYCommand()
{
bool responseSuccess = false, syntax = false;
char *pszPostToken;
EPXString response, innerResponse;
DEBUGLOGLN("CExpressivePixelsApp::ExecuteTTYCommand");
DEBUGLOGLN("%s", m_TTYValue.c_str());
if (stricmp(m_TTYValue.c_str(), (const char *)TTYTOKEN_HELP) == 0 || stricmp(m_TTYValue.c_str(), (const char *)TTYTOKEN_HELPQ) == 0)
{
EPXString body;
body += "SYNTAX\n";
body += "- HELP\n";
body += "- AUTOPLAY <ON | OFF> (toggle ON/OFF auto sequence play on USB power)\n";
body += "- CHASER <ON | OFF> (toggle 0-N led lightup)\n";
body += "- DEEPSLEEP (places device into deep sleep)\n";
body += "- DELETE <ID> (delete stored sequence by unique ID)\n";
body += "- FORMAT YES (erases flash memory followed by reboot)\n";
body += "- INFO (displays device information)\n";
body += "- LIST (lists stored sequences)\n";
body += "- NAME <DeviceName> (Name of device)\n";
body += "- REBOOT <UF2 | OTA> (Reboot device, optionally into bootloader mode)\n";
body += "- ROTATE <90 | 180 | 270> (Rotate image on display)\n";
body += "\n>>> Trigger Systems <<<\n";
body += "-----------------------\n";
body += "- BEACONACTIVATION ADD <HOSTDEVICENAME> <ACTIVATION BIT> <ANIMATION NAME> (Add beacon activation entry)\n";
body += "- BEACONACTIVATION ALWAYSON <ON | OFF> (disable beacon activation expiry sleep)\n";
body += "- BEACONACTIVATION DELETE <HOSTDEVICENAME> <ACTIVATION BIT> (Remove beacon activation entry)\n";
body += "- BEACONACTIVATION DELETEALL (Remove all beacon activation entries)\n";
body += "- BEACONACTIVATION LIST (List beacon activations)\n";
body += "- BEACONACTIVATION <ENABLED | DISABLED> (Enable or disable Beacon activation)\n";
body += "- MIDIACTIVATION ADD <CHANNEL> <NOTE> <ANIMATION NAME> (Add MIDI activation entry)\n";
body += "- MIDIACTIVATION DELETE <CHANNEL> <NOTE> (Remove MIDI activation entry)\n";
body += "- MIDIACTIVATION DELETEALL (Remove all MIDI activation entries)\n";
body += "- MIDIACTIVATION LIST (List MIDI activations)\n";
body += "- MIDIACTIVATION TRACE <ON | OFF> (enable note tracings)\n";
// Add the trigger subsystem help syntax
TRIGGERSOURCEITEM *pTriggerSource = m_triggerSources;
while (pTriggerSource != NULL)
{
body += pTriggerSource->pITriggerSource->AppendConsoleHelpSyntax();
pTriggerSource = pTriggerSource->pNext;
}
innerResponse += JSON_KEYVALUE_STRINGPAIR("info", body);
responseSuccess = true;
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_CLEARSETTINGS, &pszPostToken))
{
CSettings::ClearAll();
responseSuccess = true;
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_CHASER, &pszPostToken))
{
DEBUGLOGLN("TTY CHASER");
if (stricmp(pszPostToken, (const char *) TOGGLE_ON) == 0)
{
AutoPlayClear();
m_CAnimator.Clear();
m_renderMode = RENDERMODE_CHASER;
m_CDisplayArray.Chase(true);
}
else
{
m_CDisplayArray.Clear();
m_CDisplayArray.Show();
m_renderMode = RENDERMODE_ANIMATE;
}
responseSuccess = true;
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_AUTOPLAY, &pszPostToken))
{
char *pszAction = ExtractNextParameter(pszPostToken, &pszPostToken);
DEBUGLOGLN("WRITING SETTINGSKEY_AUTOPLAYONUSBPOWER %s", pszPostToken);
m_bAutoPlayOnUSBPower = stricmp(pszAction, (const char *)TOGGLE_ON) == 0;
CSettings::Write((const char *)SETTINGSKEY_AUTOPLAYONUSBPOWER, &m_bAutoPlayOnUSBPower, sizeof(m_bAutoPlayOnUSBPower));
responseSuccess = true;
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_NAME, &pszPostToken))
{
DEBUGLOGLN("DEVICENAME %s", pszPostToken);
CSettings::WriteString((const char *)SETTINGSKEY_DEVICENAME, pszPostToken, strlen(pszPostToken));
responseSuccess = true;
}
else if (!stricmp(m_TTYValue.c_str(), (const char *)TTYTOKEN_INFO))
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += GetDeviceResponseInfo();
innerResponse += JSON_CLOSEOBJECT;
responseSuccess = true;
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_BEACONACTIVATION, &pszPostToken))
{
char *pszAction = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszAction != NULL)
{
if (!stricmp(pszAction, (const char *)TTYTOKEN_ADD))
{
char *pszHost = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszHost != NULL)
{
char *pszActivationBit = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszActivationBit != NULL)
{
if (pszPostToken != NULL)
{
syntax = true;
m_CBLEChannel.SetBeaconActivation(false);
responseSuccess = m_beaconActivation.AddEntry(pszHost, pszActivationBit, pszPostToken);
m_CBLEChannel.SetBeaconActivation(m_bBeaconActivation);
}
}
}
if (!syntax)
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Syntax error");
innerResponse += JSON_CLOSEOBJECT;
}
}
else if (!stricmp(pszAction, (const char *)TTYTOKEN_DELETE))
{
char *pszHost = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszHost != NULL)
{
char *pszActivationBit = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszActivationBit != NULL)
{
syntax = true;
m_CBLEChannel.SetBeaconActivation(false);
responseSuccess = m_beaconActivation.RemoveEntry(pszHost, pszActivationBit);
m_CBLEChannel.SetBeaconActivation(m_bBeaconActivation);
}
}
if (!syntax)
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Syntax error");
innerResponse += JSON_CLOSEOBJECT;
}
}
else if (!stricmp(pszAction, (const char *)TTYTOKEN_DELETEALL))
{
m_CBLEChannel.SetBeaconActivation(false);
responseSuccess = m_beaconActivation.RemoveAll();
m_CBLEChannel.SetBeaconActivation(m_bBeaconActivation);
}
else if (!stricmp(pszAction, (const char *)TTYTOKEN_ALWAYSON))
{
char *pszOn = ExtractNextParameter(pszPostToken, &pszPostToken);
m_bBeaconActivationAlwaysOn = stricmp(pszOn, (const char *)TOGGLE_ON) == 0;
CSettings::Write((const char *) SETTINGSKEY_BEACONACTIVATIONALWAYSON, &m_bBeaconActivationAlwaysOn, sizeof(m_bBeaconActivationAlwaysOn));
responseSuccess = true;
}
else if (!stricmp(pszAction, (const char *)TTYTOKEN_LIST))
{
BEACONACTIVATIONITEM *pCur = m_beaconActivation.FirstEntry();
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYOBJECTOPENARRAY("Activations");
while (pCur != NULL)
{
char szBit[8];
itoa(pCur->m_beaconActivationBit, szBit, 10);
innerResponse += JSON_QUOTEDVALUE(EPXString(pCur->szBeaconHostName) + " " + szBit + " " + pCur->szAnimationName);
if (pCur->pNext != NULL)
innerResponse += EPXString(JSON_CONTINUATION) + JSON_LINESEPARATOR;
pCur = pCur->pNext;
}
innerResponse += JSON_CLOSEARRAY;
innerResponse += JSON_CLOSEOBJECT;
responseSuccess = true;
}
else if (stricmp(pszAction, (const char *) TTYTOKEN_ENABLED) == 0 || stricmp(pszAction, (const char *) TTYTOKEN_DISABLED) == 0)
{
DEBUGLOGLN("BEACON ACTIVATION %s", pszAction);
m_bBeaconActivation = stricmp(pszAction, (const char *)TTYTOKEN_ENABLED) == 0;
CSettings::Write((const char *) SETTINGSKEY_BEACONACTIVATION, &m_bBeaconActivation, sizeof(m_bBeaconActivation));
m_CBLEChannel.SetBeaconActivation(m_bBeaconActivation);
responseSuccess = true;
}
else
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Unknown BEACONACTIVATION action");
innerResponse += JSON_CLOSEOBJECT;
}
}
}
#ifdef EPXMIDI
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_MIDIACTIVATION, &pszPostToken))
{
char *pszAction = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszAction != NULL)
{
if (!stricmp(pszAction, (const char *)TTYTOKEN_ADD))
{
char *pszChannel = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszChannel != NULL)
{
char *pszNote = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszNote != NULL)
{
if (pszPostToken != NULL)
{
syntax = true;
responseSuccess = m_CMIDIActivation.AddEntry(pszChannel, pszNote, pszPostToken);
}
}
}
if (!syntax)
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Syntax error");
innerResponse += JSON_CLOSEOBJECT;
}
}
else if (!stricmp(pszAction, (const char *)TTYTOKEN_DELETE))
{
char *pszHost = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszHost != NULL)
{
char *pszActivationBit = ExtractNextParameter(pszPostToken, &pszPostToken);
if (pszActivationBit != NULL)
{
syntax = true;
responseSuccess = m_CMIDIActivation.RemoveEntry(pszHost, pszActivationBit);
}
}
if (!syntax)
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Syntax error");
innerResponse += JSON_CLOSEOBJECT;
}
}
else if (!stricmp(pszAction, (const char *)TTYTOKEN_DELETEALL))
responseSuccess = m_CMIDIActivation.RemoveAll();
else if (!stricmp(pszAction, (const char *)TTYTOKEN_TRACE))
{
syntax = true;
char *pszOn = ExtractNextParameter(pszPostToken, &pszPostToken);
m_CMIDIActivation.SetTrace(stricmp(pszOn, (const char *)TOGGLE_ON) == 0);
responseSuccess = true;
}
else if (!stricmp(pszAction, (const char *)TTYTOKEN_LIST))
{
MIDIACTIVATIONITEM *pCur = m_CMIDIActivation.FirstEntry();
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYOBJECTOPENARRAY("Activations");
while (pCur != NULL)
{
char szChannel[8], szNote[8];
itoa(pCur->channel, szChannel, 10);
itoa(pCur->note, szNote, 10);
innerResponse += JSON_QUOTEDVALUE(EPXString("Channel ") + szChannel + ", Note " + szNote + " -> " + pCur->szAnimationName);
if (pCur->pNext != NULL)
innerResponse += EPXString(JSON_CONTINUATION) + JSON_LINESEPARATOR;
pCur = pCur->pNext;
}
innerResponse += EPXString(JSON_CLOSEARRAY);
innerResponse += JSON_CLOSEOBJECT;
responseSuccess = true;
}
else
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Unknown MIDIACTIVATION action");
innerResponse += JSON_CLOSEOBJECT;
}
}
}
#endif
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_LIST, &pszPostToken))
{
pszPostToken = MoveToTokenParameter(m_TTYValue.c_str(), (const char *)TTYTOKEN_LIST);
m_CAppStorage.EnumerateSequencesJSON(response, m_PayloadActiveTransactionID, (char *) "info");
DataChannelSendResponseJSON(response);
return;
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_FORMAT, &pszPostToken))
{
pszPostToken = MoveToTokenParameter(m_TTYValue.c_str(), (const char *)TTYTOKEN_FORMAT);
if (stricmp(pszPostToken, (const char *)TOGGLE_YES) == 0)
{
m_CAppStorage.Format();
delay(1000);
EPXPlatform_Runtime_Reboot(REBOOTTYPE_NONE);
responseSuccess = true;
}
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_DELETE, &pszPostToken))
{
AutoPlayClear();
m_CAnimator.Clear();
if (m_CAppStorage.SequenceDelete(pszPostToken))
responseSuccess = true;
else
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Delete file by ID not found");
innerResponse += JSON_CLOSEOBJECT;
}
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_DEEPSLEEP, &pszPostToken))
{
PowerManage(false);
EPXPlatform_Runtime_MCUDeepSleep();
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_REBOOT, &pszPostToken))
{
uint8_t rebootType = REBOOTTYPE_NONE;
DEBUGLOGLN("REBOOT Type %s", pszPostToken);
responseSuccess = true;
if (stricmp(pszPostToken, (const char *) "DISCONNECT") == 0)
{
m_bRebootOnDisconnect = true;
goto sendResponse;
}
else if (stricmp(pszPostToken, (const char *) "UF2") == 0)
rebootType = REBOOTTYPE_UF2;
else if (stricmp(pszPostToken, (const char *) "OTA") == 0)
rebootType = REBOOTTYPE_OTA;
else if (stricmp(pszPostToken, (const char *) "USB") == 0)
rebootType = REBOOTTYPE_SERIAL;
EPXPlatform_Runtime_Reboot(rebootType);
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_ROTATE, &pszPostToken))
{
int rotateBy = -1;
// Rotation only supported on square displays
if(m_CDisplayArray.Width() != m_CDisplayArray.Height())
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Rotation only supported on square displays");
innerResponse += JSON_CLOSEOBJECT;
}
else
{
DEBUGLOGLN("ROTATE IMAGE BY %s degrees", pszPostToken);
if (stricmp(pszPostToken, (const char *) "0") == 0)
rotateBy = 0;
else if (stricmp(pszPostToken, (const char *) "90") == 0)
rotateBy = 90;
else if (stricmp(pszPostToken, (const char *) "180") == 0)
rotateBy = 180;
else if (stricmp(pszPostToken, (const char *) "270") == 0)
rotateBy = 270;
if (rotateBy == 0 || rotateBy == 90 || rotateBy == 180 || rotateBy == 270)
{
m_CAnimator.SetRotation(rotateBy);
CSettings::Write((const char *) SETTINGSKEY_ROTATEBY, &rotateBy, sizeof(rotateBy));
responseSuccess = true;
}
else
{
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Unsupported rotation angle");
innerResponse += JSON_CLOSEOBJECT;
}
}
}
else if (CompareToken(m_TTYValue.c_str(), (const char *)TTYTOKEN_PLAY, &pszPostToken))
{
EXPRESSIVEPIXEL_SEQUENCE sequence;
AutoPlayClear();
m_CDisplayArray.Clear();
if (m_CAppStorage.SequenceRead(pszPostToken, &sequence))
m_CAnimator.Activate(&sequence);
responseSuccess = true;
}
else
{
bool bProcessed = false;
TRIGGERSOURCEITEM *pTriggerSourceItem = m_triggerSources;
while (!bProcessed && pTriggerSourceItem != NULL)
{
bProcessed = pTriggerSourceItem->pITriggerSource->ConsoleCommandProcess(m_TTYValue.c_str(), &responseSuccess, innerResponse);
pTriggerSourceItem = pTriggerSourceItem->pNext;
}
if (!bProcessed)
{
DEBUGLOGLN("UNKNOWN CONSOLE COMMAND");
innerResponse += JSON_KEYOBJECTOPEN("info");
innerResponse += JSON_KEYVALUE_STRINGPAIR("details", "Unknown command");
innerResponse += JSON_CLOSEOBJECT;
}
}
sendResponse:
response += JSON_OPENOBJECT;
response += JSON_KEYVALUE_STRINGPAIR_CONTINUED(JSON_STATUS, responseSuccess ? JSON_SUCCESS : JSON_ERROR);
response += JSON_KEYVALUE_STRINGPAIR_CONTINUED((const char *) JSONKEY_TRANSACTIONID, EPXString(m_PayloadActiveTransactionID));
if (innerResponse.length() > 0)
response += innerResponse;
response += JSON_CLOSEOBJECT;
//Serial.println(response.c_str());
DataChannelSendResponseJSON(response);
}