cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <html ng-app="myApp" ng-cloak>
<head>
    <meta charset="utf-8">
    <title>TP-Link Web UI</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body layout="column" style="overflow-y:hidden">
<div ng-controller="dash" style="overflow-y:auto" md-theme="custom">

    <md-toolbar>
        <div class="md-toolbar-tools">
            <h2 flex md-truncate>TP-Link Web UI</h2>
        </div>
    </md-toolbar>

    <md-tabs md-dynamic-height md-border-bottom md-selected="selected_tab_index">
        <md-tab label="Devices">
            <md-content ng-if="tpl.devices.length">
                <md-list class="md-dense" flex>
                    <md-subheader class="md-no-sticky">TP-Link Devices</md-subheader>
                    <md-list-item class="md-2-line" ng-repeat="device in tpl.devices">
                        <div class="md-list-item-text">
                            <h3>{{device.alias}}</h3>

                            <p>{{device.deviceModel}} - {{device.deviceName}}</p>
                        </div>
                        <md-switch ng-change="tpl_setState($index,device.is_powered)"
                                   ng-model="device.is_powered"></md-switch>
                        <md-divider/>
                    </md-list-item>
                </md-list>
                <md-button class="md-raised md-primary" ng-click="tpl_refreshDevices()">Refresh Devices</md-button>
            </md-content>
            <md-content ng-if="!tpl.devices.length" class="md-padding">
                <h3>Warning</h3>
                No devices were found. Please visit the 'Settings' tab to verify your credentials or request a new
                token.</h3>
            </md-content>

        </md-tab>

        <md-tab label="Settings">
            <md-content class="md-padding">
                <h3>TP-Link Authentication</h3>

                <p class="md-caption">
                    Please provide the credentials used to connect to your TP-Link account.
                </p>

                <div layout="column" layout-gt-xs="row">

                    <md-input-container flex="50">
                        <label>Username/Email</label>
                        <input required type="email" ng-model="tpl.username"
                               minlength="10" maxlength="100" ng-pattern="/^.+@.+\..+$/"/>

                        <div ng-messages="tpl.username.$error" role="alert">
                            <div ng-message-exp="['required', 'minlength', 'maxlength', 'pattern']">
                                Your email must be between 10 and 100 characters long and look like an e-mail address.
                            </div>
                        </div>
                    </md-input-container>
                    <md-input-container flex="50">
                        <label>Password</label>
                        <input required ng-model="tpl.password"/>

                        <div ng-messages="tpl.password.$error" role="alert">
                            <div ng-message-exp="['required']">
                                Password must be provided.
                            </div>
                        </div>
                    </md-input-container>
                </div>
                <div layout="column" layout-gt-xs="row">

                    <md-input-container flex="50">
                        <md-checkbox ng-model="tpl.store_credentials" aria-label="store credentials">
                            Allow this browser to store credentials.
                        </md-checkbox>
                    </md-input-container>
                    <md-input-container flex="50">
                        <md-checkbox ng-model="tpl.store_token" aria-label="store token">
                            Allow this browser to store the authentication token.
                        </md-checkbox>
                    </md-input-container>

                </div>

                <md-button class="md-button md-raised md-primary" ng-click="tpl_authenticate()">Authenticate and Refresh
                    Devices
                </md-button>

                <div ng-if="tpl.token != ''">
                    <br/>
                    <md-divider/>
                    <h3>Current Status</h3>

                    <p class="md-caption">Found {{tpl.devices.length}} devices using token: <code>{{tpl.token}}</code></p>
                </div>
            </md-content>

        </md-tab>

        <md-tab label="About">
            <md-content flex layout-padding>
                <h3>About this application</h3>

                <p>This webpage uses angular services to build a web control surface for the TP-Link series of smart
                    plugs. TP-Link's own official app (Kasa) only runs on iOS and Android devices, so those of you
                    with other tablets, mobiles, or desktops get left in the cold. This page attempts to address that.</p>

                <md-divider/>
                <div class="md-caption">
                <p>In use, the Kasa app generates a unique ID, then combines this with the registration information you
                supply at sign-up to retrieve a secure token from a TP-Link hosted API service. Requests for smart plug
                availability, status and relay states are sent with this token to protect your privacy.</p>

                <p>This application is able to generate a random UUID of its own, capture your credentials, and pass these
                to the TP-Link hosted API service to obtain a genuine token. That token can then be used to list the devices
                which are present, indicate their status, and allow you to switch them on/off from anywhere you like.</p>

                <p>Information is only sent to and from the TP-Link APIs, and is not exposed to any other server. If you
                opt to retain your login credentials and/or authentication token, these are stored in cookies within your
                browser. They are not released to me or anyone else.</p>

                <p>To access your plugs, make sure they have remote control enabled, and are registered with the
                    official Kasa App from TP-Link (<a
                    href="http://www.tp-link.com/us/home-networking/smart-home/kasa.html" target="_blank">Get
                    Kasa here</a>). Unfortunately, I don't know a way to perform the registration without (at least)
                    temporary access to the official app, but once registered you can send requests from any modern browser.</p>

                <p>The full source for this project is available for free, online at <a
                        href="https://github.com/arallsopp/tp-link-smart-switch-web-client-" target="_blank">GitHub</a>.
                </p>

                <p>I hope you are able to make use of it.</p>
                </div>

                <p>Andy Allsopp</p>

            </md-content>
        </md-tab>

    </md-tabs>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-cookies.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.4/angular-material.min.js"></script>

<script src="script.js"></script>
</body>
</html>


            
          
!
            
              var app = angular.module('myApp', ['ngMaterial','ngCookies']);

myDash.$inject = ['$scope', '$mdToast','$http','$interval','$cookies'];

angular.module('myApp').controller('dash', myDash)
    .config(['$mdThemingProvider',function($mdThemingProvider) {
        $mdThemingProvider.theme('custom').primaryPalette('blue-grey').accentPalette('deep-orange');
    }]);

function myDash($scope, $mdToast, $http, $interval, $cookies) {

    $scope.getUUID = function () {
        var d = new Date().getTime();
        if (window.performance && typeof window.performance.now === "function") {
            d += performance.now();
            //use high-precision timer if available
        }
        var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = (d + Math.random() * 16) % 16 | 0;
            d = Math.floor(d / 16);
            return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
        });
        return uuid;
    };

    $scope.showToast = function (msg) {
        $mdToast.show($mdToast.simple().textContent(msg).position('top right'))
    };

    $scope.tpl_authenticate = function () {
        $scope.tpl.UUID = $scope.getUUID();

        var auth_obj = {
            "method": "login", "params": {
                "appType": "Kasa_Android",
                "cloudUserName": $scope.tpl.username,
                "cloudPassword": $scope.tpl.password,
                "terminalUUID": $scope.tpl.UUID
            }
        };

        $http.post("https://wap.tplinkcloud.com/", auth_obj).then(function mySuccess(response) {
            var now = new Date();
            var exp = new Date(now.getFullYear()+1, now.getMonth(), now.getDate());

            $scope.tpl.token = response.data.result.token;

            $cookies.put('tpl_uuid', $scope.tpl.UUID,{expires: exp});
            if($scope.tpl.store_credentials) {
                $cookies.put('tpl_username', $scope.tpl.username,{ expires: exp});
                $cookies.put('tpl_password', $scope.tpl.password,{ expires: exp});
                $cookies.put('tpl_store_credentials',true,{ expires: exp});

            }else{
                $cookies.remove('tpl_username');
                $cookies.remove('tpl_password');
                $cookies.remove('tpl_store_credentials');
            }

            if($scope.tpl.store_token) {
                $cookies.put('tpl_token', $scope.tpl.token,{ expires: exp});
                $cookies.put('tpl_store_token',true,{ expires: exp});

            }else{
                $cookies.remove('tpl_token');
                $cookies.remove('tpl_store_token');
            }

            $scope.tpl_refreshDevices();

        }, function myError(response) {
            $scope.myWelcome = response.statusText;
        });

    };

    $scope.tpl_refreshDevices = function () {
        var request_obj = {"method": "getDeviceList"};

        $http.post("https://wap.tplinkcloud.com?token=" + $scope.tpl.token, request_obj).then(function mySuccess(response) {
            $scope.tpl.devices = (response.data.result.deviceList);
            console.log($scope.tpl.devices);
            if ($scope.tpl.devices.length) {
                for (var i = 0; i < $scope.tpl.devices.length; i++) {
                    $scope.tpl_getState(i);
                }

                $scope.selected_tab_index = 0;
            }
        });


    };

    $scope.tpl_getState = function (device_index) {
        var url = $scope.tpl.devices[device_index].appServerUrl;
        var deviceId = $scope.tpl.devices[device_index].deviceId;

        var request_obj = {
            "method": "passthrough", "params": {
                "deviceId": deviceId,
                "requestData": "{\"system\":{\"get_sysinfo\":null},\"emeter\":{\"get_realtime\":null}}"
            }
        };
        $http.post(url + "?token=" + $scope.tpl.token, request_obj).then(function mySuccess(response) {
            window.response = response;
            var testval = JSON.parse(response.data.result.responseData).system.get_sysinfo.relay_state;
            console.log(device_index, testval, response);

            $scope.tpl.devices[device_index].is_powered = (testval == true);
        });
    };

    $scope.tpl_setState = function (device_index, device_state) {
        var url = $scope.tpl.devices[device_index].appServerUrl;
        var deviceId = $scope.tpl.devices[device_index].deviceId;

        var request_obj = {
            "method": "passthrough", "params": {
                "deviceId": deviceId,
                "requestData": "{\"system\":{\"set_relay_state\":{\"state\":" + (device_state ? 1 : 0 ) + "}}}"
            }
        };
        $http.post(url + "?token=" + $scope.tpl.token, request_obj).then(function mySuccess(response) {
            window.response = response;
            console.log(response);
        });
    };

    $scope.tpl = {};
    $scope.tpl.refresh_rate = 5; //check every 5 seconds. Set this as you fancy.

    $scope.tpl.devices = [];
    $scope.tpl.store_token = $cookies.get('tpl_store_token') == "true";
    $scope.tpl.store_credentials = $cookies.get('tpl_store_credentials') == "true";
    $scope.tpl.UUID = $cookies.get('tpl_uuid');

    if($scope.tpl.store_credentials){
        $scope.tpl.username = $cookies.get('tpl_username');
        $scope.tpl.password = $cookies.get('tpl_password');
    }
    if($scope.tpl.store_token) {
        $scope.tpl.token = $cookies.get('tpl_token');
    }
    $scope.selected_tab_index = 0;


    if (typeof ($scope.tpl.token) === "undefined") {
        $scope.tpl.token = '';
    } else {
        $scope.tpl_refreshDevices();
    }

    $interval(function () {
        for (var i = 0; i < $scope.tpl.devices.length; i++) {
            $scope.tpl_getState(i);
        }
    }, $scope.tpl.refresh_rate * 1000);

}

            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console