1
0
mirror of https://github.com/taigrr/tplinkController synced 2025-01-18 04:43:13 -08:00

Add ability to do first-time plug setup

This commit is contained in:
Jason Benaim 2018-11-24 05:29:26 -08:00
parent f01121c37c
commit b66401d5a1
2 changed files with 124 additions and 11 deletions

View File

@ -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 <ip> <command>`
Command can be:
- `on`: turn the power on
Commands:
- `associate <ssid> <key> <key_type>`: 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 <url>`: set cloud server to \<url\> 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 <ip> '{"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 <ssid> <password> <key_type>`
. 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

89
hs100.c
View File

@ -2,9 +2,69 @@
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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 <ssid> <key> <key_type>\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 <url>\n"
"\t\t\tset cloud server to <url> 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