Skip to content

Commit

Permalink
initial commit of docker repo
Browse files Browse the repository at this point in the history
*adapted from danlogan9 gist https://gist.github.com/danlogan9/66b8ed43c1cb372e3811756a737397b2.
* Updated the version dependency on hs-100 api 0.2.0
  • Loading branch information
Aaron Paquette committed Dec 2, 2016
1 parent 58b4184 commit c1ff0d4
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 0 deletions.
27 changes: 27 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#1. put the files in a folder called hs-100
#2. sudo docker build -t hs-100:latest hs-100/.
#3. run the container
#4. sudo docker run -d -p 8083:8083 -v /etc/localtime:/etc/localtime:ro --restart=always --name hs-100 hs-100:latest
# or
#4. docker run -t -i -p 8083:8083 --entrypoint=/bin/bash --name hs-100 hs-100:latest

FROM phusion/baseimage:latest

CMD ["/sbin/my_init"]

RUN apt-get -y update
RUN apt-get install -y nodejs npm
RUN apt-get install -y nano


RUN npm install -g n
RUN n stable

COPY . /hs-100
RUN cd /hs-100; npm install


EXPOSE 8083

CMD ["node", "/hs-100/hs100.js"]

61 changes: 61 additions & 0 deletions hs100.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* hs100.js
*
* Copyright 2016 Dan Logan
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/

var sock = require('net');
var http = require('http');
var url = require('url');
var hs100api = require('hs100-api');

const PORT = 8083;

var server = http.createServer(onRequest);
server.listen(PORT);
console.log("The HS-100 controller has started");

function onRequest(request, response){
//var pathname = url.parse(request.url).pathname;
var command = request.headers["x-hs100-command"];
var deviceIP = request.headers["x-hs100-ip"];
var hs100 = new hs100api({host:deviceIP});
var msg = '';
switch(command) {
case "on":
console.log("on");
msg = 'you turned ' + deviceIP + ' on';
hs100.setPowerState(true);
response.end(msg);
break;
case "off":
console.log("off");
msg = 'you turned ' + deviceIP + ' off';
hs100.setPowerState(false);
response.end(msg);
break;
case "status":
console.log("status");
console.log(deviceIP + ":" + command);
var p = Promise.resolve(hs100.getPowerState());
p.then(function(data){
var state = ((data) ? "on" : "off");
msg = "you checked " + deviceIP + " status:" + state;
response.setHeader("x-hs100-status", state);
response.end(msg);
});
break;
default:
response.end('hs100');
}
}
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "hs-100-api-bridge",
"version": "1.0.0",
"author": "none",
"scripts": {
},
"dependencies": {
"hs100-api":"^0.2.0"
},
"private":true
}
154 changes: 154 additions & 0 deletions tplink-hs-100.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/**
* tplink-hs-100
*
* Copyright 2016 Dan Logan
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/

metadata {
definition (name: "tplink-hs-100", namespace: "danlogan9", author: "Dan Logan") {
capability "Switch"
capability "Refresh"
capability "Polling"
}

simulator {
// TODO: define status and reply messages here
}

tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: 'Off', action: "switch.on",
icon: "st.switches.switch.off", backgroundColor: "#ffffff"
state "on", label: 'On', action: "switch.off",
icon: "st.switches.switch.on", backgroundColor: "#79b821"
}

standardTile("refresh", "capability.refresh", width: 1, height: 1, decoration: "flat") {
state ("default", label:"Refresh", action:"refresh.refresh", icon:"st.secondary.refresh")
}

main("switch")
details(["switch"])
}

command "on"
command "off"

}

preferences {
input("outletIP", "text", title: "Outlet IP", required: true, displayDuringSetup: true)
input("gatewayIP", "text", title: "Gateway IP", required: true, displayDuringSetup: true)
input("gatewayPort", "text", title: "Gateway Port", required: true, displayDuringSetup: true)
}

def message(msg){
log.debug(msg)
}

// parse events into attributes
def parse(String description) {
//message("Parsing '${description}'")
log.debug "parsing"
def msg = parseLanMessage(description)

def headersAsString = msg.header // => headers as a string
def headerMap = msg.headers // => headers as a Map
def body = msg.body // => request body as a string
def status = msg.status // => http status code of the response
def json = msg.json // => any JSON included in response body, as a data structure of lists and maps
def xml = msg.xml // => any XML included in response body, as a document tree structure
def data = msg.data // => either JSON or XML in response body (whichever is specified by content-type header in response)
//message(msg.body)
//message(headerAsString)

//status = headerMap["x-hs100-status"] ?: ""
//message("switch status: '${status}'")
//if (status != "") {
// sendEvent(name: "switch", value: status, isStateChange: true)
//}

//def uuid = UUID.randomUUID().toString()
//device.deviceNetworkId = "tp_link_${uuid}"
}

def refresh(){
executeCommand("status")
}

// handle commands
def on() {
message("Executing 'on'")
executeCommand("on")
sendEvent(name: "switch", value: "on", isStateChange: true)
}

def off() {
message("Executing 'off'")
executeCommand("off")
sendEvent(name: "switch", value: "off", isStateChange: true)
}

def hubActionResponse(response){
message("Executing 'hubActionResponse': '${device.deviceNetworkId}'")
//message(response)

def status = response.headers["x-hs100-status"] ?: ""
message("switch status: '${status}'")
if (status != "") {
sendEvent(name: "switch", value: status, isStateChange: true)
}

}

def poll(){
executeCommand("status")
}

private executeCommand(command){

def gatewayIPHex = convertIPtoHex(gatewayIP)
def gatewayPortHex = convertPortToHex(gatewayPort)
//device.deviceNetworkId = "$gatewayIPHex:$gatewayPortHex"
message (device.deviceNetworkId)
message ("gateway port: $gatewayIP:$gatewayPort")

def headers = [:]
headers.put("HOST", "$gatewayIP:$gatewayPort")
headers.put("x-hs100-ip", outletIP)
headers.put("x-hs100-command", command)

//message("x-hs100-ip: '$outletIP'")
//message("executeCommand: '${command}'")
try {
sendHubCommand(new physicalgraph.device.HubAction([
method: "GET",
path: "/",
headers: headers],
device.deviceNetworkId,
[callback: "hubActionResponse"]
))
} catch (e) {
message(e.message)
}
}

private String convertIPtoHex(ipAddress) {
String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
return hex
}

private String convertPortToHex(port) {
String hexport = port.toString().format( '%04x', port.toInteger() )
return hexport
}

0 comments on commit c1ff0d4

Please sign in to comment.