diff --git a/README.md b/README.md index d5622cc..14d92cb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # hs100 A tool for using TP-Link HS100/HS105/HS110 wi-fi smart plugs. You can turn -them on or off, reboot them, and factory reset them. +them on and off, reboot them, and so on. You can even set them up without +using the app (see Initial Setup). Tested to work on Linux, OSX, IRIX, and Windows under WSL. @@ -12,16 +13,53 @@ Loosely based on [pyHS100](https://github.com/GadgetReactor/pyHS100) and `hs100 ` -Command can be: -- `on`: turn the power on +Commands: +- `associate `: set wifi AP to connect to. get your +key\_type by doing a scan +- `factory-reset`: reset the plug to factory settings - `off`: turn the power off +- `on`: turn the power on - `reboot`: reboot the plug -- `reset-yes-really`: factory reset the plug - `scan`: scan for nearby wifi APs (probably only 2.4 GHz ones) +- `set_server `: set cloud server to \ instead of TP-Link's - Alternatively, you can supply a JSON string to be sent directly to the device. Note that the JSON string must be quoted, like so: `hs100 '{"system":{"set_relay_state":{"state":1}}}'` +## Initial Setup + +According to TP-Link, initial setup of the plugs is performed by installing +their "Kasa" app on your smartphone (free account required), and using its +setup tool. This sucks and I do not recommend it. Instead, follow these +alternative instructions. + +You want to get the plug into the "blinking amber and blue" state, in which +it will spin up its own AP and await commands. If you have a brand new plug, +then it should do this automatically. Otherwise, hold down one of the buttons +(depending on your model) for about 5 seconds, until its light blinks amber +and blue. + +You should see a wifi AP called "TP-Link\_Smart Plug\_XXXX" or similar. +Connect to this AP. You will be given an IP of 192.168.0.100, with the plug +at 192.168.0.1. + +Issue the following commands to the plug: +- Factory reset the plug to get rid of any settings from a previous owner: +`hs100 192.168.0.1 factory-reset`. You will be disconnected from its wifi AP. +Once the factory reset is done (usually a few seconds), reconnect to the +plug's AP. +- Disable cloud nonsense by setting a bogus server URL: `hs100 192.168.0.1 set_server localhost` +- Scan for your wifi AP using `hs100 192.168.0.1 scan`. Find your AP in the +list and note its `key_type`; you will need this to associate. +- Associate with your AP using `hs100 192.168.0.1 ` +. Your key\_type is a number that indicates the kind of wifi security that +your AP is using. You can find it by doing a wifi scan (see previous step). + +If the light turns solid amber, then it was unable to associate-- factory +reset the plug and try again. Otherwise, the light on your plug will change +first to blinking blue, then to solid blue indicating that it has successfully +connected to your AP. + ## Todo - better error checking diff --git a/hs100.c b/hs100.c index 7750c16..3d7b4ec 100644 --- a/hs100.c +++ b/hs100.c @@ -2,9 +2,69 @@ #include #include #include +#include #include "version.h" #include "comms.h" +char *handler_associate(int argc, char *argv[]) +{ + if(argc < 6) { + fprintf(stderr, "not enough arguments\n"); + exit(1); + } + char *plug_addr = argv[1]; + char *ssid = argv[3]; + char *password = argv[4]; + char *key_type = argv[5]; + + errno = 0; + char *endptr; + int key_type_num = (int)strtol(key_type, &endptr, 10); + if(errno || endptr == key_type) { + fprintf(stderr, "invalid key type: %s\n", key_type); + exit(1); + } + + const char *template = + "{\"netif\":{\"set_stainfo\":{\"ssid\":\"%s\",\"password\":" + "\"%s\",\"key_type\":%d}}}"; + + size_t len = snprintf(NULL, 0, template, ssid, password, + key_type_num); + len++; // snprintf does not count the null terminator + + char *msg = calloc(1, len); + snprintf(msg, len, template, ssid, password, key_type_num); + + char *response = hs100_send(plug_addr, msg); + + return response; +} + +char *handler_set_server(int argc, char *argv[]) +{ + if(argc < 4) { + fprintf(stderr, "not enough arguments\n"); + exit(1); + } + char *plug_addr = argv[1]; + char *server = argv[3]; + + const char *template = + "{\"cnCloud\":{\"set_server_url\":{\"server\":\"%s\"}}}"; + + size_t len = snprintf(NULL, 0, template, server); + len++; // snprintf does not count the null terminator + + char *msg = calloc(1, len); + snprintf(msg, len, template, server); + + char *response = hs100_send(plug_addr, msg); + + return response; +} + + struct cmd_s { char *command; char *help; @@ -13,11 +73,22 @@ struct cmd_s { int end; }; struct cmd_s cmds[] = { + { + .command = "associate", + .help = "associate \n" + "\t\t\tset wifi AP to connect to", + .handler = handler_associate, + }, { .command = "factory-reset", .help = "factory-reset\treset the plug to factory settings", .json = "{\"system\":{\"reset\":{\"delay\":0}}}", }, + { + .command = "info", + .help = "info\t\tget device info", + .json = "{\"system\":{\"get_sysinfo\":{}}}", + }, { .command = "off", .help = "off\t\tturn the plug on", @@ -39,6 +110,12 @@ struct cmd_s cmds[] = { " GHz ones)", .json = "{\"netif\":{\"get_scaninfo\":{\"refresh\":1}}}", }, + { + .command = "set_server", + .help = "set_server \n" + "\t\t\tset cloud server to instead of tplink's", + .handler = handler_set_server, + }, { .end = 1, }, @@ -68,29 +145,27 @@ void print_usage() int cmds_index = 0; while(!cmds[cmds_index].end) { - fprintf(stderr, "\t%s\n", cmds[cmds_index].help); + fprintf(stderr, "\t%s\n\n", cmds[cmds_index].help); cmds_index++; } - fprintf(stderr, "\n" - "Report bugs to https://github.com/jkbenaim/hs100\n" - ); + fprintf(stderr, "Report bugs to https://github.com/jkbenaim/hs100\n"); } int main(int argc, char *argv[]) { - if(argc != 3) { + if(argc < 3) { print_usage(); return 1; } char *plug_addr = argv[1]; char *cmd_string = argv[2]; - char *response; + char *response = NULL; struct cmd_s *cmd = get_cmd_from_name(cmd_string); if(cmd != NULL) { if(cmd->handler != NULL) response = cmd->handler(argc, argv); - else + else if(cmd->json != NULL) response = hs100_send(plug_addr, cmd->json); } else { // command not recognized, so send it to the plug raw