Compare commits

...

4 Commits

4 changed files with 99 additions and 14 deletions

9
Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM scratch
COPY wemo /
COPY --chown=65534:65534 movieMode.json /tmp/
WORKDIR /tmp
USER 65534
EXPOSE 8081
ENTRYPOINT ["/wemo"]

View File

@ -429,10 +429,10 @@ FAUXMOS = [
[ [
'Movie Mode', 'Movie Mode',
rest_api_handler( rest_api_handler(
'http://movielights:20000/basement?moviemode=true', 'http://movielights:10000/basement?moviemode=true',
'http://movielights:20000/basement?moviemode=false' 'http://movielights:10000/basement?moviemode=false'
), ),
20000 10000
], ],
] ]

View File

@ -7,19 +7,26 @@
"content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState><brightness>${val}</brightness></u:SetBinaryState></s:Body></s:Envelope>", "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState><brightness>${val}</brightness></u:SetBinaryState></s:Body></s:Envelope>",
"steps": 15, "steps": 15,
"seconds": 5, "seconds": 5,
"start": 100, "start": -1,
"end": 20, "end": 20,
"endOff": true "endOff": true,
"startAction": "\"urn:Belkin:service:basicevent:1#GetBinaryState\"",
"startContent": "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:GetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"></u:GetBinaryState></s:Body></s:Envelope>",
"startField": "brightness"
}, },
{ {
"device": "Bar", "device": "Bar",
"comment": "Bar lights down to 10%", "comment": "Bar lights down to 10%, then off",
"action": "\"urn:Belkin:service:basicevent:1#SetBinaryState\"", "action": "\"urn:Belkin:service:basicevent:1#SetBinaryState\"",
"content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState><brightness>${val}</brightness></u:SetBinaryState></s:Body></s:Envelope>", "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>1</BinaryState><brightness>${val}</brightness></u:SetBinaryState></s:Body></s:Envelope>",
"steps": 15, "steps": 15,
"seconds": 5, "seconds": 5,
"start": 100, "start": -1,
"end": 0 "end": 0,
"endOff": true,
"startAction": "\"urn:Belkin:service:basicevent:1#GetBinaryState\"",
"startContent": "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:GetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"></u:GetBinaryState></s:Body></s:Envelope>",
"startField": "brightness"
} }
], ],
"off": [ "off": [
@ -31,7 +38,7 @@
"steps": 15, "steps": 15,
"seconds": 5, "seconds": 5,
"start": 20, "start": 20,
"end": 100 "end": 60
}, },
{ {
"device": "Bar", "device": "Bar",

79
wemo.go
View File

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"encoding/xml"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
@ -17,6 +18,7 @@ import (
"git.lerch.org/lobo/wemo/wemodiscovery" "git.lerch.org/lobo/wemo/wemodiscovery"
) )
type basementPost struct { type basementPost struct {
MovieMode bool MovieMode bool
} }
@ -31,6 +33,7 @@ type controlData struct {
type deviceAction struct { type deviceAction struct {
Device, Action, Content string Device, Action, Content string
StartAction, StartContent, StartField string
Steps, Seconds, Start, End int Steps, Seconds, Start, End int
EndOff bool EndOff bool
} }
@ -39,10 +42,28 @@ type soapActions struct {
On, Off []deviceAction On, Off []deviceAction
} }
type soapResponse struct {
XMLName xml.Name `xml:"Envelope"`
Body getBinaryStateBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}
type getBinaryStateBody struct {
XMLName xml.Name
GetBinaryStateResponse getBinaryStateBodyResponse `xml:"urn:Belkin:service:basicevent:1 GetBinaryStateResponse"`
}
type getBinaryStateBodyResponse struct {
XMLName xml.Name
State bool `xml:"BinaryState"`
Brightness int `xml:"brightness"`
Fader string `xml:"fader"`
}
var command string var command string
var client http.Client var client http.Client
func main() { func main() {
getState := "\"urn:Belkin:service:basicevent:1#GetBinaryState\""
getStateContent := "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:GetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"></u:GetBinaryState></s:Body></s:Envelope>"
command = os.Getenv("CMD") command = os.Getenv("CMD")
port := os.Getenv("PORT") port := os.Getenv("PORT")
if port == "" { port = ":8081" } else { port = ":" + port } if port == "" { port = ":8081" } else { port = ":" + port }
@ -50,10 +71,25 @@ func main() {
if len(os.Args) > 1 { if len(os.Args) > 1 {
if os.Args[1] == "on" { if os.Args[1] == "on" {
movieMode(true) movieMode(true)
}else{ time.Sleep(10 * time.Second) // Allow the thread time to do magic
}else if os.Args[1] == "off" {
movieMode(false) movieMode(false)
time.Sleep(10 * time.Second) // Allow the thread time to do magic
}else if os.Args[1] == "scan" {
refreshControl()
println("control file updated")
}else if os.Args[1] == "getState" {
logger.Infof("getState")
addresses := readAddresses()
for _, address := range addresses.Devices {
if address.Name == os.Args[2] {
dim := getCurrentDimValue(address.Url+addresses.BasicEvent, address.Name, getState, getStateContent)
println("dim value is ", dim)
}
}
}else{
println("wemo [on|off|scan|getState] [device name]")
} }
time.Sleep(10 * time.Second)
// movieMode(false) // movieMode(false)
os.Exit(0) os.Exit(0)
} }
@ -131,13 +167,17 @@ func commandDevice(url string, device deviceAction) {
sendCommand(url, device, device.Content) sendCommand(url, device, device.Content)
return return
} }
logger.Tracef("%s: Stepping the change. %d to %d over %d steps", device.Device, device.Start, device.End, device.Steps) start := device.Start
if start == -1 {
start = getCurrentDimValue(url, device.Device, device.StartAction, device.StartContent)
}
logger.Tracef("%s: Stepping the change. %d to %d over %d steps", device.Device, start, device.End, device.Steps)
// We want to fade something... // We want to fade something...
millisecondsPerTick := device.Seconds * 1000 / device.Steps millisecondsPerTick := device.Seconds * 1000 / device.Steps
logger.Tracef("%s: %d ms per step over %d seconds", device.Device, millisecondsPerTick, device.Seconds) logger.Tracef("%s: %d ms per step over %d seconds", device.Device, millisecondsPerTick, device.Seconds)
deltaPerTick := (device.End - device.Start) / (device.Steps - 1) deltaPerTick := (device.End - start) / (device.Steps - 1)
logger.Tracef("%s: %d change per command", device.Device, deltaPerTick) logger.Tracef("%s: %d change per command", device.Device, deltaPerTick)
currentValue := device.Start currentValue := start
currentSteps := 0 currentSteps := 0
ticker := time.NewTicker(time.Duration(millisecondsPerTick) * time.Millisecond) ticker := time.NewTicker(time.Duration(millisecondsPerTick) * time.Millisecond)
quit := make(chan struct{}) quit := make(chan struct{})
@ -192,6 +232,35 @@ func sendCommand(url string, device deviceAction, content string) {
} }
} }
func getCurrentDimValue(url string, name string, action string, content string) int {
req, err := http.NewRequest("POST", url, nil)
rc := 0
if err != nil {
logger.Errorf("Error building http request to device %s: %s", name, err)
}
req.Header.Add("SOAPACTION", action)
req.Header.Add("Content-type", `text/xml; charset="utf-8"`)
req.Body = ioutil.NopCloser(bytes.NewBufferString(content))
res, err := client.Do(req)
if err != nil {
logger.Errorf("Error on http request to device %s: %s", name, err)
}else{
responseBytes, err := ioutil.ReadAll(res.Body)
logger.Tracef("%s: Response from lights: %s", name, responseBytes)
if err != nil {
logger.Errorf("Error reading response from device %s: %s", name, err)
}
var envelope soapResponse
xml.Unmarshal(responseBytes, &envelope)
rc = envelope.Body.GetBinaryStateResponse.Brightness
logger.Tracef("%s: Unmarshal: #v", name, envelope)
logger.Tracef("%s: Brightness: %d", name, rc)
logger.Tracef("%s: State: %t", name, envelope.Body.GetBinaryStateResponse.State)
logger.Tracef("%s: Fader: %s", name, envelope.Body.GetBinaryStateResponse.Fader)
}
return rc
}
func readAddresses() controlData { func readAddresses() controlData {
var rc controlData var rc controlData
bytes, err := ioutil.ReadFile("controlData.json") bytes, err := ioutil.ReadFile("controlData.json")