예제 #1
0
파일: command.c 프로젝트: dkulp/fpp
  void ProcessCommand(char *command)
  {
    char *s;
    char *s2;
    char *s3;
    char *s4;
    char *response2 = NULL;
    int i;
		char NextPlaylist[128] = "No playlist scheduled.";
		char NextScheduleStartText[64] = "";
		char CommandStr[64];
		LogExcess(VB_COMMAND, "CMD: %s\n", command);
		s = strtok(command,",");
		strcpy(CommandStr, s);
		if (!strcmp(CommandStr, "s"))
		{
				Json::Value info = player->GetCurrentPlaylistInfo();
				char currentEntryType = 'u'; // Unknown
				const char *currentSequenceName = NULL;
				const char *currentMediaName = NULL;

				if (info["currentEntry"].isMember("sequenceName"))
					currentSequenceName = info["currentEntry"]["sequenceName"].asString().c_str();

				if (info["currentEntry"].isMember("mediaFilename"))
					currentMediaName = info["currentEntry"]["mediaFilename"].asString().c_str();

				if (info["currentEntry"].isMember("type"))
				{
					std::string type = info["currentEntry"]["type"].asString();
					if (type == "both")
						currentEntryType = 'b';
					else if (type == "event")
						currentEntryType = 'e';
					else if (type == "media")
						currentEntryType = 'm';
					else if (type == "pause")
						currentEntryType = 'p';
					else if (type == "plugin")
						currentEntryType = 'P';
					else if (type == "sequence")
						currentEntryType = 's';
				}

				player->GetNextScheduleStartText(NextScheduleStartText);
				player->GetNextPlaylistText(NextPlaylist);
				if(FPPstatus==FPP_STATUS_IDLE)
				{
					if (getFPPmode() == REMOTE_MODE)
					{
						sprintf(response,"%d,%d,%d,%s,%s,%d,%d\n",
							getFPPmode(), 0, getVolume(),
							currentSequenceName,
							currentMediaName,
							player->GetPlaybackSecondsElapsed(),
							player->GetPlaybackSecondsRemaining());
					}
					else
					{
						sprintf(response,"%d,%d,%d,%s,%s\n",
							getFPPmode(),0,getVolume(),
							NextPlaylist,NextScheduleStartText);
					}
				}
				else
				{
					sprintf(response,"%d,%d,%d,%s,%c,%s,%s,%d,%d,%d,%d,%s,%s,%d\n",
							getFPPmode(),FPPstatus,getVolume(),
							info["name"].asString().c_str(),
							currentEntryType,
							currentSequenceName,
							currentMediaName,
							info["currentPosition"].asInt() + 1,
							info["size"].asInt(),
							info["currentEntry"]["secondsElapsed"].asInt(),
							info["currentEntry"]["secondsRemaining"].asInt(),
							NextPlaylist,NextScheduleStartText,
							info["repeat"].asInt());
				}
		}
		else if ((!strcmp(CommandStr, "p")) ||
				 (!strcmp(CommandStr, "P")))
		{
				if(FPPstatus==FPP_STATUS_PLAYLIST_PLAYING || FPPstatus==FPP_STATUS_STOPPING_GRACEFULLY)
				{
					player->PlaylistStopNow();
					sleep(1);
				}
	
				s = strtok(NULL,",");
				if (s)
				{
					std::string playlistName(s);
					int repeat = !strcmp(CommandStr, "p") ? 1 : 0;
					int position = 0;

					s = strtok(NULL,",");
					if (s)
						position = atoi(s);

					if (player->PlaylistStart(playlistName, position, repeat))
					{
						sprintf(response,"%d,%d,Playlist Started,,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS);
					}
					else
					{
						sprintf(response,"%d,%d,Playlist Start Failed,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
					}
				}
				else
				{
					sprintf(response,"%d,%d,Unknown Playlist,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
				}
		}
		else if (!strcmp(CommandStr, "S"))
		{
				if(FPPstatus==FPP_STATUS_PLAYLIST_PLAYING)
				{
					player->PlaylistStopGracefully();
					player->ReLoadCurrentScheduleInfo();
					sprintf(response,"%d,%d,Playlist Stopping Gracefully,,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS);
				}
				else
				{
					sprintf(response,"%d,Not playing,,,,,,,,,,,\n",COMMAND_FAILED);
				}
		}
		else if (!strcmp(CommandStr, "d"))
		{
				if(FPPstatus==FPP_STATUS_PLAYLIST_PLAYING || FPPstatus==FPP_STATUS_STOPPING_GRACEFULLY)
				{
					player->PlaylistStopNow();
					player->ReLoadCurrentScheduleInfo();
					sprintf(response,"%d,%d,Playlist Stopping Now,,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS);
				}
				else
				{
					sprintf(response,"%d,%d,Not playing,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
				}
		}
		else if (!strcmp(CommandStr, "R"))
		{
				if(FPPstatus==FPP_STATUS_IDLE)
				{
					player->ReLoadCurrentScheduleInfo();
				}
				player->ReLoadNextScheduleInfo();
				
				
				sprintf(response,"%d,%d,Reloading Schedule,,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS);
		}
		else if (!strcmp(CommandStr, "v"))
		{
				s = strtok(NULL,",");
				if (s)
				{
					setVolume(atoi(s));
					sprintf(response,"%d,%d,Setting Volume,,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS);
				}
				else
				{
					sprintf(response,"%d,%d,Invalid Volume,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
				}
		}
		else if (!strcmp(CommandStr, "w"))
		{
				LogInfo(VB_SETTING, "Sending Falcon hardware config\n");
				if (!DetectFalconHardware(1))
					SendFPDConfig();
		}
		else if (!strcmp(CommandStr, "r"))
		{
				WriteBytesReceivedFile();
				sprintf(response,"true\n");
		}
		else if (!strcmp(CommandStr, "q"))
		{
			// Quit/Shutdown fppd
			ShutdownFPPD();
		}
		else if (!strcmp(CommandStr, "e"))
		{
			// Start an Effect
			s = strtok(NULL,",");
			s2 = strtok(NULL,",");
			s3 = strtok(NULL,",");
			if (s && s2)
			{
				i = StartEffect(s, atoi(s2), atoi(s3));
				if (i >= 0)
					sprintf(response,"%d,%d,Starting Effect,%d,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS,i);
				else
					sprintf(response,"%d,%d,Invalid Effect,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
			}
			else
				sprintf(response,"%d,%d,Invalid Effect,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
		}
		else if (!strcmp(CommandStr, "t"))
		{
			// Trigger an event
			s = strtok(NULL,",");
			pluginCallbackManager.eventCallback(s, "command");
			i = TriggerEventByID(s);
			if (i >= 0)
				sprintf(response,"%d,%d,Event Triggered,%d,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS,i);
			else
				sprintf(response,"%d,%d,Event Failed,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
		}
		else if (!strcmp(CommandStr, "GetTestMode"))
		{
			strcpy(response, channelTester->GetConfig().c_str());
			strcat(response, "\n");
		}
		else if (!strcmp(CommandStr, "SetTestMode"))
		{
			if (channelTester->SetupTest(std::string(s + strlen(s) + 1)))
			{
				sprintf(response, "0,%d,Test Mode Activated,,,,,,,,,\n",
					COMMAND_SUCCESS);
			}
			else
			{
				sprintf(response, "0,%d,Test Mode Deactivated,,,,,,,,,\n",
					COMMAND_SUCCESS);
			}
		}
		else if (!strcmp(CommandStr, "LogLevel"))
		{
			s = strtok(NULL,",");

			if (SetLogLevel(s)) {
				sprintf(response,"%d,%d,Log Level Updated,%d,%d,,,,,,,,,\n",
					getFPPmode(),COMMAND_SUCCESS,logLevel,logMask);
			} else {
				sprintf(response,"%d,%d,Error Updating Log Level,%d,%d,,,,,,,,,\n",
					getFPPmode(),COMMAND_FAILED,logLevel,logMask);
			}
		}
		else if (!strcmp(CommandStr, "LogMask"))
		{
			s = strtok(NULL,",");

			if ((s && SetLogMask(s)) || SetLogMask("")) {
				sprintf(response,"%d,%d,Log Mask Updated,%d,%d,,,,,,,,,\n",
					getFPPmode(),COMMAND_SUCCESS,logLevel,logMask);
			} else {
				sprintf(response,"%d,%d,Error Updating Log Mask,%d,%d,,,,,,,,,\n",
					getFPPmode(),COMMAND_FAILED,logLevel,logMask);
			}
		}
		else if (!strcmp(CommandStr, "SetSetting"))
		{
			char name[128];

			s = strtok(NULL,",");
			if (s)
			{
				strcpy(name, s);
				s = strtok(NULL,",");
				if (s)
					parseSetting(name, s);
			}
		}
		else if (!strcmp(CommandStr, "StopAllEffects"))
		{
			StopAllEffects();
			sprintf(response,"%d,%d,All Effects Stopped,,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS);
		}
		else if (!strcmp(CommandStr, "StopEffectByName"))
		{
			s = strtok(NULL,",");
			if (strlen(s))
			{
				if (StopEffect(s))
						sprintf(response,"%d,%d,Stopping Effect,%s,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS,s);
				else
						sprintf(response,"%d,%d,Stop Effect Failed,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
			}
		}
		else if (!strcmp(CommandStr, "StopEffect"))
		{
			s = strtok(NULL,",");
			i = atoi(s);
			if (StopEffect(i))
					sprintf(response,"%d,%d,Stopping Effect,%d,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS,i);
			else
					sprintf(response,"%d,%d,Stop Effect Failed,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
		}
		else if (!strcmp(CommandStr, "GetRunningEffects"))
		{
			sprintf(response,"%d,%d,Running Effects",getFPPmode(),COMMAND_SUCCESS);
			GetRunningEffects(response, &response2);
		}
		else if (!strcmp(CommandStr, "ReloadChannelRemapData"))
		{
			if ((FPPstatus==FPP_STATUS_IDLE) &&
				(LoadChannelRemapData())) {
				sprintf(response,"%d,%d,Channel Remap Data Reloaded,,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS);
			} else {
				sprintf(response,"%d,%d,Failed to reload Channel Remap Data,,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED);
			}
		}
		else if (!strcmp(CommandStr, "GetFPPDUptime"))
		{
			sprintf(response,"%d,%d,FPPD Uptime,%d,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS, time(NULL) - fppdStartTime);
		}
		else if (!strcmp(CommandStr, "StartSequence"))
		{
			s = strtok(NULL,",");
			s2 = strtok(NULL,",");
			if (s && s2)
			{
				i = atoi(s2);
				player->StartSequence(s, 0, i);
				sprintf(response,"%d,%d,Sequence Started,,,,,,,,,,,,\n",
					getFPPmode(), COMMAND_SUCCESS);
			}
			else
			{
				LogErr(VB_COMMAND, "Tried to start a sequence when a playlist or "
						"sequence is already running\n");
				sprintf(response,"%d,%d,Sequence Failed,,,,,,,,,,,,\n",
					getFPPmode(), COMMAND_FAILED);
			}
		}
		else if (!strcmp(CommandStr, "StopSequence"))
		{
			s = strtok(NULL,",");
			if (s)
			{
				player->StopSequence(s);
				sprintf(response,"%d,%d,Sequence Stopped,,,,,,,,,,,,\n",
					getFPPmode(), COMMAND_SUCCESS);
			}
			else
			{
				LogDebug(VB_COMMAND, "Invalid command: %s\n", command);
				sprintf(response,"%d,%d,Sequence Name Missing,,,,,,,,,,,,\n",
					getFPPmode(), COMMAND_FAILED);
			}
		}
		else if (!strcmp(CommandStr, "ToggleSequencePause"))
		{
			if ((player->SequencesRunning()) &&
				((FPPstatus == FPP_STATUS_IDLE) ||
				 (FPPstatus != FPP_STATUS_IDLE)))
			{
				player->ToggleSequencePause();
			}
		}
		else if (!strcmp(CommandStr, "SingleStepSequence"))
		{
			if ((player->SequencesRunning()) &&
				(player->SequencesArePaused()) &&
				((FPPstatus == FPP_STATUS_IDLE) ||
				 ((FPPstatus != FPP_STATUS_IDLE))))
			{
				player->SingleStepSequences();
			}
		}
		else if (!strcmp(CommandStr, "SingleStepSequenceBack"))
		{
			if ((player->SequencesRunning()) &&
				(player->SequencesArePaused()) &&
				((FPPstatus == FPP_STATUS_IDLE) ||
				 ((FPPstatus != FPP_STATUS_IDLE))))
			{
				player->SingleStepSequencesBack();
			}
		}
		else if (!strcmp(CommandStr, "NextPlaylistItem"))
		{
			switch (FPPstatus)
			{
				case FPP_STATUS_IDLE:
					sprintf(response,"%d,%d,No playlist running\n",getFPPmode(),COMMAND_FAILED);
					break;
				case FPP_STATUS_PLAYLIST_PLAYING:
					sprintf(response,"%d,%d,Skipping to next playlist item\n",getFPPmode(),COMMAND_SUCCESS);
					player->NextPlaylistItem();
					break;
				case FPP_STATUS_STOPPING_GRACEFULLY:
					sprintf(response,"%d,%d,Playlist is stopping gracefully\n",getFPPmode(),COMMAND_FAILED);
					break;
			}
		}
		else if (!strcmp(CommandStr, "PrevPlaylistItem"))
		{
			switch (FPPstatus)
			{
				case FPP_STATUS_IDLE:
					sprintf(response,"%d,%d,No playlist running\n",getFPPmode(),COMMAND_FAILED);
					break;
				case FPP_STATUS_PLAYLIST_PLAYING:
					sprintf(response,"%d,%d,Skipping to previous playlist item\n",getFPPmode(),COMMAND_SUCCESS);
					player->PrevPlaylistItem();
					break;
				case FPP_STATUS_STOPPING_GRACEFULLY:
					sprintf(response,"%d,%d,Playlist is stopping gracefully\n",getFPPmode(),COMMAND_FAILED);
					break;
			}
		}
		else if (!strcmp(CommandStr, "SetupExtGPIO"))
		{
			// Configure the given GPIO to the given mode
			s = strtok(NULL,",");
			s2 = strtok(NULL,",");
			if (s && s2)
			{
				
				if (!SetupExtGPIO(atoi(s), s2))
				{
					sprintf(response, "%d,%d,Configuring GPIO,%d,%s,,,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS,atoi(s),s2);
				}
				else
				{
					sprintf(response, "%d,%d,Configuring GPIO,%d,%s,,,,,,,,,\n",getFPPmode(),COMMAND_FAILED,atoi(s),s2);
				}
			}
		}
		else if (!strcmp(CommandStr, "ExtGPIO"))
		{
			s = strtok(NULL,",");
			s2 = strtok(NULL,",");
			s3 = strtok(NULL,",");
			if (s && s2 && s3)
			{
				i = ExtGPIO(atoi(s), s2, atoi(s3));
				if (i >= 0) 
				{
					sprintf(response, "%d,%d,Setting GPIO,%d,%s,%d,%d,,,,,,,\n",getFPPmode(),COMMAND_SUCCESS,atoi(s),s2,atoi(s3),i);
				}
				else
				{
					sprintf(response, "%d,%d,Setting GPIO,%d,%s,%d,,,,,,,,\n",getFPPmode(),COMMAND_FAILED,atoi(s),s2,atoi(s3));
				}
			}
		}
		else
		{
			sprintf(response,"Invalid command: '%s'\n", CommandStr);
		}

		if (response2)
		{
			bytes_sent = sendto(socket_fd, response2, strlen(response2), 0,
                          (struct sockaddr *) &(client_address), sizeof(struct sockaddr_un));
			LogDebug(VB_COMMAND, "%s %s", CommandStr, response2);
			free(response2);
			response2 = NULL;
		}
		else
		{
			bytes_sent = sendto(socket_fd, response, strlen(response), 0,
                          (struct sockaddr *) &(client_address), sizeof(struct sockaddr_un));
			LogDebug(VB_COMMAND, "%s %s", CommandStr, response);
		}
  }
예제 #2
0
int InitializeChannelOutputs(void) {
	Json::Value root;
	Json::Reader reader;
	int i = 0;

	channelOutputFrame = 0;

	for (i = 0; i < FPPD_MAX_CHANNEL_OUTPUTS; i++) {
		bzero(&channelOutputs[i], sizeof(channelOutputs[i]));
	}

	// Reset index so we can start populating the outputs array
	i = 0;

	if (FPDOutput.isConfigured())
	{
		channelOutputs[i].startChannel = getSettingInt("FPDStartChannelOffset");
		channelOutputs[i].outputOld = &FPDOutput;

		if (FPDOutput.open("", &channelOutputs[i].privData)) {
			channelOutputs[i].channelCount = channelOutputs[i].outputOld->maxChannels(channelOutputs[i].privData);

			i++;
		} else {
			LogErr(VB_CHANNELOUT, "ERROR Opening FPD Channel Output\n");
		}
	}

	if (((getFPPmode() != BRIDGE_MODE) ||
		 (getSettingInt("E131Bridging"))) &&
		(E131Output.isConfigured()))
	{
		channelOutputs[i].startChannel = 0;
		channelOutputs[i].outputOld  = &E131Output;

		if (E131Output.open("", &channelOutputs[i].privData)) {
			channelOutputs[i].channelCount = channelOutputs[i].outputOld->maxChannels(channelOutputs[i].privData);

			i++;
		} else {
			LogErr(VB_CHANNELOUT, "ERROR Opening E1.31 Channel Output\n");
		}
	}

	FILE *fp;
	char filename[1024];
	char buf[2048];

	// Parse the channeloutputs.json config file
	strcpy(filename, getMediaDirectory());
	strcat(filename, "/config/channeloutputs.json");

	LogDebug(VB_CHANNELOUT, "Loading %s\n", filename);

	if (FileExists(filename))
	{
		std::ifstream t(filename);
		std::stringstream buffer;

		buffer << t.rdbuf();

		std::string config = buffer.str();

		bool success = reader.parse(buffer.str(), root);
		if (!success)
		{
			LogErr(VB_CHANNELOUT, "Error parsing %s\n", filename);
			return 0;
		}

		const Json::Value outputs = root["channelOutputs"];
		std::string type;
		int start = 0;
		int count = 0;

		for (int c = 0; c < outputs.size(); c++)
		{
			type = outputs[c]["type"].asString();

			if (!outputs[c]["enabled"].asInt())
			{
				LogDebug(VB_CHANNELOUT, "Skipping Disabled Channel Output: %s\n", type.c_str());
				continue;
			}

			start = outputs[c]["startChannel"].asInt();
			count = outputs[c]["channelCount"].asInt();

			// internally we start channel counts at zero
			start -= 1;

			channelOutputs[i].startChannel = start;
			channelOutputs[i].channelCount = count;

			if (type == "LEDPanelMatrix") {
#if defined(PLATFORM_PI) || defined(PLATFORM_ODROID)
				if (outputs[c]["subType"] == "RGBMatrix")
					channelOutputs[i].output = new RGBMatrixOutput(start, count);
				else
				{
					LogErr(VB_CHANNELOUT, "%s subType not valid on Pi\n", outputs[c]["subType"].asString().c_str());
					continue;
				}
#endif
#ifdef PLATFORM_BBB
				if (outputs[c]["subType"] == "LEDscapeMatrix")
					channelOutputs[i].output = new LEDscapeMatrixOutput(start, count);
				else
				{
					LogErr(VB_CHANNELOUT, "%s subType not valid on BBB\n", outputs[c]["subType"].asString().c_str());
					continue;
				}
			} else if (type == "BBB48String") {
				channelOutputs[i].output = new BBB48StringOutput(start, count);
			} else if (type == "BBBSerial") {
				channelOutputs[i].output = new BBBSerialOutput(start, count);
#endif
#ifdef USEOLA
			} else if (type == "OLA") {
				channelOutputs[i].output = new OLAOutput(start, count);
#endif
			} else if (type == "USBRelay") {
				channelOutputs[i].output = new USBRelayOutput(start, count);
#if defined(PLATFORM_PI)
			} else if (type == "Hill320") {
				channelOutputs[i].output = new Hill320Output(start, count);
#endif
#ifdef USE_X11Matrix
			} else if (type == "X11Matrix") {
				channelOutputs[i].output = new X11MatrixOutput(start, count);
#endif
			} else {
				LogErr(VB_CHANNELOUT, "Unknown Channel Output type: %s\n", type.c_str());
				continue;
			}

			if (channelOutputs[i].output->Init(outputs[c])) {
				i++;
			} else {
				LogErr(VB_CHANNELOUT, "ERROR Opening %s Channel Output\n", type.c_str());
			}
		}
	}

	// Parse the channeloutputs config file
	strcpy(filename, getMediaDirectory());
	strcat(filename, "/channeloutputs");

	if (FileExists(filename))
	{
		LogDebug(VB_CHANNELOUT, "Loading %s\n", filename);

		fp = fopen(filename, "r");

		if (fp == NULL)
		{
			LogErr(VB_CHANNELOUT,
				"Could not open Channel Outputs config file %s: %s\n",
				filename, strerror(errno));
			channelOutputCount = 0;

			return 0;
		}

		while(fgets(buf, 2048, fp) != NULL)
		{
			int  enabled = 0;
			char type[32];
			int  start = 0;
			int  count = 0;
			char deviceConfig[160];

			if (buf[0] == '#') // Allow # comments for testing
				continue;

			int fields = sscanf(buf, "%d,%[^,],%d,%d,%s",
				&enabled, type, &start, &count, deviceConfig);

			if (fields != 5) {
				LogErr(VB_CHANNELOUT,
					"Invalid line in channeloutputs config file: %s\n", buf);
				continue;
			}

			if (!enabled) {
				LogDebug(VB_CHANNELOUT, "Skipping disabled channel output: %s", buf);
				continue;
			}

			if (count > (FPPD_MAX_CHANNELS - start)) {
				LogWarn(VB_CHANNELOUT,
					"Channel Output config, start (%d) + count (%d) exceeds max (%d) channel\n",
					start, count, FPPD_MAX_CHANNELS);

				count = FPPD_MAX_CHANNELS - start;

				LogWarn(VB_CHANNELOUT,
					"Count suppressed to %d for config line: %s\n", count, buf);
			}

			if (strlen(deviceConfig))
				strcat(deviceConfig, ";");

			strcat(deviceConfig, "type=");
			strcat(deviceConfig, type);

			LogDebug(VB_CHANNELOUT, "ChannelOutput: %d %s %d %d %s\n", enabled, type, start, count, deviceConfig);

			// internally we start channel counts at zero
			start -= 1;

			channelOutputs[i].startChannel = start;
			channelOutputs[i].channelCount = count;


			if ((!strcmp(type, "Pixelnet-Lynx")) ||
				(!strcmp(type, "Pixelnet-Open")))
			{
				channelOutputs[i].output = new USBPixelnetOutput(start, count);
			} else if ((!strcmp(type, "DMX-Pro")) ||
					   (!strcmp(type, "DMX-Open"))) {
				channelOutputs[i].output = new USBDMXOutput(start, count);
			} else if ((!strcmp(type, "VirtualMatrix")) ||
						(!strcmp(type, "FBMatrix"))) {
				channelOutputs[i].output = new FBMatrixOutput(start, count);
			} else if (!strcmp(type, "GPIO")) {
				channelOutputs[i].output = new GPIOOutput(start, count);
			} else if (!strcmp(type, "GenericSerial")) {
				channelOutputs[i].output = new GenericSerialOutput(start, count);
			} else if (!strcmp(type, "LOR")) {
				channelOutputs[i].outputOld = &LOROutput;
			} else if (!strcmp(type, "Renard")) {
				channelOutputs[i].outputOld = &USBRenardOutput;
#ifdef PLATFORM_PI
			} else if (!strcmp(type, "RPIWS281X")) {
				channelOutputs[i].output = new RPIWS281xOutput(start, count);
#endif
			} else if (!strcmp(type, "SPI-WS2801")) {
				channelOutputs[i].output = new SPIws2801Output(start, count);
			} else if (!strcmp(type, "SPI-nRF24L01")) {
				channelOutputs[i].outputOld = &SPInRF24L01Output;
			} else if (!strcmp(type, "Triks-C")) {
				channelOutputs[i].outputOld = &TriksCOutput;
			} else if (!strcmp(type, "GPIO-595")) {
				channelOutputs[i].output = new GPIO595Output(start, count);
			} else if (!strcmp(type, "Debug")) {
				channelOutputs[i].output = new DebugOutput(start, count);
			} else {
				LogErr(VB_CHANNELOUT, "Unknown Channel Output type: %s\n", type);
				continue;
			}

			if ((channelOutputs[i].outputOld) &&
				(channelOutputs[i].outputOld->open(deviceConfig, &channelOutputs[i].privData)))
			{
				if (channelOutputs[i].channelCount > channelOutputs[i].outputOld->maxChannels(channelOutputs[i].privData)) {
					LogWarn(VB_CHANNELOUT,
						"Channel Output config, count (%d) exceeds max (%d) channel for configured output\n",
						channelOutputs[i].channelCount, channelOutputs[i].outputOld->maxChannels(channelOutputs[i].privData));

					channelOutputs[i].channelCount = channelOutputs[i].outputOld->maxChannels(channelOutputs[i].privData);

					LogWarn(VB_CHANNELOUT,
						"Count suppressed to %d for config: %s\n", channelOutputs[i].channelCount, buf);
				}
				i++;
			} else if ((channelOutputs[i].output) &&
					   (channelOutputs[i].output->Init(deviceConfig))) {
				i++;
			} else {
				LogErr(VB_CHANNELOUT, "ERROR Opening %s Channel Output\n", type);
			}
		}
	}

	channelOutputCount = i;

	LogDebug(VB_CHANNELOUT, "%d Channel Outputs configured\n", channelOutputCount);

	LoadChannelRemapData();

	return 1;
}