UNPKG

@petro-kushchak/homebridge-homepod-radio

Version:

Homebridge accessory for streaming radio to Homepod Mini and Apple TV

150 lines 6.44 kB
import * as path from 'path'; import * as os from 'os'; import { PLUGIN_MANUFACTURER, PLUGIN_NAME } from './platformConstants.js'; import { callbackify } from './lib/homebridgeCallbacks.js'; import { AirPlayDevice } from './lib/airplayDevice.js'; import { Storage } from './lib/storage.js'; /** * Homepod Radio Platform Accessory. */ export class HomepodRadioPlatformAccessory { platform; accessory; radio; playbackController; device; storage; service; currentMediaState; constructor(platform, accessory, radio, playbackController) { this.platform = platform; this.accessory = accessory; this.radio = radio; this.playbackController = playbackController; this.device = new AirPlayDevice(this.platform.platformConfig.homepodId, platform.logger, platform.platformConfig.verboseMode, this.streamerName(), radio.metadataUrl, radio.artworkUrl); const accessoryFileName = `${PLUGIN_NAME}-${this.accessory.UUID}.status.json`; const accessoryPath = path.join(os.homedir(), '.homebridge', accessoryFileName); this.platform.logger.info(`[${this.streamerName()}] Storage path: ${accessoryPath}`); this.storage = new Storage(accessoryPath); this.currentMediaState = this.getMediaState(); this.playbackController.addStreamer(this); this.accessory .getService(this.platform.Service.AccessoryInformation) .setCharacteristic(this.platform.Characteristic.Manufacturer, PLUGIN_MANUFACTURER) .setCharacteristic(this.platform.Characteristic.Model, this.radio.model) .setCharacteristic(this.platform.Characteristic.SerialNumber, this.platform.platformConfig.serialNumber) .setCharacteristic(this.platform.Characteristic.Name, this.radio.name); this.service = this.accessory.getService(this.platform.Service.SmartSpeaker) || this.accessory.addService(this.platform.Service.SmartSpeaker); // Allows name to show when adding speaker. // This has the added benefit of keeping the speaker name in sync with Roon and your config. this.service.setCharacteristic(this.platform.Characteristic.ConfiguredName, this.accessory.displayName); // Event handlers for CurrentMediaState and TargetMediaState Characteristics. this.service .getCharacteristic(this.platform.Characteristic.CurrentMediaState) .on("get" /* CharacteristicEventTypes.GET */, callbackify(this.getCurrentMediaState.bind(this))); this.service .getCharacteristic(this.platform.Characteristic.TargetMediaState) .on("set" /* CharacteristicEventTypes.SET */, callbackify(this.setTargetMediaState.bind(this))); this.service .getCharacteristic(this.platform.Characteristic.TargetMediaState) .on("set" /* CharacteristicEventTypes.SET */, callbackify(this.setSpeakerOn.bind(this))); // This will do its best to keep the actual outputs status up to date with Homekit. setInterval(async () => { this.currentMediaState = this.getMediaState(); this.service .getCharacteristic(this.platform.Characteristic.CurrentMediaState) .updateValue(this.currentMediaState); }, 3000); } async platformLaunched() { if (!this.radio.autoResume) { this.platform.logger.info(`[${this.streamerName()}] Skipped reading state`); return; } const state = (await this.storage.read()); this.platform.logger.info(`[${this.streamerName()}] State: ${JSON.stringify(state)}`); if (state) { await this.setTargetMediaState(state.playbackState); } } async shutdownRequested() { await this.storeState(); } async storeState() { if (!this.radio.autoResume) { this.platform.logger.info(`[${this.streamerName()}] Skipped storing state`); return; } const state = { playbackState: this.currentMediaState, }; await this.storage.write(state); this.platform.logger.info(`[${this.streamerName()}] Stored state: ${JSON.stringify(state)}`); } // eslint-disable-next-line @typescript-eslint/no-unused-vars async volumeUpdated(homepodId, volume) { return await Promise.resolve(); } async stopRequested(source) { this.platform.logger.info(`[${this.streamerName()}] Stopping playback - received stop request from ${source.streamerName()} `); await this.device.stop(); await this.storeState(); } streamerName() { return this.radio.name; } isPlaying() { return this.device.isPlaying(); } async startPlaying() { await this.playbackController.requestStop(this); await this.device.playStream(this.radio.radioUrl, this.radio.trackName, this.radio.volume); } async stopPlaying() { await this.device.stop(); } getMediaState() { if (this.device.isPlaying()) { return this.platform.Characteristic.CurrentMediaState.PLAY; } else { return this.platform.Characteristic.CurrentMediaState.STOP; } } /** * Set the setSpeakerOn. */ async setSpeakerOn(value) { this.platform.logger.info(`[${this.streamerName()}] Triggered SET Speaker ON: ${value}`); if (!value) { await this.device.stop(); } else { await this.startPlaying(); } } /** * Get the currentMediaState. */ async getCurrentMediaState() { this.currentMediaState = this.getMediaState(); this.platform.logger.debug(`[${this.streamerName()}] Triggered GET CurrentMediaState:`, this.currentMediaState); return Promise.resolve(this.currentMediaState); } /** * Set the targetMediaState. */ async setTargetMediaState(value) { this.platform.logger.info(`[${this.streamerName()}] Triggered SET TargetMediaState: ${value}`); if (value === this.platform.Characteristic.CurrentMediaState.PAUSE || value === this.platform.Characteristic.CurrentMediaState.STOP) { await this.stopPlaying(); } else { await this.startPlaying(); } } } //# sourceMappingURL=platformRadioAccessory.js.map