-
Notifications
You must be signed in to change notification settings - Fork 2
/
http-app-protocol.coffee
112 lines (101 loc) · 3.99 KB
/
http-app-protocol.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# HttpAppProtocol class
module.exports = (env) ->
Promise = env.require 'bluebird'
rest = require('restler-promise')(Promise)
http = require 'http'
parseXmlString = Promise.promisify require('xml2js').parseString
retry = require('promise-retryer')(Promise)
commons = require('pimatic-plugin-commons')(env)
# ###DenonAvrPlugin class
class HttpAppProtocol extends require('events').EventEmitter
constructor: (@config) ->
@scheduledUpdates = {}
@host = @config.host
@port = @config.port || 80
@debug = @config.debug || false
@opts =
agent: new http.Agent()
@base = commons.base @, 'HttpAppProtocol'
@on "newListener", =>
@base.debug "Status response event listeners: #{1 + @listenerCount 'response'}"
pause: (ms=50) ->
@base.debug "Pausing:", ms, "ms"
Promise.delay ms
_mapZoneToUrlPath: (command) ->
switch command[...2]
when 'Z2' then return 'formZone2_Zone2'
when 'Z3' then return 'formZone3_Zone3'
else return 'formMainZone_MainZone'
_mapZoneToCommandPrefix: (command) ->
switch command[...2]
when 'Z2' then return 'Z2'
when 'Z3' then return 'Z3'
else return ''
_mapZoneToObjectKey: (command) ->
switch command[...2]
when 'Z2' then return 'zone2'
when 'Z3' then return 'zone3'
else return 'main'
_triggerResponse: (command, param) ->
# emulate the regex matcher of telnet transport - should be refactored
@emit 'response',
matchedResults: [
"#{command}#{param}"
"#{command}",
"#{param}"
index: 0
input: "#{command}#{param}"
]
command: command
param: param
message: "#{command}#{param}"
_requestUpdate: (command, param="") =>
url = "http://#{@host}:#{@port}/goform/#{@_mapZoneToUrlPath command}XmlStatusLite.xml"
@base.debug url
return rest.get url, @opts
.then (response) =>
if response.data.length isnt 0
@base.debug response.data
parseXmlString response.data
.then (dom) =>
prefix = @_mapZoneToCommandPrefix command
@_triggerResponse "#{prefix}MU", dom.item.Mute[0].value[0].toUpperCase()
@_triggerResponse "#{prefix}PW", dom.item.Power[0].value[0].toUpperCase()
volume = parseInt(dom.item.MasterVolume[0].value[0], 10)
if not isNaN volume
if dom.item.VolumeDisplay[0].value[0].toUpperCase() is 'ABSOLUTE'
volume += 80
@_triggerResponse "#{prefix}MV", volume
@_triggerResponse "#{prefix}SI", dom.item.InputFuncSelect[0].value[0].toUpperCase()
else
throw new Error "Empty result received for status request"
.catch (err) =>
@base.error err
.finally =>
if @scheduledUpdates[@_mapZoneToObjectKey command]?
delete @scheduledUpdates[@_mapZoneToObjectKey command]
_scheduleUpdate: (command, param="", immediate) ->
timeout=1500
if not @scheduledUpdates[@_mapZoneToObjectKey command]?
@base.debug "Scheduling update for zone #{@_mapZoneToObjectKey command}"
@scheduledUpdates[@_mapZoneToObjectKey command] = true
timeout=0 if immediate
else
@base.debug "Re-scheduling update for zone #{@_mapZoneToObjectKey command}"
@base.cancelUpdate()
@base.scheduleUpdate @_requestUpdate, timeout, command, param
return Promise.resolve()
sendRequest: (command, param="", immediate=false) ->
return new Promise (resolve, reject) =>
if param isnt '?'
url = "http://#{@host}:#{@port}/goform/formiPhoneAppDirect.xml?#{command}#{param}"
@base.debug url
promise = rest.get url, @opts
.then =>
@_triggerResponse command, param
else
promise = @_scheduleUpdate command, param, immediate
promise.then =>
resolve()
.catch (errorResult) =>
reject errorResult.error