304 lines
9.4 KiB
C++
304 lines
9.4 KiB
C++
#include <stdarg.h>
|
|
#include "WiFi.h"
|
|
|
|
#include "config.h"
|
|
#include "telnet.h"
|
|
#include "player.h"
|
|
#include "network.h"
|
|
|
|
Telnet telnet;
|
|
|
|
bool Telnet::_isIPSet(IPAddress ip) {
|
|
return ip.toString() == "0.0.0.0";
|
|
}
|
|
|
|
bool Telnet::begin() {
|
|
if (WiFi.status() == WL_CONNECTED || _isIPSet(WiFi.softAPIP())) {
|
|
server.begin();
|
|
server.setNoDelay(true);
|
|
Serial.printf("Ready! Use 'telnet %s 23' to connect\n", WiFi.localIP().toString().c_str());
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Telnet::stop() {
|
|
server.stop();
|
|
}
|
|
|
|
void Telnet::emptyClientStream(WiFiClient client) {
|
|
client.flush();
|
|
delay(50);
|
|
while (client.available()) {
|
|
client.read();
|
|
}
|
|
}
|
|
|
|
void Telnet::cleanupClients() {
|
|
for (int i = 0; i < MAX_TLN_CLIENTS; i++) {
|
|
if (!clients[i].connected()) {
|
|
if (clients[i]) {
|
|
Serial.printf("Client [%d] is %s\n", i, clients[i].connected() ? "connected" : "disconnected");
|
|
clients[i].stop();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Telnet::loop() {
|
|
uint8_t i;
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
if (server.hasClient()) {
|
|
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
|
|
if (!clients[i] || !clients[i].connected()) {
|
|
if (clients[i]) {
|
|
clients[i].stop();
|
|
}
|
|
clients[i] = server.available();
|
|
if (!clients[i]) Serial.println("available broken");
|
|
on_connect(clients[i].remoteIP().toString().c_str(), i);
|
|
clients[i].setNoDelay(true);
|
|
emptyClientStream(clients[i]);
|
|
break;
|
|
}
|
|
}
|
|
if (i >= MAX_TLN_CLIENTS) {
|
|
server.available().stop();
|
|
}
|
|
}
|
|
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
|
|
if (clients[i] && clients[i].connected() && clients[i].available()) {
|
|
String inputstr = clients[i].readStringUntil('\n');
|
|
inputstr.trim();
|
|
on_input(inputstr.c_str(), i);
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
|
|
if (clients[i]) {
|
|
clients[i].stop();
|
|
}
|
|
}
|
|
delay(1000);
|
|
}
|
|
yield();
|
|
}
|
|
|
|
void Telnet::print(const char *buf) {
|
|
for (int id = 0; id < MAX_TLN_CLIENTS; id++) {
|
|
if (clients[id] && clients[id].connected()) {
|
|
print(id, buf);
|
|
}
|
|
}
|
|
Serial.print(buf);
|
|
}
|
|
|
|
void Telnet::print(byte id, const char *buf) {
|
|
if (clients[id] && clients[id].connected()) {
|
|
clients[id].print(buf);
|
|
}
|
|
}
|
|
|
|
void Telnet::printf(const char *format, ...) {
|
|
char buf[MAX_PRINTF_LEN];
|
|
va_list args;
|
|
va_start (args, format );
|
|
vsnprintf(buf, MAX_PRINTF_LEN, format, args);
|
|
va_end (args);
|
|
for (int id = 0; id < MAX_TLN_CLIENTS; id++) {
|
|
if (clients[id] && clients[id].connected()) {
|
|
clients[id].print(buf);
|
|
}
|
|
}
|
|
Serial.print(buf);
|
|
}
|
|
|
|
void Telnet::printf(byte id, const char *format, ...) {
|
|
if (clients[id] && clients[id].connected()) {
|
|
va_list argptr;
|
|
va_start(argptr, format);
|
|
char *szBuffer = 0;
|
|
const size_t nBufferLength = vsnprintf(szBuffer, 0, format, argptr) + 1;
|
|
if (nBufferLength == 1) return;
|
|
szBuffer = (char *) malloc(nBufferLength);
|
|
if (! szBuffer) return;
|
|
vsnprintf(szBuffer, nBufferLength, format, argptr);
|
|
va_end(argptr);
|
|
clients[id].print(szBuffer);
|
|
free(szBuffer);
|
|
}
|
|
}
|
|
|
|
void Telnet::on_connect(const char* str, byte clientId) {
|
|
Serial.printf("Telnet: [%d] %s connected\n", clientId, str);
|
|
print(clientId, "\nWelcome to ёRadio!\n(Use ^] + q to disconnect.)\n> ");
|
|
}
|
|
|
|
void Telnet::info() {
|
|
telnet.printf("##CLI.INFO#\n");
|
|
char timeStringBuff[50];
|
|
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &network.timeinfo);
|
|
telnet.printf("##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset
|
|
telnet.printf("##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
|
|
if (player.mode == PLAYING) {
|
|
telnet.printf("##CLI.META#: %s\n", config.station.title);
|
|
}
|
|
telnet.printf("##CLI.VOL#: %d\n", config.store.volume);
|
|
if (player.mode == PLAYING) {
|
|
telnet.printf("##CLI.PLAYING#\n");
|
|
} else {
|
|
telnet.printf("##CLI.STOPPED#\n");
|
|
}
|
|
telnet.printf("> ");
|
|
}
|
|
|
|
void Telnet::on_input(const char* str, byte clientId) {
|
|
if (strlen(str) == 0) return;
|
|
if (strcmp(str, "cli.prev") == 0 || strcmp(str, "prev") == 0) {
|
|
player.prev();
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.next") == 0 || strcmp(str, "next") == 0) {
|
|
player.next();
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.toggle") == 0 || strcmp(str, "toggle") == 0) {
|
|
player.toggle();
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.stop") == 0 || strcmp(str, "stop") == 0) {
|
|
player.mode = STOPPED;
|
|
//display.title("[stopped]");
|
|
info();
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.start") == 0 || strcmp(str, "start") == 0 || strcmp(str, "cli.play") == 0 || strcmp(str, "play") == 0) {
|
|
player.play(config.store.lastStation);
|
|
return;
|
|
}
|
|
if (strcmp(str, "sys.boot") == 0 || strcmp(str, "boot") == 0 || strcmp(str, "reboot") == 0) {
|
|
ESP.restart();
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.vol") == 0 || strcmp(str, "vol") == 0) {
|
|
printf(clientId, "##CLI.VOL#: %d\n> ", config.store.volume);
|
|
return;
|
|
}
|
|
if (strcmp(str, "sys.date") == 0) {
|
|
network.requestTimeSync(true);
|
|
return;
|
|
}
|
|
int volume;
|
|
if (sscanf(str, "vol(%d)", &volume) == 1 || sscanf(str, "cli.vol(\"%d\")", &volume) == 1 || sscanf(str, "vol %d", &volume) == 1) {
|
|
if (volume < 0) volume = 0;
|
|
if (volume > 254) volume = 254;
|
|
player.setVol(volume, false);
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.audioinfo") == 0 || strcmp(str, "audioinfo") == 0) {
|
|
printf(clientId, "##CLI.AUDIOINFO#: %d\n> ", config.store.audioinfo > 0);
|
|
return;
|
|
}
|
|
int ainfo;
|
|
if (sscanf(str, "audioinfo(%d)", &ainfo) == 1 || sscanf(str, "cli.audioinfo(\"%d\")", &ainfo) == 1 || sscanf(str, "audioinfo %d", &ainfo) == 1) {
|
|
config.store.audioinfo = ainfo > 0;
|
|
printf(clientId, "new audioinfo value is: %d\n> ", config.store.audioinfo);
|
|
config.save();
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.smartstart") == 0 || strcmp(str, "smartstart") == 0) {
|
|
printf(clientId, "##CLI.SMARTSTART#: %d\n> ", config.store.smartstart);
|
|
return;
|
|
}
|
|
int sstart;
|
|
if (sscanf(str, "smartstart(%d)", &sstart) == 1 || sscanf(str, "cli.smartstart(\"%d\")", &sstart) == 1 || sscanf(str, "smartstart %d", &sstart) == 1) {
|
|
config.store.smartstart = (byte)sstart;
|
|
printf(clientId, "new smartstart value is: %d\n> ", config.store.smartstart);
|
|
config.save();
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.list") == 0 || strcmp(str, "list") == 0) {
|
|
printf(clientId, "#CLI.LIST#\n");
|
|
File file = SPIFFS.open(PLAYLIST_PATH, "r");
|
|
if (!file || file.isDirectory()) {
|
|
return;
|
|
}
|
|
char sName[BUFLEN], sUrl[BUFLEN];
|
|
int sOvol;
|
|
byte c = 1;
|
|
while (file.available()) {
|
|
if (config.parseCSV(file.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
|
|
printf(clientId, "#CLI.LISTNUM#: %*d: %s, %s\n", 3, c, sName, sUrl);
|
|
c++;
|
|
}
|
|
}
|
|
printf(clientId, "##CLI.LIST#\n");
|
|
printf(clientId, "> ");
|
|
return;
|
|
}
|
|
if (strcmp(str, "cli.info") == 0 || strcmp(str, "info") == 0) {
|
|
printf(clientId, "##CLI.INFO#\n");
|
|
char timeStringBuff[50];
|
|
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S", &network.timeinfo);
|
|
if (config.store.tzHour < 0) {
|
|
printf(clientId, "##SYS.DATE#: %s%03d:%02d\n", timeStringBuff, config.store.tzHour, config.store.tzMin);
|
|
} else {
|
|
printf(clientId, "##SYS.DATE#: %s+%02d:%02d\n", timeStringBuff, config.store.tzHour, config.store.tzMin);
|
|
}
|
|
printf(clientId, "##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
|
|
if (player.mode == PLAYING) {
|
|
printf(clientId, "##CLI.META#: %s\n", config.station.title);
|
|
}
|
|
printf(clientId, "##CLI.VOL#: %d\n", config.store.volume);
|
|
if (player.mode == PLAYING) {
|
|
printf(clientId, "##CLI.PLAYING#\n");
|
|
} else {
|
|
printf(clientId, "##CLI.STOPPED#\n");
|
|
}
|
|
printf(clientId, "> ");
|
|
return;
|
|
}
|
|
int sb;
|
|
if (sscanf(str, "play(%d)", &sb) == 1 || sscanf(str, "cli.play(\"%d\")", &sb) == 1 || sscanf(str, "play %d", &sb) == 1 ) {
|
|
if (sb < 1) sb = 1;
|
|
if (sb >= config.store.countStation) sb = config.store.countStation;
|
|
player.play((uint16_t)sb);
|
|
return;
|
|
}
|
|
if (strcmp(str, "sys.tzo") == 0 || strcmp(str, "tzo") == 0) {
|
|
printf(clientId, "##SYS.TZO#: %d:%d\n> ", config.store.tzHour, config.store.tzMin);
|
|
return;
|
|
}
|
|
//int16_t tzh, tzm;
|
|
int tzh, tzm;
|
|
if (sscanf(str, "tzo(%d:%d)", &tzh, &tzm) == 2 || sscanf(str, "sys.tzo(\"%d:%d\")", &tzh, &tzm) == 2 || sscanf(str, "tzo %d:%d", &tzh, &tzm) == 2) {
|
|
if (tzh < -12) tzh = -12;
|
|
if (tzh > 14) tzh = 14;
|
|
if (tzm < 0) tzm = 0;
|
|
if (tzm > 59) tzm = 59;
|
|
config.setTimezone((int8_t)tzh, (int8_t)tzm);
|
|
if(tzh<0){
|
|
printf(clientId, "new timezone offset: %03d:%02d\n", config.store.tzHour, config.store.tzMin);
|
|
}else{
|
|
printf(clientId, "new timezone offset: %02d:%02d\n", config.store.tzHour, config.store.tzMin);
|
|
}
|
|
network.requestTimeSync(true);
|
|
return;
|
|
}
|
|
if (sscanf(str, "tzo(%d)", &tzh) == 1 || sscanf(str, "sys.tzo(\"%d\")", &tzh) == 1 || sscanf(str, "tzo %d", &tzh) == 1) {
|
|
if (tzh < -12) tzh = -12;
|
|
if (tzh > 14) tzh = 14;
|
|
config.setTimezone((int8_t)tzh, 0);
|
|
if(tzh<0){
|
|
printf(clientId, "new timezone offset: %03d:%02d\n", config.store.tzHour, config.store.tzMin);
|
|
}else{
|
|
printf(clientId, "new timezone offset: %02d:%02d\n", config.store.tzHour, config.store.tzMin);
|
|
}
|
|
network.requestTimeSync(true);
|
|
return;
|
|
}
|
|
|
|
telnet.printf(clientId, "unknown command: %s\n> ", str);
|
|
}
|