UNPKG

homebridge-soundtouch-plugins

Version:
191 lines (183 loc) 5.53 kB
import { API, APIDiscovery, compactMap, Info, SourceStatus, } from '@bossoq/soundtouch-api' import { apiNotFoundWithName } from './errors' import { isVerboseInConfigs, stringUpperCaseFirst } from './utils' import { AccessoryConfig, GlobalConfig, PresetConfig, SoundTouchDevice, SoundTouchPreset, SoundTouchSource, SourceConfig, VolumeMode, } from './types' import { Logging } from 'homebridge' export const searchAllDevices = async ( globalConfig: GlobalConfig, accessoryConfigs: AccessoryConfig[], log: Logging ): Promise<SoundTouchDevice[]> => { const apis = await APIDiscovery.search() return Promise.all( apis.map(async (api) => { const info = await api.getInfo() const accessoryConfig = accessoryConfigs.find( (ac) => ac.room === info.name || ac.ip === api['host'] ) return _deviceFromApi(api, info, globalConfig, accessoryConfig || {}, log) }) ) } export const deviceFromConfig = async ( globalConfig: GlobalConfig, accessoryConfig: AccessoryConfig, log: Logging ): Promise<SoundTouchDevice> => { let api: API if (accessoryConfig.ip) { api = new API(accessoryConfig.ip, accessoryConfig.port) } else if (accessoryConfig.room) { api = await APIDiscovery.find(accessoryConfig.room) if (!api) throw apiNotFoundWithName(accessoryConfig.name) } return _deviceFromApi( api, await api.getInfo(), globalConfig, accessoryConfig, log ) } export const _deviceFromApi = async ( api: API, info: Info, globalConfig: GlobalConfig, accessoryConfig: AccessoryConfig, log: Logging ): Promise<SoundTouchDevice> => { const displayName = accessoryConfig.name || info.name const isVerbose = isVerboseInConfigs(globalConfig, accessoryConfig) const pollingInterval = accessoryConfig.pollingInterval || globalConfig.pollingInterval if (isVerbose) log(`[${displayName}] found device`) const component = info.components.find( (c) => c.serialNumber.toLowerCase() === info.deviceId.toLowerCase() ) const presets = await _availablePresets( api, displayName, accessoryConfig.presets, globalConfig.presets, isVerbose ? log : undefined ) const sources = await _availableSources( api, displayName, accessoryConfig.sources, globalConfig.sources, isVerbose ? log : undefined ) const globalVolume = globalConfig.volume || {} const accessoryVolume = accessoryConfig.volume || {} const onValue = globalVolume.onValue || accessoryVolume.onValue return { api: api, name: displayName, id: info.deviceId, model: info.type, version: component ? component.softwareVersion : undefined, verbose: isVerbose, pollingInterval: pollingInterval, volumeSettings: { onValue: onValue || -1, maxValue: globalVolume.maxValue || accessoryVolume.maxValue || 100, unmuteValue: globalVolume.unmuteValue || accessoryVolume.unmuteValue || onValue || 35, mode: globalVolume.mode || accessoryVolume.mode || VolumeMode.lightbulb, }, presets: presets, sources: sources, } } export const deviceIsOn = async ( device: SoundTouchDevice ): Promise<boolean> => { const nowPlaying = await device.api.getNowPlaying() return nowPlaying.source !== SourceStatus.standBy } const _availablePresets = async ( api: API, deviceName: string, accessoryPresets: PresetConfig[], globalPresets: PresetConfig[], log?: Logging ): Promise<SoundTouchPreset[]> => { const presets = (await api.getPresets()) || [] return compactMap(presets, (preset) => { const presetConfig = _findConfig( (p) => p.index === preset.id, accessoryPresets, globalPresets ) || { index: preset.id } if (log !== undefined) log( `[${deviceName}] found preset n°${preset.id} '${preset.contentItem.itemName}' on device` ) if ((<PresetConfig>presetConfig).enabled === false) return undefined return { name: (<PresetConfig>presetConfig).name || preset.contentItem.itemName, index: preset.id, } }) } const _availableSources = async ( api: API, deviceName: string, accessorySources?: SourceConfig[], globalSources?: SourceConfig[], log?: Logging ): Promise<SoundTouchSource[]> => { const sources = await api.getSources() const localSources = sources.items.filter((s) => s.isLocal) return localSources.map((source) => { if (log !== undefined) log( `[${deviceName}] found local source '${source.source}' with account '${source.sourceAccount}' on device` ) const sourceConfig = _findConfig( (p) => p.source === source.source && (p.account !== undefined ? p.account === source.sourceAccount : true), accessorySources, globalSources ) || { source: source.source } return { name: (<SourceConfig>sourceConfig).name || `${deviceName} ${ source.name ? source.name : stringUpperCaseFirst(sourceConfig.source) }`, source: sourceConfig.source, account: (<SourceConfig>sourceConfig).account, enabled: (<SourceConfig>sourceConfig).enabled, } }) } const _findConfig = <Config>( predicate: (config: Config) => boolean, accessoryConfigs?: Config[], globalConfigs?: Config[] ): Config | undefined => { const config = accessoryConfigs ? accessoryConfigs.find(predicate) : undefined if (config !== undefined) return config return globalConfigs ? globalConfigs.find(predicate) : undefined }