index ↓
phone
client.phone Call state from the connected phone. onCallStarted, onCallUpdated, and onCallEnded track
each call, and onCommunicationsChanged reports signal, registration, and usable commands.
requests
you ask, the daemon answers. await the tagged result and check .ok
stateGet(): Promise<TypedRequestResult<PhoneStateReply, never>>
push form: onStateReply (subscribe instead of awaiting)
const res = await client.phone.stateGet();
if (res.ok) {
console.log(res.response.state);
} type Result =
| { ok: true; response: PhoneStateReply }
| { ok: false; kind: 'domain'; error: never }
| { ok: false; kind: 'protocol'; error: WireError }; commands
fire-and-forget. the promise resolves once the daemon has taken the message
answer(PhoneCallAction): Promise<void>
await client.phone.answer({ callId: '...' }); accept(PhoneAcceptAction): Promise<void>
await client.phone.accept({ callId: '...', action: 'accept' }); decline(PhoneCallAction): Promise<void>
await client.phone.decline({ callId: '...' }); end(PhoneCallAction): Promise<void>
await client.phone.end({ callId: '...' }); endTyped(PhoneEndAction): Promise<void>
await client.phone.endTyped({ callId: '...', action: 'end' }); hold(PhoneCallAction): Promise<void>
Requires holdAvailable on CommunicationsState.
await client.phone.hold({ callId: '...' }); unhold(PhoneCallAction): Promise<void>
await client.phone.unhold({ callId: '...' }); initiate(PhoneInitiateAction): Promise<void>
await client.phone.initiate({ kind: 'destination' }); mute(PhoneMuteAction): Promise<void>
await client.phone.mute({ mute: true }); dtmf(PhoneDtmfAction): Promise<void>
await client.phone.dtmf({ tone: 'd0' }); events
the daemon pushes these unprompted. subscribing returns an unsubscribe function
onCallStarted(handler: (PhoneCall) => void): () => void
const off = client.phone.onCallStarted((phoneCall) => {
console.log(phoneCall.callId);
});
// call off() to unsubscribe onCallUpdated(handler: (PhoneCall) => void): () => void
const off = client.phone.onCallUpdated((phoneCall) => {
console.log(phoneCall.callId);
});
// call off() to unsubscribe onCallEnded(handler: (PhoneCallEnded) => void): () => void
const off = client.phone.onCallEnded((phoneCallEnded) => {
console.log(phoneCallEnded.callId);
});
// call off() to unsubscribe onCommunicationsChanged(handler: (PhoneCommunicationsReply) => void): () => void
const off = client.phone.onCommunicationsChanged((reply) => {
console.log(reply.state);
});
// call off() to unsubscribe onErrorEvent(handler: (PhoneErrorReply) => void): () => void
const off = client.phone.onErrorEvent((reply) => {
console.log(reply.error);
});
// call off() to unsubscribe types
shapes referenced above, as the sdk types them
callId stays the same for the life of the call. Pass it to answer, decline, end, hold.
type PhoneCall = {
callId: string;
remoteId: string; // E.164 form when the phone provides one.
displayName: string;
status: PhoneCallStatus;
direction: PhoneCallDirection;
startedAtUnixS?: number;
label?: string;
addressBookId?: string;
service?: PhoneCallService;
isConferenced?: boolean;
conferenceGroup?: number;
}; type PhoneCallEnded = {
callId: string;
reason: CallEndReason;
}; type PhoneCommunicationsReply = {
state: CommunicationsState;
}; type PhoneErrorReply = {
error: PhoneError;
}; type PhoneStateReply = {
state: PhoneState;
}; type PhoneCallAction = {
callId: string;
}; type PhoneAcceptAction = {
callId: string;
action: AcceptCallAction;
}; type PhoneEndAction = {
callId: string;
action: EndCallAction;
}; type PhoneInitiateAction = {
kind: InitiateCallType;
destinationId?: string;
service?: PhoneCallService;
addressBookId?: string;
}; type PhoneMuteAction = {
mute: boolean;
}; type PhoneDtmfAction = {
callId?: string;
tone: DtmfTone;
}; type PhoneCallStatus =
| 'disconnected'
| 'sending'
| 'ringing'
| 'connecting'
| 'active'
| 'held'
| 'disconnecting'; type PhoneCallDirection = 'incoming' | 'outgoing'; How the call is carried. A phone that does not distinguish bearers reports telephony.
type PhoneCallService = 'unknown' | 'telephony' | 'faceTimeAudio' | 'faceTimeVideo'; type CallEndReason = 'local' | 'remote' | 'missed' | 'declined' | 'failed'; Enable a call-control button only while its *Available flag is true. Treat a null flag as
unavailable.
type CommunicationsState = {
signalStrength?: number;
registrationStatus?: RegistrationStatus;
airplaneMode?: boolean;
carrierName?: string;
cellularSupported?: boolean;
telephonyEnabled?: boolean;
faceTimeAudioEnabled?: boolean;
faceTimeVideoEnabled?: boolean;
muteStatus?: boolean;
currentCallCount?: number;
newVoicemailCount?: number;
initiateCallAvailable?: boolean;
endAndAcceptAvailable?: boolean;
holdAndAcceptAvailable?: boolean;
swapAvailable?: boolean;
mergeAvailable?: boolean;
holdAvailable?: boolean;
}; type PhoneError =
| 'callNotFound' // The phone reports no call with this id.
| 'actionRejected'
| 'noTarget' // No phone is connected.
| 'unavailable' // The verb's `*Available` flag was false when the action ran.; Call waiting and conference calls produce more than one entry.
type PhoneState = {
activeCalls: PhoneCall[];
}; What to do with an existing call when answering a new one.
type AcceptCallAction =
| 'accept' // Answer the new call and hold the existing one.
| 'endAndAccept' // End the existing call and answer the new one.; type EndCallAction = 'end' | 'endAll'; type InitiateCallType = 'destination' | 'voicemail' | 'redial'; type DtmfTone =
| 'd0'
| 'd1'
| 'd2'
| 'd3'
| 'd4'
| 'd5'
| 'd6'
| 'd7'
| 'd8'
| 'd9'
| 'star'
| 'hash'; type RegistrationStatus =
| 'unknown'
| 'notRegistered'
| 'searching'
| 'denied'
| 'registeredHome'
| 'registeredRoaming'
| 'emergencyCallsOnly';