From c1244a0c3d0b2eea77df97285cd73265d9ff7ce0 Mon Sep 17 00:00:00 2001 From: Steffen Pohle Date: Wed, 23 Feb 2022 18:26:52 +0100 Subject: [PATCH] adding light and switch control --- fbsh-cli.cc | 85 +++++++++++++++++++-- fbsh.cc | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fbsh.h | 12 ++- 3 files changed, 299 insertions(+), 9 deletions(-) diff --git a/fbsh-cli.cc b/fbsh-cli.cc index 42514b2..d79eb7c 100644 --- a/fbsh-cli.cc +++ b/fbsh-cli.cc @@ -16,14 +16,18 @@ void help() { printf (" Client for accessing the fritzbox smart home devices.\n"); printf (" Most parameters can be set in a seperate config file.\n"); printf ("Commands:\n"); - printf ("\thelp displays this helptext.\n"); - printf ("\tconnect connect to the device, it will save the SID file.\n"); - printf ("\tlist lists all devices, connection will be recovered from SID.\n"); - printf ("\t If silent parameter is set, we will not be asked for credentials\n"); - printf ("\tlisthkr shows a list of Heating Crontrols\n"); - printf ("\tgethkr AIN options reads heating control device\n"); - printf ("\t -nochart display no history chart\n"); - printf ("\tsaveconfig save configfile.\n"); + printf ("\thelp displays this helptext.\n"); + printf ("\tconnect connect to the device, it will save the SID file.\n"); + printf ("\tlist lists all devices, connection will be recovered from SID.\n"); + printf ("\t If silent parameter is set, we will not be asked for credentials\n"); + printf ("\tlisthkr shows a list of Heating Crontrols\n"); + printf ("\tgethkr AIN options reads heating control device\n"); + printf ("\t -nochart display no history chart\n"); + printf ("\tswitch AIN on/off/toggle Turns the switch on/off\n"); + printf ("\tcolor AIN hue,sat,duration Set the Color of the Light\n"); + printf ("\t hue (0..356) sat (0..255) duration (ms)\n"); + printf ("\tlevel AIN level Light level (0..255)\n"); + printf ("\tsaveconfig save configfile.\n"); printf ("\n"); printf ("Parameters:\n"); printf ("\t-config configfile to use\n"); @@ -150,6 +154,62 @@ void listhkr(int argci, int argc, char **argv) { } +void switchonoff (int argci, int argc, char **argv) { + if (argci+2 >= argc) { + fprintf (stderr, "SwitchOnOff missing parameter?\n"); + } + + char *ain = argv[++argci]; + char *onoff = argv[++argci]; + int ionoff = 0; + + if (ain == NULL) return; + if (onoff == NULL) return; + + connect(); + if (strcmp (onoff, "off") == 0) ionoff = 0; + else if (strcmp (onoff, "on") == 0) ionoff = 1; + else if (strcmp (onoff, "toggle") == 0) ionoff = 2; + fbsh.SimpleOnOff(ain, ionoff); +} + + +void lightlevel (int argci, int argc, char **argv) { + if (argci+2 >= argc) { + fprintf (stderr, "Level missing parameter?\n"); + } + + char *ain = argv[++argci]; + char *level = argv[++argci]; + + if (ain == NULL) return; + if (level == NULL) return; + + connect(); + fbsh.SetLevel(ain, atoi(level)); +} + + +void light (int argci, int argc, char **argv) { + if (argci+2 >= argc) { + fprintf (stderr, "SwitchOnOff missing parameter?\n"); + } + + char *ain = argv[++argci]; + char *color = argv[++argci]; + int ihue = 0; + int isat = 0; + int iduration = 0; + + if (ain == NULL) return; + if (color == NULL) return; + + sscanf (color, "%d,%d,%d", &ihue, &isat, &iduration); + connect(); + fbsh.SetColor(ain, ihue, isat, iduration); +} + + int main(int argc, char** argv) { int i; @@ -183,6 +243,15 @@ int main(int argc, char** argv) { else if (strcmp(argv[i], "listhkr") == 0) { listhkr(i, argc, argv); } + else if (strcmp(argv[i], "switch") == 0) { + switchonoff(i, argc, argv); + } + else if (strcmp(argv[i], "color") == 0) { + light(i, argc, argv); + } + else if (strcmp(argv[i], "level") == 0) { + lightlevel(i, argc, argv); + } // diff --git a/fbsh.cc b/fbsh.cc index 191717f..58f0ee3 100644 --- a/fbsh.cc +++ b/fbsh.cc @@ -593,4 +593,215 @@ int FBSmartHome::GetHKR(std::string ain, FBSmartHomeHKR *hkr) { } +/* + * switch device off = 0, on = 1 or toggle = 2 + */ +int FBSmartHome::SimpleOnOff(std::string ain, int oot) { + std::string s; + int i, len; + + if (ain.length() <= 0) return -1; + for (s = "", i = 0; i < (int)ain.length(); i++) { + if (ain[i] == ' ') s += "%20"; + else s += ain[i]; + } + + // + // send switch on / off + len = tcp.WebGetFile(hostname+"/webservices/homeautoswitch.lua?sid="+SID+"&switchcmd=setsimpleonoff&ain="+s+"&onoff="+to_string(oot), inbuffer, FB_BUFFER, NULL); + if (len > FB_BUFFER) len = FB_BUFFER; + else if (len < 0) { + fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__); + errno = EAI_FAIL; + return -1; + } + inbuffer[len] = '\0'; + + return 0; +} + + +/* + * set device level 0...255 (0-100%) + */ +int FBSmartHome::SetLevel(std::string ain, int level) { + std::string s; + int i, len; + + if (ain.length() <= 0) return -1; + for (s = "", i = 0; i < (int)ain.length(); i++) { + if (ain[i] == ' ') s += "%20"; + else s += ain[i]; + } + + // + // send switch on / off + len = tcp.WebGetFile(hostname+"/webservices/homeautoswitch.lua?sid="+SID+"&switchcmd=setlevel&ain="+s+"&level="+to_string(level), inbuffer, FB_BUFFER, NULL); + if (len > FB_BUFFER) len = FB_BUFFER; + else if (len < 0) { + fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__); + errno = EAI_FAIL; + return -1; + } + inbuffer[len] = '\0'; + + return 0; +} + + +std::list *FBSmartHome::GetColorDefaults (std::string ain) { + std::list *lst = new std::list; + std::string cmd; + std::string s; + int i, len; + xmlDocPtr xmldoc; + xmlNodePtr xmlnode; + xmlAttrPtr xmlattr; + + // + // convert space char to %20 to be used in URLs + // + if (ain.length() <= 0) return lst; + for (s = "", i = 0; i < (int)ain.length(); i++) { + if (ain[i] == ' ') s += "%20"; + else s += ain[i]; + } + + cmd = hostname+"/webservices/homeautoswitch.lua?sid="+SID+"&switchcmd=getcolordefaults&ain="+s; + + // + // send switch on / off + len = tcp.WebGetFile(cmd, inbuffer, FB_BUFFER, NULL); + if (len > FB_BUFFER) len = FB_BUFFER; + else if (len < 0) { + fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__); + errno = EAI_FAIL; + return lst; + } + inbuffer[len] = '\0'; + + // + // convert inbuffer to list of values + // + XMLPrepare(inbuffer, len, &xmldoc, &xmlnode); + + // + // parse for SID and Challange + while (xmlnode) { + if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"colordefaults"))){ + xmlNodePtr xmlchild = xmlnode->xmlChildrenNode; + std::string txt; + + while (xmlchild) { + // hsdefaults + if (!xmlStrcmp(xmlchild->name, (const xmlChar *)"hsdefaults")) { + xmlNodePtr xmlhs = xmlchild->children; + + while(xmlhs) { + if (!xmlStrcmp(xmlhs->name, (const xmlChar *)"hs")) { + xmlNodePtr xmlc = xmlhs->children; + + while (xmlc) { + if (!xmlStrcmp(xmlc->name, (const xmlChar *)"color")) { + xmlattr = xmlc->properties; + FBSmartHomeLightDefaults def = { -1, -1, -1 }; + while (xmlattr) { + if (!xmlStrcmp(xmlattr->name, (const xmlChar *)"hue")) + def.hue = atoi((char *) xmlNodeListGetString(xmldoc, xmlattr->children, 1)); + else if (!xmlStrcmp(xmlattr->name, (const xmlChar *)"sat")) + def.sat = atoi((char *) xmlNodeListGetString(xmldoc, xmlattr->children, 1)); + else if (!xmlStrcmp(xmlattr->name, (const xmlChar *)"val")) + def.val = atoi((char *) xmlNodeListGetString(xmldoc, xmlattr->children, 1)); + xmlattr = xmlattr->next; + } + if (def.hue != -1) lst->push_back(def); + + } + xmlc = xmlc->next; + } + } + xmlhs = xmlhs->next; + } + } + + xmlchild = xmlchild->next; + } + + } + xmlnode = xmlnode->next; + } + + return lst; +} + + +/* + * + */ +int FBSmartHome::SetColor(std::string ain, int hue, int sat, int duration) { + std::list *defcolors = NULL; + std::list::iterator iter; + std::string s; + std::string cmd; + int i, len; + float delta; + struct { + float delta; + int hue; + int sat; + int val; + } selected_color; + + // + // convert space char to %20 to be used in URLs + // + if (ain.length() <= 0) return -1; + for (s = "", i = 0; i < (int)ain.length(); i++) { + if (ain[i] == ' ') s += "%20"; + else s += ain[i]; + } + + // + // need to read defaults and round up to the next closest parameters + // + defcolors = GetColorDefaults(ain); + iter = defcolors->begin(); + if (iter != defcolors->end()) { + selected_color.hue = iter->hue; + selected_color.sat = iter->sat; + selected_color.val = iter->val; + selected_color.delta = (iter->hue - hue) * (iter->hue - hue) + + (iter->sat - sat) * (iter->hue - sat); + + for (iter++; iter != defcolors->end(); iter++) { + delta = (iter->hue - hue) * (iter->hue - hue) + + (iter->sat - sat) * (iter->hue - sat); + if (delta < selected_color.delta) { + selected_color.hue = iter->hue; + selected_color.sat = iter->sat; + selected_color.val = iter->val; + selected_color.delta = delta; + } + } + + cmd = hostname+"/webservices/homeautoswitch.lua?sid="+SID+"&switchcmd=setcolor&ain="+s; + cmd += "&hue="+to_string(selected_color.hue)+"&saturation="+to_string(selected_color.sat)+"&duration="+to_string(duration); + + printf ("%s:%d SetColor ain:'%s' hue,sat %d,%d selected hue,sat: %d,%d duration:%d\n", __FILE__, __LINE__, ain.c_str(), + hue, sat, selected_color.hue, selected_color.sat, duration); + + // + // send switch on / off + len = tcp.WebGetFile(cmd, inbuffer, FB_BUFFER, NULL); + if (len > FB_BUFFER) len = FB_BUFFER; + else if (len < 0) { + fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__); + errno = EAI_FAIL; + return -1; + } + inbuffer[len] = '\0'; + } + return 0; +} + diff --git a/fbsh.h b/fbsh.h index ac03669..7493e1c 100644 --- a/fbsh.h +++ b/fbsh.h @@ -24,6 +24,13 @@ struct { } typedef FBSmartHomeDevice; +struct { + int hue; + int sat; + int val; +} typedef FBSmartHomeLightDefaults; + + struct { std::string id; std::string name; @@ -66,7 +73,10 @@ public: int Connect(std::string host); // return 0 on success int Connect(std::string host, std::string username, std::string password); int GetHKR(std::string ain, FBSmartHomeHKR *hkr); - + int SimpleOnOff(std::string ain, int oot); // oot 0=off, 1=on, 2=toggle + int SetLevel(std::string ain, int level); // level 0...255 + int SetColor(std::string ain, int hue, int sat, int duration); + std::list *GetColorDefaults (std::string ain); std::list *GetDevices(); };