File: cmdc.cpp | Size: 13,017 bytes | Download file | Back to directory listing | BWPOW's homepage
/***********************************************************************************
    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;
}