void CExpressivePixelsApp::ExecuteTTYCommand()

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);
}