UNPKG

pimatic-denon-avr

Version:

Pimatic Plugin to monitor and control a Denon or Marantz AV Receiver

112 lines (101 loc) 4.2 kB
# 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