diff --git a/README.md b/README.md index 18be0ec..1fac71e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![npm](https://badgen.net/npm/dt/homebridge-airport-express-playing)](https://www.npmjs.com/package/homebridge-airport-express-playing) [![donate](https://badgen.net/badge/donate/paypal/91BE09)](https://www.paypal.me/apexadm) -[Homebridge](https://github.com/homebridge/homebridge) plugin that creates a smart speaker accessory that shows playing/paused for airport express devices. +[Homebridge](https://github.com/homebridge/homebridge) plugin that creates a smart speaker accessory that shows playing/paused for airport express devices. Also creates a switch that can be used to trigger home automations. ## Configuration This easiest way to use this plugin is to use [homebridge-config-ui-x](https://www.npmjs.com/package/homebridge-config-ui-x). @@ -15,9 +15,11 @@ To configure manually, add to the `platforms` section of Homebridge's `config.js ```json { "platform": "AirportExpressPlaying", - "name": "Airport Express Playing Platform" + "name": "Airport Express Playing Platform", + "showSwitch": true } ``` ## Sponsors [mbmccormick](https://github.com/mbmccormick) +[jsiegenthaler](https://github.com/jsiegenthaler) - orginal idea to use airport express txt record [posted to reddit](https://www.reddit.com/r/homebridge/comments/jxt9le/added_a_switch_in_homebridge_to_show_if_airport/) diff --git a/config.schema.json b/config.schema.json index 8b6f121..37b4a16 100644 --- a/config.schema.json +++ b/config.schema.json @@ -12,7 +12,7 @@ "default": "Airport Express Playing Platform", "required": true }, - "showSwitches": { + "showSwitch": { "title": "Show Switch (for home automations)", "type": "boolean", "default": true, diff --git a/src/platformAccessory.ts b/src/platformAccessory.ts index f2bbbab..8db3b34 100644 --- a/src/platformAccessory.ts +++ b/src/platformAccessory.ts @@ -24,7 +24,7 @@ export default class AirportExpress implements AccessoryPlugin { constructor(hap: HAP, mdns: any, log: Logging, config: PlatformConfig, data: mDNSReply) { this.log = log; this.name = data.fullname.replace('._airplay._tcp.local', ''); - this.showSwitch = config.showSwitches || true; + this.showSwitch = config.showSwitch || true; this.serialNumber = data.txt.find((str) => str.indexOf('serialNumber') > -1)?.replace('serialNumber=', '') || ''; this.hap = hap; this.mdns = mdns; @@ -41,9 +41,7 @@ export default class AirportExpress implements AccessoryPlugin { .getCharacteristic(this.hap.Characteristic.TargetMediaState) /* ignore attempts to set media state */ .on(CharacteristicEventTypes.SET, (state: CharacteristicValue, callback: CharacteristicSetCallback) => callback(null)); - if(this.showSwitch) { - this.switchService = new hap.Service.Switch(this.name); - } + if(this.showSwitch) { this.switchService = new hap.Service.Switch(this.name); } this.informationService = new this.hap.Service.AccessoryInformation() .setCharacteristic(this.hap.Characteristic.Manufacturer, 'Apple Inc.') @@ -57,23 +55,25 @@ export default class AirportExpress implements AccessoryPlugin { } convertMediaState(mDNS_TXT_record: Array) { - let playState = this.hap.Characteristic.CurrentMediaState.STOP; const bit11 = parseInt(((parseInt(mDNS_TXT_record.find((row: string) => row.indexOf('flags') > -1)!.replace('flags=', ''), 16).toString(2)).padStart(11, '0')).charAt(0)); if (bit11 === 0) { - playState = this.hap.Characteristic.CurrentMediaState.PAUSE; + return this.hap.Characteristic.CurrentMediaState.PAUSE; } else if (bit11 === 1) { /* bit11 correspponds to playing https://github.com/openairplay/airplay-spec/blob/master/src/status_flags.md */ - playState = this.hap.Characteristic.CurrentMediaState.PLAY; + return this.hap.Characteristic.CurrentMediaState.PLAY; } - return playState; + return this.hap.Characteristic.CurrentMediaState.STOP; } updateMediaState() { this.log.debug(`Updating Airport Exrpess with serial number ${this.serialNumber}`); const mdnsBrowser = this.mdns.createBrowser(this.mdns.tcp("airplay")); + mdnsBrowser.on('ready', () => mdnsBrowser.discover()); mdnsBrowser.on('update', (data: mDNSReply) => { const foundSerialNumber = data.txt.find((str) => str.indexOf('serialNumber') > -1)?.replace('serialNumber=', ''); + if (data.txt.includes('model=AirPort10,115') && foundSerialNumber && this.serialNumber === foundSerialNumber) { + this.log.debug(`txt record contents: ${data.txt}`) this.setMediaState(this.convertMediaState(data.txt)); mdnsBrowser.stop(); } @@ -84,16 +84,16 @@ export default class AirportExpress implements AccessoryPlugin { this.speakerService .setCharacteristic(this.hap.Characteristic.TargetMediaState, state) .setCharacteristic(this.hap.Characteristic.CurrentMediaState, state); + if (this.showSwitch) { this.switchService - .setCharacteristic( - this.hap.Characteristic.On, - state ===this.hap.Characteristic.CurrentMediaState.PLAY - ? true - : false - ) + .setCharacteristic(this.hap.Characteristic.On, state ===this.hap.Characteristic.CurrentMediaState.PLAY ? true : false) } } - getServices(): Service[] { return [ this.informationService, this.speakerService, this.switchService ]; } + getServices(): Service[] { + const services = [ this.informationService, this.speakerService ]; + if (this.showSwitch) { services.push(this.switchService); } + return services; + } }