Skip to content

Commit

Permalink
Use discovered capabilities instead of whitelist
Browse files Browse the repository at this point in the history
- Create the accessory for each zoneplayer as soon as it's discovered, but delay creating the services and characteristics, until after the homebridge server has started.
- Subscribe to `DeviceProperties` events as soon as the accessory has been created, so the discovered capabilities and setup can be used for creating services and characteristics.  Needed for #74.
- No more need for whitelists.
  • Loading branch information
ebaauw committed May 31, 2019
1 parent 0eb53b8 commit 3882162
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 57 deletions.
67 changes: 28 additions & 39 deletions lib/ZpAccessory.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,6 @@ function setHomebridge (Homebridge) {
volumeSelectors[Characteristic.VolumeSelector.DECREMENT] = 'Down'
}

const tvModels = [
'ZPS9', // PlayBar.
'ZPS11', // Playbase, see #58.
'ZPS14', // Beam, see #74.
'ZPS16' // Amp, see #8.
]

const stereoModels = [
'ZP90' // Connect
]

// ===== SONOS ACCESSORY =======================================================

// Constructor for ZpAccessory.
Expand All @@ -66,9 +55,6 @@ function ZpAccessory (platform, zp) {
this.uuid_base = zp.id
this.zp = zp
this.platform = platform
this.tv = tvModels.includes(this.zp.model)
this.hasBalance = stereoModels.includes(this.zp.model) ||
(this.zp.hasSlaves && !tvModels.includes(this.zp.model))
my = my || this.platform.my
this.subscriptions = {}
this.state = {
Expand All @@ -79,6 +65,32 @@ function ZpAccessory (platform, zp) {
this.log = this.platform.log
this.parser = new xml2js.Parser()

this.avTransport = new SonosModule.Services.AVTransport(this.zp.host, this.zp.port)
this.renderingControl = new SonosModule.Services.RenderingControl(this.zp.host, this.zp.port)
this.groupRenderingControl = new SonosModule.Services.GroupRenderingControl(this.zp.host, this.zp.port)
this.alarmClock = new SonosModule.Services.AlarmClock(this.zp.host, this.zp.port)

this.on('GroupManagement', this.handleGroupManagementEvent)
this.on('DeviceProperties', this.handleDevicePropertiesEvent)
this.on('AVTransport', this.handleAVTransportEvent)
this.on('RenderingControl', this.handleRenderingControlEvent)
this.on('GroupRenderingControl', this.handleGroupRenderingControlEvent)
this.on('AlarmClock', this.handleAlarmClockEvent)

this.subscribe('DeviceProperties', (err) => {
if (err) {
this.log.error('%s: subscribe to DeviceProperties events: %s', this.name, err)
}
})
}

util.inherits(ZpAccessory, events.EventEmitter)

// Called by homebridge to initialise a static accessory.
ZpAccessory.prototype.getServices = function () {
this.tv = this.capabilities.tvIn
this.hasBalance = this.capabilities.audioIn || this.capabilities.stereoPair

this.infoService = new Service.AccessoryInformation()
this.infoService
.updateCharacteristic(Characteristic.Manufacturer, 'homebridge-zp')
Expand Down Expand Up @@ -266,33 +278,15 @@ function ZpAccessory (platform, zp) {

this.alarms = {}
if (this.platform.alarms) {
for (let id in zp.alarms) {
const alarm = zp.alarms[id]
for (let id in this.zp.alarms) {
const alarm = this.zp.alarms[id]
this.alarms[alarm.ID] = new ZpAlarm(this, alarm)
this.services.push(this.alarms[alarm.ID].service)
this.hasAlarms = true
}
}

this.avTransport = new SonosModule.Services.AVTransport(this.zp.host, this.zp.port)
this.renderingControl = new SonosModule.Services.RenderingControl(this.zp.host, this.zp.port)
this.groupRenderingControl = new SonosModule.Services.GroupRenderingControl(this.zp.host, this.zp.port)
this.alarmClock = new SonosModule.Services.AlarmClock(this.zp.host, this.zp.port)

this.on('GroupManagement', this.handleGroupManagementEvent)
this.on('DeviceProperties', this.handleDevicePropertiesEvent)
this.on('AVTransport', this.handleAVTransportEvent)
this.on('RenderingControl', this.handleRenderingControlEvent)
this.on('GroupRenderingControl', this.handleGroupRenderingControlEvent)
this.on('AlarmClock', this.handleAlarmClockEvent)

this.createSubscriptions()
}

util.inherits(ZpAccessory, events.EventEmitter)

// Called by homebridge to initialise a static accessory.
ZpAccessory.prototype.getServices = function () {
return this.services
}

Expand Down Expand Up @@ -389,11 +383,6 @@ ZpAccessory.prototype.createSubscriptions = function () {
member.log.info('%s: member of group %s', member.name, member.coordinator.name)
member.copyCoordinator()
}
this.subscribe('DeviceProperties', (err) => {
if (err) {
this.log.error('%s: subscribe to DeviceProperties events: %s', this.name, err)
}
})
this.subscribe('MediaRenderer/AVTransport', (err) => {
if (err) {
this.log.error('%s: subscribe to AVTransport events: %s', this.name, err)
Expand Down
27 changes: 9 additions & 18 deletions lib/ZpPlatform.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function setHomebridge (homebridge) {
function ZpPlatform (log, config) {
this.log = log
this.name = config.name || 'ZP'
this.host = config.host
this.host = config.host || '0.0.0.0'
this.port = config.port || 0
this.nameScheme = config.nameScheme || '% Sonos'
this.packageJson = packageJson
Expand Down Expand Up @@ -133,9 +133,8 @@ function ZpPlatform (log, config) {
this.subscriptionTimeout = config.subscriptionTimeout || 30 // minutes
this.subscriptionTimeout *= 60 // seconds

this.players = []
this.slaves = []
this.zpAccessories = {}
this.zpAccessoryList = []

var msg = util.format(
'%s v%s, node %s, homebridge v%s', packageJson.name,
Expand Down Expand Up @@ -166,20 +165,9 @@ function ZpPlatform (log, config) {

// Called by homebridge to retrieve static list of ZpAccessories.
ZpPlatform.prototype.accessories = function (callback) {
let accessoryList = []
// Allow for search to find all Sonos ZonePlayers.
setTimeout(() => {
// this.listen(() => {
for (const zp of this.players) {
if (this.slaves.includes(zp.zone)) {
zp.hasSlaves = true
}
const accessory = new ZpAccessory(this, zp)
this.zpAccessories[zp.id] = accessory
accessoryList.push(accessory)
}
return callback(accessoryList)
// })
return callback(this.zpAccessoryList)
}, this.searchTimeout)
const npmRegistry = new homebridgeLib.RestClient({
host: 'registry.npmjs.org',
Expand All @@ -202,7 +190,9 @@ ZpPlatform.prototype.accessories = function (callback) {

// Create listener to receive notifications from Sonos ZonePlayers.
ZpPlatform.prototype.listen = function (ipaddress) {
this.host = findMyAddressFor(ipaddress)
if (this.host === '0.0.0.0') {
this.host = findMyAddressFor(ipaddress)
}
if (this.host === '0.0.0.0') {
this.log.error('cannot find network interface to zoneplayers')
}
Expand Down Expand Up @@ -288,7 +278,6 @@ ZpPlatform.prototype.findPlayers = function () {
'%s: %s: %s v%s in %s (slave)',
zp.host, zp.id, zp.modelName, zp.version, zp.zone
)
this.slaves.push(zp.zone)
} else {
this.log.debug(
'%s: %s: %s v%s in %s',
Expand All @@ -308,7 +297,9 @@ ZpPlatform.prototype.findPlayers = function () {
}
})
}
this.players.push(zp)
const accessory = new ZpAccessory(this, zp)
this.zpAccessories[zp.id] = accessory
this.zpAccessoryList.push(accessory)
}
}
})
Expand Down

0 comments on commit 3882162

Please sign in to comment.