Skip to content

Capabilities ​

Every capability is a property of the device, and every property exists from the moment you construct it. You never check for undefined. Whether a capability works depends on which protocol is up right now.

Which protocol serves what ​

CapabilityPropertyProtocolDevice
remoteremoteAirPlay, Companion Link for holds and double pressesboth
nowPlayingnowPlayingAirPlayboth
playbackplaybackAirPlayboth
volumevolumeAirPlayboth
artworkartworkAirPlayboth
multiroommultiroomAirPlayboth
streamAudiomedia.streamAudio()AirPlayboth
playUrlmedia.playUrl()AirPlay, when the device plays URLsApple TV
texttextCompanion Link, else AirPlayApple TV
powerpowerCompanion Link for the state, AirPlay to wake and sleepApple TV
appsappsCompanion LinkApple TV
accountsaccountsCompanion LinkApple TV
systemsystemCompanion LinkApple TV
touchtouchCompanion LinkApple TV

HomePodCapability types the capability names of a HomePod, and AppleTVCapability those of an AppleTV. Passing 'apps' to pod.supports() is a type error.

supports() ​

supports(capability) answers whether the capability works now:

  • playUrl needs an AirPlay session on a device that plays URLs itself;
  • text needs Companion Link or AirPlay;
  • apps, accounts, system and touch need Companion Link;
  • everything else needs AirPlay.
ts
if (tv.supports('apps')) {
    const apps = await tv.apps.list();
    console.log(apps.map(app => app.name));
}

The answer changes with the connection. Check it right before you act, or catch the error.

UnavailableError ​

A call that cannot work throws an UnavailableError. It carries the capability and a reason:

ReasonMeaning
disconnectedThe protocol the capability needs is down. It may work again after the next reconnect
unsupportedThe device cannot do this at all, such as playUrl on a HomePod
ts
import { UnavailableError } from '@corewire/devices';

try {
    await tv.apps.launch('com.apple.TVMusic');
} catch (error) {
    if (error instanceof UnavailableError && error.reason === 'disconnected') {
        console.log(`${error.capability} comes back after the reconnect`);
    } else {
        throw error;
    }
}

Reading state never throws. nowPlaying.current, volume.level, power.state, text.state and multiroom.outputs return the last known values while a protocol is down. Fetching is different. artwork.get() and artwork.getAnimated() ask the device, so they throw UnavailableError without a session, like every method.

Commands the app accepts ​

nowPlaying.supports(command) is a different question. It asks whether the app that plays now accepts a command, such as seek or shuffle. See Playback.

CoreWire is released under the MIT license.