/*********************************************************************************** Command-line MDC for Samsung TVs Copyright (C) 2011 SAGE team s.r.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. ***********************************************************************************/ #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <cctype> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <pthread.h> #include <vector> using namespace std; #define MAXSIZE 8192 #define MAX_TELKA 512 #define MAX_VAL 32 #define CMD_POWER 1 #define CMD_WALL 2 #define CMD_LAMP 3 #define CMD_SETWALL 4 #define CMD_COLORTEMP 5 #define CMD_COLORTONE 6 // 0x27 (0-100) #define CMD_COLOR 7 // 0x26 (0-100/2) #define CMD_SHARPNESS 8 // 0x25 (0-100) #define CMD_BRIGHTNESS 9 // 0x24 (0-100) #define CMD_CONTRAST 10 /* 0x14 - PC 0x1E - BNC 0x18 - DVI 0x0C - AV 0x04 - S-video 0x08 - Component 0x20 - MagicNet 0x21 - HDMI 0x23 - HDMI2 0x30 - TV 0x40 - DTV */ #define CMD_INPUT 11 #define CMD_GET_VIDEO 12 #define CMD_GET_MAINT 13 #define CMD_GET_STATUS 14 #define CMD_TIME 15 #define CMD_GET_TIME 16 #define CMD_GET_DISPLAY 17 #define CMD_WALLMODE 18 #define CMD_GET_WALLMODE 19 typedef struct { int id; char val[MAX_VAL]; char nazov[64]; int params; char hodnoty[128]; int wait; }INPUTS; typedef struct { int id; const char *nazov[MAX_VAL]; }OUTPUTS; const INPUTS inputs[]={ {CMD_POWER,"power","Power on/off",1,"0 - off, 1 - on",2}, {CMD_WALL,"wall","Wall mode on/off",1,"0 - off, 1 - on",0}, {CMD_LAMP,"lamp","Manual lamp control",1,"0 - 100",0}, {CMD_INPUT,"input","Input source",1,"source",0}, {CMD_CONTRAST,"con","Contrast",1,"0-100",0}, {CMD_BRIGHTNESS,"br","Brightness",1,"0-100",0}, {CMD_SHARPNESS,"sh","Sharpness",1,"0-100",0}, {CMD_COLOR,"col","Color",1,"0-100",0}, {CMD_COLORTONE,"tone","Color tone",1,"0-4,5 - off",0}, {CMD_COLORTEMP,"temp","Color temperature",1,"0-10",0}, {CMD_TIME,"time","Set time",0,"",0}, {CMD_SETWALL,"setwall","Set wall parameters",0,"",0}, {CMD_WALLMODE,"wallmode","Set wall mode",1,"0 - natural, 1 - full",0}, {CMD_GET_VIDEO,"getvideo","Get video settings",0,"",0}, {CMD_GET_MAINT,"getmaint","Get maintenance",0,"",0}, {CMD_GET_STATUS,"getstatus","Get status",0,"",0}, {CMD_GET_TIME,"gettime","Get time",0,"",0}, {CMD_GET_DISPLAY,"getdisplay","Get display status",0,"",0}, {CMD_GET_WALLMODE,"getwallmode","Get wall mode",0,"",0}, {0,"","",0,"",0} }; const OUTPUTS outputs[]={ {CMD_POWER,{"POWER",NULL}}, {CMD_WALL,{"WALL",NULL}}, {CMD_LAMP,{"LAMP",NULL}}, {CMD_INPUT,{"INPUT",NULL}}, {CMD_GET_VIDEO,{"CONTRAST","BRIGHTNESS","SHARPNESS","COLOR","TINT","COLORTONE","COLORTEMP",NULL}}, {CMD_GET_MAINT,{"POWER","P.SIZE","P.SOURCE","LMAX_H","LMAX_M","LMAX_AP","LMAXVALUE","LMIN_H","LMIN_M","LMIN_AP","LMINVALUE","LAMPVALUE","SCREENINTERVAL","SCREENTIME","SCREENTYPE","VWALL","VWALLFORMAT","VWALLDIVID","VWALLSET",NULL}}, {CMD_GET_STATUS,{"POWER","VOLUME","MUTE","INPUT","ASPECT","N_TIME_NF","F_TIME_NF",NULL}}, {CMD_GET_DISPLAY,{"LAMP_ERROR","TEMP_ERROR","BRIGHTSENSOR_ERROR","SYNC_ERROR","CUR_TEMP","FAN_ERROR",NULL}}, {CMD_TIME,{"AM","HOUR","MIN",NULL}}, {CMD_GET_TIME,{"AM","HOUR","MIN",NULL}}, {CMD_WALLMODE,{"WALLMODE",NULL}}, {CMD_GET_WALLMODE,{"WALLMODE",NULL}}, {0,{NULL}} }; typedef struct { char nazov[64]; int id; char ip[32]; int wall_h,wall_v,wall_pos; }TELKA; vector<TELKA> telka; int finalize(unsigned char *pole,int l) { unsigned char s=0; pole[0]=0xaa; for(int i=1;i<l;i++){ s+=pole[i]; } pole[l]=s; return l+1; } typedef struct{ int id; int wait; // kolko sekund cakat unsigned char val[16]; }CMD; void show_status(const CMD cmd,const unsigned char id,unsigned char *val) { char tbuf[1024],temp[128]; for(int i=0;outputs[i].id>0;i++){ if(cmd.id==outputs[i].id){ sprintf(tbuf,"@ %s",telka[id].nazov); for(int j=0;outputs[i].nazov[j];j++){ sprintf(temp," %s:%d",outputs[i].nazov[j],val[j]); strcat(tbuf,temp); } printf("%s\n",tbuf); return; } } } int get_message(CMD cmd,const unsigned char id,unsigned char *pole) { if(cmd.id==CMD_POWER){ int l=1; pole[l++]=0x11; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>1) cmd.val[0]=1; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_WALL){ int l=1; pole[l++]=0x84; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>1) cmd.val[0]=1; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_LAMP){ int l=1; pole[l++]=0x58; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>100) cmd.val[0]=100; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_SETWALL){ int l=1; pole[l++]=0x89; pole[l++]=telka[id].id; pole[l++]=0x02; pole[l++]=telka[id].wall_h*0x10+telka[id].wall_v; pole[l++]=telka[id].wall_pos; return finalize(pole,l); } if(cmd.id==CMD_COLORTEMP){ int l=1; pole[l++]=0x3f; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>10) cmd.val[0]=10; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_COLORTONE){ int l=1; pole[l++]=0x3e; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>4) cmd.val[0]=0x50; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_COLOR){ int l=1; pole[l++]=0x27; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>100) cmd.val[0]=100; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_SHARPNESS){ int l=1; pole[l++]=0x26; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>100) cmd.val[0]=100; if(cmd.val[0]&1) cmd.val[0]--; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_BRIGHTNESS){ int l=1; pole[l++]=0x25; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>100) cmd.val[0]=100; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_CONTRAST){ int l=1; pole[l++]=0x24; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>100) cmd.val[0]=100; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_INPUT){ int l=1; pole[l++]=0x14; pole[l++]=telka[id].id; pole[l++]=0x01; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_GET_VIDEO){ int l=1; pole[l++]=0x04; pole[l++]=telka[id].id; pole[l++]=0x00; return finalize(pole,l); } if(cmd.id==CMD_GET_MAINT){ int l=1; pole[l++]=0x08; pole[l++]=telka[id].id; pole[l++]=0x00; return finalize(pole,l); } if(cmd.id==CMD_GET_STATUS){ int l=1; pole[l++]=0x00; pole[l++]=telka[id].id; pole[l++]=0x00; return finalize(pole,l); } if(cmd.id==CMD_TIME){ time_t theTime; time(&theTime); tm *t=localtime(&theTime); int l=1; pole[l++]=0x01; pole[l++]=telka[id].id; pole[l++]=0x05; pole[l++]=(t->tm_hour<12)?1:0; pole[l++]=(t->tm_hour%12)==0?12:t->tm_hour%12; pole[l++]=t->tm_min; pole[l++]=0x00; pole[l++]=0x00; return finalize(pole,l); } if(cmd.id==CMD_GET_TIME){ int l=1; pole[l++]=0x01; pole[l++]=telka[id].id; pole[l++]=0x00; return finalize(pole,l); } if(cmd.id==CMD_GET_DISPLAY){ int l=1; pole[l++]=0x0D; pole[l++]=telka[id].id; pole[l++]=0x00; return finalize(pole,l); } if(cmd.id==CMD_WALLMODE){ int l=1; pole[l++]=0x5c; pole[l++]=telka[id].id; pole[l++]=0x01; if(cmd.val[0]>1) cmd.val[0]=1; pole[l++]=cmd.val[0]; return finalize(pole,l); } if(cmd.id==CMD_GET_WALLMODE){ int l=1; pole[l++]=0x5c; pole[l++]=telka[id].id; pole[l++]=0x00; return finalize(pole,l); } return 0; } int check_message(const unsigned char id,unsigned char *pole,const int l,const unsigned char rCMD,unsigned char *out) { unsigned char s=0; int pos=-l*10; int ok=-1,dl=-1; for(int i=0;i<l;i++,pos++){ if(pos>0){ // printf("%d: %02x\n",pos,pole[i]); if(pos==1&&pole[i]!=0xff) return -1; if(pos==2&&pole[i]!=telka[id].id) return -1; if(pos==3) dl=pole[i]; if(pos==4&&pole[i]=='A') ok=1; if(pos==4&&pole[i]=='N') ok=0; if(pos==5&&pole[i]!=rCMD) return -1; if(pos==4+dl&&s!=pole[i]) return -2; if(pos>=4+dl) return ok; out[pos-6]=pole[i]; s+=pole[i]; } else{ if(pole[i]==0xaa) pos=0; } } return -3; } CMD command; int in_telka; pthread_mutex_t main_mutex = PTHREAD_MUTEX_INITIALIZER; void *main_thread(void *a) { unsigned char pole[MAXSIZE]; unsigned char val[MAX_VAL]; unsigned char rCMD; int sd,id,l=0; struct sockaddr_in pin; id=in_telka; pthread_mutex_unlock(&main_mutex); memset(&pin,0,sizeof(pin)); pin.sin_family=AF_INET; inet_pton(AF_INET,telka[id].ip,&pin.sin_addr.s_addr); pin.sin_port=htons(1515); if((sd=socket(AF_INET, SOCK_STREAM,0))==-1){ pthread_exit(NULL); } if(connect(sd,(struct sockaddr *)&pin,sizeof(pin))==-1){ pthread_exit(NULL); } l=get_message(command,id,pole); if(l<=0){ close(sd); pthread_exit(NULL); } rCMD=pole[1]; printf("-> %s : Sending message (ID: %d, IP: %s)\n",telka[id].nazov,telka[id].id,telka[id].ip); if(send(sd,pole,l,0)==-1){ close(sd); pthread_exit(NULL); } struct timespec req,rem; req.tv_sec=2; req.tv_nsec=0; nanosleep(&req,&rem); if((l=recv(sd,pole,MAXSIZE,0))==-1){ close(sd); pthread_exit(NULL); } close(sd); int ret=check_message(id,pole,l,rCMD,val); printf("<- %s : Reply (ID: %d, IP: %s), status = %d\n",telka[id].nazov,telka[id].id,telka[id].ip,ret); if(ret==1){ show_status(command,id,val); } pthread_exit(NULL); } void send_to(void) { uint32_t i; pthread_t thr[MAX_TELKA]; struct timespec req,rem; req.tv_sec=command.wait; req.tv_nsec=333000000; for(i=0;i<telka.size();i++){ in_telka=i; pthread_mutex_lock(&main_mutex); if(pthread_create(&thr[i],NULL,main_thread,NULL)!=0){ } nanosleep(&req,&rem); } for(i=0;i<telka.size();i++){ pthread_join(thr[i], NULL); } } void usage(const char *argv0) { printf("Command-line MDC for Samsung TVs v0.1\n"); printf("Copyright (C) 2011 SAGE team s.r.o.\n"); printf("Web: http://www.sageteam.eu/\nE-mail: sage@sageteam.eu\n\n"); printf("Usage: %s SELECTION COMMAND [ARGS]\n",argv0); printf("\nSelection:\n"); printf("all\t- all monitors in config file\n"); printf("PREFIX\t- prefix of monitors in config file\n"); printf("\nCommands:\n"); for(int i=0;inputs[i].id>0;i++){ printf("%s\t- %s",inputs[i].val,inputs[i].nazov); if(inputs[i].params>0){ printf(" (%s)",inputs[i].hodnoty); } printf("\n"); } exit(1); } int check_prefix(const char *a,const char *b) { if(!strcasecmp("all",a)) return 1; int l=strlen(a); if((int)strlen(b)<l) return 0; for(int i=0;i<l;i++){ if(tolower(a[i])!=tolower(b[i])) return 0; } return 1; } int main(int argc,char *argv[]) { if(argc<3) usage(argv[0]); command.id=0; for(int i=0;inputs[i].id>0;i++){ if(!strcasecmp(argv[2],inputs[i].val)){ if(argc<inputs[i].params+3) usage(argv[0]); command.id=inputs[i].id; command.wait=inputs[i].wait; for(int j=0;j<inputs[i].params;j++){ int x; if(sscanf(argv[j+3],"%d",&x)!=1) usage(argv[0]); command.val[j]=x&0xff; } break; } } if(command.id<=0) usage(argv[0]); FILE *s; char tbuf[1024]; strcpy(tbuf,argv[0]); for(int i=strlen(tbuf)-1;i>=0;i--){ if(tbuf[i]=='/'||tbuf[i]=='\\'){ tbuf[i+1]='\0'; break; } if(i==0){ tbuf[i+1]='\0'; } } strcat(tbuf,"cmdc.conf"); s=fopen(tbuf,"r"); if(s==0){ printf("Can't read '%s'!\n",tbuf); return 1; } while(!feof(s)){ if(fgets(tbuf,sizeof(tbuf)-1,s)==NULL) break; if(tbuf[0]=='#'||tbuf[0]==';') continue; TELKA a; if(sscanf(tbuf,"%s %d %s %d %d %d",a.nazov,&a.id,a.ip,&a.wall_h,&a.wall_v,&a.wall_pos)==6){ if(check_prefix(argv[1],a.nazov)){ telka.push_back(a); } } } fclose(s); if(telka.empty()){ printf("No devices selected\n"); } else{ send_to(); } return 0; }