Compare commits
3 Commits
5945effb3c
...
4609c0b583
Author | SHA1 | Date | |
---|---|---|---|
4609c0b583 | |||
beda73ea3b | |||
3b5f835f11 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
wemo
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
let Wemo = require('wemo-client');
|
|
||||||
let wemo = new Wemo();
|
|
||||||
|
|
||||||
wemo.discover(function(err, deviceInfo) {
|
|
||||||
console.log('Wemo Device Found: %j', deviceInfo);
|
|
||||||
|
|
||||||
// Get the client for the found device
|
|
||||||
let client = wemo.client(deviceInfo);
|
|
||||||
|
|
||||||
// You definitely want to listen to error events (e.g. device went offline),
|
|
||||||
// Node will throw them as an exception if they are left unhandled
|
|
||||||
client.on('error', function(err) {
|
|
||||||
console.log('Error: %s', err.code);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle BinaryState events
|
|
||||||
client.on('binaryState', function(value) {
|
|
||||||
console.log('Binary State changed to: %s', value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Turn the switch on
|
|
||||||
//client.setBinaryState(1);
|
|
||||||
});
|
|
120
diag/package-lock.json
generated
120
diag/package-lock.json
generated
|
@ -1,120 +0,0 @@
|
||||||
{
|
|
||||||
"name": "wemo",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
|
||||||
"dependencies": {
|
|
||||||
"async": {
|
|
||||||
"version": "2.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
|
||||||
"requires": {
|
|
||||||
"lodash": "^4.17.14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bluebird": {
|
|
||||||
"version": "3.5.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
|
|
||||||
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w=="
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"version": "2.6.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"entities": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
|
|
||||||
},
|
|
||||||
"extend": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
|
||||||
},
|
|
||||||
"ip": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
|
||||||
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
|
|
||||||
},
|
|
||||||
"lodash": {
|
|
||||||
"version": "4.17.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz",
|
|
||||||
"integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw=="
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
|
||||||
},
|
|
||||||
"node-ssdp": {
|
|
||||||
"version": "3.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-ssdp/-/node-ssdp-3.3.0.tgz",
|
|
||||||
"integrity": "sha512-hFBkfUJytKC2x64jljojAbktG8aOL0C1YuNjCK54ZGBBg2382J3oTuK17T+aFgmy47noKHE5arLnYppo0JjcLw==",
|
|
||||||
"requires": {
|
|
||||||
"async": "^2.6.0",
|
|
||||||
"bluebird": "^3.5.1",
|
|
||||||
"debug": "^3.1.0",
|
|
||||||
"extend": "^3.0.1",
|
|
||||||
"ip": "^1.1.5"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"debug": {
|
|
||||||
"version": "3.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
|
||||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "^2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sax": {
|
|
||||||
"version": "1.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
|
||||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
|
||||||
},
|
|
||||||
"wemo-client": {
|
|
||||||
"version": "0.15.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/wemo-client/-/wemo-client-0.15.0.tgz",
|
|
||||||
"integrity": "sha512-zf266rvxXMtC7y9nR1Xu/yeYQCGgUPjYRQ1E4LZPxNiC5csDLIZBFklpUU3alcxSr+vqUpVtLDZz/iWiLX8sVw==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "^2.6.9",
|
|
||||||
"entities": "^1.1.1",
|
|
||||||
"ip": "^1.1.5",
|
|
||||||
"node-ssdp": "^3.2.5",
|
|
||||||
"xml2js": "^0.4.19",
|
|
||||||
"xmlbuilder": "^8.2.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xml2js": {
|
|
||||||
"version": "0.4.19",
|
|
||||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
|
||||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
|
||||||
"requires": {
|
|
||||||
"sax": ">=0.6.0",
|
|
||||||
"xmlbuilder": "~9.0.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"xmlbuilder": {
|
|
||||||
"version": "9.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
|
||||||
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xmlbuilder": {
|
|
||||||
"version": "8.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
|
|
||||||
"integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"name": "wemo",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Movie mode for downstairs",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"wemo-client": "^0.15.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,7 +42,7 @@ import uuid
|
||||||
SETUP_XML = """<?xml version="1.0"?>
|
SETUP_XML = """<?xml version="1.0"?>
|
||||||
<root>
|
<root>
|
||||||
<device>
|
<device>
|
||||||
<deviceType>urn:MakerMusings:device:controllee:1</deviceType>
|
<deviceType>urn:Belkin:device:controllee:1</deviceType>
|
||||||
<friendlyName>%(device_name)s</friendlyName>
|
<friendlyName>%(device_name)s</friendlyName>
|
||||||
<manufacturer>Belkin International Inc.</manufacturer>
|
<manufacturer>Belkin International Inc.</manufacturer>
|
||||||
<modelName>Emulated Socket</modelName>
|
<modelName>Emulated Socket</modelName>
|
||||||
|
@ -359,7 +359,12 @@ class upnp_broadcast_responder(object):
|
||||||
def do_read(self, fileno):
|
def do_read(self, fileno):
|
||||||
data, sender = self.recvfrom(1024)
|
data, sender = self.recvfrom(1024)
|
||||||
if data:
|
if data:
|
||||||
if data.find('M-SEARCH') == 0 and data.find('urn:Belkin:device:**') != -1:
|
if data.find('M-SEARCH') == 0 and (
|
||||||
|
data.find('urn:Belkin:device:**') != -1 or
|
||||||
|
data.find('ssdp:all') != -1 or
|
||||||
|
data.find('urn:Belkin:device:controllee:1') != -1
|
||||||
|
):
|
||||||
|
|
||||||
for device in self.devices:
|
for device in self.devices:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
device.respond_to_search(sender, 'urn:Belkin:device:**')
|
device.respond_to_search(sender, 'urn:Belkin:device:**')
|
||||||
|
|
13
go.mod
Normal file
13
go.mod
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module git.lerch.org/lobo/wemo
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.lerch.org/lobo/wemo/wemodiscovery v0.0.0
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
|
||||||
|
golang.org/x/text v0.3.2 // indirect
|
||||||
|
golang.org/x/tools v0.0.0-20190723021737-8bb11ff117ca // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace git.lerch.org/lobo/wemo/wemodiscovery v0.0.0 => ./wemodiscovery
|
26
go.sum
Normal file
26
go.sum
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
github.com/fromkeith/gossdp v0.0.0-20180102154144-1b2c43f6886e h1:cG4ivpkHpkmWTaaLrgekDVR0xAr87V697T2c+WnUdiY=
|
||||||
|
github.com/fromkeith/gossdp v0.0.0-20180102154144-1b2c43f6886e/go.mod h1:7xQpS/YtlWo38XfIqje9GgtlPuBRatYcL23GlYBtgWM=
|
||||||
|
github.com/go-home-iot/gossdp v0.0.0-20160327224030-49870f3db38d h1:ebIOreKlyZrQkj2SV6m6f6H9A6T7UcHY8YAfuYvY4Hc=
|
||||||
|
github.com/go-home-iot/gossdp v0.0.0-20160327224030-49870f3db38d/go.mod h1:nUnTkSnQ3tPjjZaxpQJU7boXxTla6IJGT/1jUZrFtfw=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
|
||||||
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190723021737-8bb11ff117ca/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
|
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
|
||||||
|
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
|
||||||
|
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
90
wemo.go
Normal file
90
wemo.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.lerch.org/lobo/wemo/wemodiscovery"
|
||||||
|
)
|
||||||
|
|
||||||
|
type basementPost struct {
|
||||||
|
MovieMode bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type controlData struct {
|
||||||
|
Bar, Basement, Jack string
|
||||||
|
BasicEvent string
|
||||||
|
}
|
||||||
|
|
||||||
|
var command string
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
command = os.Getenv("CMD")
|
||||||
|
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
if os.Args[1] == "scan" {
|
||||||
|
scan()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
movieMode(true)
|
||||||
|
time.Sleep(60 * time.Second)
|
||||||
|
movieMode(false)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
// POST /basement { movieMode: true } OR { movieMode: false }
|
||||||
|
http.HandleFunc("/basement", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "POST" {
|
||||||
|
http.Error(w, "Not found", 404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
postBodyBytes, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not read body", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var postBody basementPost
|
||||||
|
json.Unmarshal(postBodyBytes, &postBody)
|
||||||
|
fmt.Fprintf(w, "MovieMode: %t", postBody.MovieMode)
|
||||||
|
movieMode(postBody.MovieMode)
|
||||||
|
})
|
||||||
|
|
||||||
|
http.HandleFunc("*", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Error(w, "Not found", 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Fatal(http.ListenAndServe(":8081", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func movieMode(desiredState bool) {
|
||||||
|
fmt.Fprintf(os.Stdout, "setting movieMode: %t", desiredState)
|
||||||
|
// addresses := readAddresses()
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAddresses() controlData {
|
||||||
|
var rc controlData
|
||||||
|
bytes, err := ioutil.ReadFile("controlData.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "could not read controlData.json: %s", err)
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
json.Unmarshal(bytes, &rc)
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
|
||||||
|
func scan() {
|
||||||
|
devices, err := wemodiscovery.Scan(wemodiscovery.DTAllBelkin, 2)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error during scan: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, device := range devices {
|
||||||
|
device.Load(1 * time.Second)
|
||||||
|
fmt.Fprintf(os.Stdout, "Device %s: %s %s %s\n", device.Scan.DeviceId, device.Scan.Location, device.FriendlyName, device.Scan.Urn)
|
||||||
|
}
|
||||||
|
}
|
42
wemodiscovery/device.go
Normal file
42
wemodiscovery/device.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package wemodiscovery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fromkeith/gossdp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Device contains information about a device that has been found on the network
|
||||||
|
type Device struct {
|
||||||
|
Scan gossdp.ResponseMessage
|
||||||
|
DeviceType string `xml:"deviceType"`
|
||||||
|
FriendlyName string `xml:"friendlyName"`
|
||||||
|
Manufacturer string `xml:"manufacturer"`
|
||||||
|
ManufacturerURL string `xml:"manufacturerURL"`
|
||||||
|
ModelDescription string `xml:"modelDescription"`
|
||||||
|
ModelName string `xml:"modelName"`
|
||||||
|
ModelNumber string `xml:"modelNumber"`
|
||||||
|
ModelURL string `xml:"modelURL"`
|
||||||
|
SerialNumber string `xml:"serialNumber"`
|
||||||
|
UDN string `xml:"UDN"`
|
||||||
|
UPC string `xml:"UPC"`
|
||||||
|
MACAddress string `xml:"macAddress"`
|
||||||
|
FirmwareVersion string `xml:"firmwareVersion"`
|
||||||
|
IconVersion string `xml:"iconVersion"`
|
||||||
|
BinaryState int `xml:"binaryState"`
|
||||||
|
ServiceList []Service `xml:"serviceList>service"`
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service contains information about a service exposed by the Belkin device
|
||||||
|
type Service struct {
|
||||||
|
ServiceType string `xml:"serviceType"`
|
||||||
|
ServiceID string `xml:"serviceId"`
|
||||||
|
ControlURL string `xml:"controlURL"`
|
||||||
|
EventSubURL string `xml:"eventSubURL"`
|
||||||
|
SCPDURL string `xml:"SCPDURL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type root struct {
|
||||||
|
Device *Device `xml:"device"`
|
||||||
|
}
|
34
wemodiscovery/device_type.go
Normal file
34
wemodiscovery/device_type.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package wemodiscovery
|
||||||
|
|
||||||
|
// DeviceType represents an identifier for the type of Belkin device you want to
|
||||||
|
// scan the network for
|
||||||
|
type DeviceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DTBridge - belkin bridge
|
||||||
|
DTBridge DeviceType = "urn:Belkin:device:bridge:1"
|
||||||
|
|
||||||
|
// DTSwitch - belkin switch
|
||||||
|
DTSwitch = "urn:Belkin:device:controllee:1"
|
||||||
|
|
||||||
|
// DTMotion - belkin motion sensor
|
||||||
|
DTMotion = "urn:Belkin:device:sensor:1"
|
||||||
|
|
||||||
|
// DTMaker - belkin maker
|
||||||
|
DTMaker = "urn:Belkin:device:Maker:1"
|
||||||
|
|
||||||
|
// DTInsight - belkin insight
|
||||||
|
DTInsight = "urn:Belkin:device:insight:1"
|
||||||
|
|
||||||
|
// DTLightSwitch - belkin light switch
|
||||||
|
DTLightSwitch = "urn:Belkin:device:lightswitch:1"
|
||||||
|
|
||||||
|
// DTDimmer - belkin dimmer
|
||||||
|
DTDimmer = "urn:Belkin:device:dimmer:1"
|
||||||
|
|
||||||
|
// Get everything
|
||||||
|
DTAll = "ssdp:all"
|
||||||
|
|
||||||
|
// Get everything
|
||||||
|
DTAllBelkin = "urn:Belkin:device:**"
|
||||||
|
)
|
8
wemodiscovery/go.mod
Normal file
8
wemodiscovery/go.mod
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module git.lerch.org/lobo/wemo/wemodiscovery
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fromkeith/gossdp v0.0.0-20180102154144-1b2c43f6886e
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
|
||||||
|
)
|
8
wemodiscovery/go.sum
Normal file
8
wemodiscovery/go.sum
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
github.com/fromkeith/gossdp v0.0.0-20180102154144-1b2c43f6886e h1:cG4ivpkHpkmWTaaLrgekDVR0xAr87V697T2c+WnUdiY=
|
||||||
|
github.com/fromkeith/gossdp v0.0.0-20180102154144-1b2c43f6886e/go.mod h1:7xQpS/YtlWo38XfIqje9GgtlPuBRatYcL23GlYBtgWM=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
75
wemodiscovery/scan.go
Normal file
75
wemodiscovery/scan.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package wemodiscovery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fromkeith/gossdp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var responses []gossdp.ResponseMessage
|
||||||
|
|
||||||
|
type belkinListener struct {
|
||||||
|
// Response func(message gossdp.ResponseMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l belkinListener) Response(message gossdp.ResponseMessage) {
|
||||||
|
responses = append(responses, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan detects Belkin devices on the network. The devices that are returned have
|
||||||
|
// limited information in the Scan field, to get more detailed information you will
|
||||||
|
// have to call Load() on the device
|
||||||
|
func Scan(dt DeviceType, waitTimeSeconds int) ([]*Device, error) {
|
||||||
|
responses = []gossdp.ResponseMessage{}
|
||||||
|
l := belkinListener{}
|
||||||
|
|
||||||
|
c, err := gossdp.NewSsdpClientWithLogger(l, gossdp.DefaultLogger{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to start ssdp discovery client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer c.Stop()
|
||||||
|
go c.Start()
|
||||||
|
err = c.ListenFor(string(dt))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("discovery failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(waitTimeSeconds) * time.Second)
|
||||||
|
|
||||||
|
devices := make([]*Device, len(responses))
|
||||||
|
for i, response := range responses {
|
||||||
|
devices[i] = &Device{Scan: response}
|
||||||
|
}
|
||||||
|
return devices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load fetches all of the device specific information and updates the calling struct. The timeout
|
||||||
|
// parameter specifies how long to wait to connect and get a response before giving up
|
||||||
|
func (d *Device) Load(timeout time.Duration) error {
|
||||||
|
client := http.Client{Timeout: timeout}
|
||||||
|
resp, err := client.Get(d.Scan.Location)
|
||||||
|
if resp != nil {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error fetching device info: %s", err)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading response from device: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var root root
|
||||||
|
root.Device = d
|
||||||
|
err = xml.Unmarshal(b, &root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user