Skip to content

@mega-yfue/eufy-sdk / LiveTrace

Type Alias: LiveTrace ​

ts
type LiveTrace = 
  | {
  action: "start" | "keepalive";
  level2: boolean;
  phase: "media-command";
  topology: "attached" | "own";
}
  | {
  action: "start";
  phase:   | "media-command-ack"
     | "media-command-retry"
     | "media-command-unacknowledged";
}
  | {
  accepted: boolean;
  phase: "first-video-command";
  signCode: number;
}
  | {
  keyframe: boolean;
  phase: "first-video-unit";
}
  | {
  phase: "first-keyframe";
}
  | {
  media: "audio" | "video";
  phase: "first-foreign-media-command";
}
  | {
  phase: "video-decode-empty";
  signCode: number;
}
  | {
  dataType: number;
  phase: "datagram-gap";
}
  | {
  dataType: number;
  phase: "sequence-restart";
}
  | {
  cloud: boolean;
  cloudMissing?: "dsk-key" | "cloud-addresses";
  local: boolean;
  phase: "lookup-channels";
}
  | {
  phase: "session-connect-wait";
  waitMs: number;
}
  | {
  phase: "session-connected";
  waitedMs: number;
}
  | {
  phase: "session-unreachable";
  waitedMs: number;
}
  | {
  phase: "level2-wait";
  waitMs: number;
}
  | {
  cipherId: number;
  phase: "level2-ready";
}
  | {
  cipherId: number;
  phase: "level2-set-payload-only";
}
  | {
  cipherId?: number;
  phase: "level2-unavailable";
  reason:   | "no-cipher-key"
     | "derivation-failed"
     | "not-negotiating"
     | "session-closed"
     | "grace-elapsed";
  waitedMs?: number;
}
  | {
  answeredCipherId: number;
  cipherId: number;
  phase: "cipher-fallback";
}
  | {
  cipherId: number;
  phase: "level2-negotiating";
}
  | {
  channel: number;
  phase: "station-resolved";
  stationAdmin: "self" | "other" | "unstated";
  stationModel?: string;
  topology: "attached" | "own";
}
  | {
  issue: "missing" | "shared";
  phase: "station-channel-unresolved";
}
  | {
  deadlineMs: number;
  phase: "warming";
  retryMs: number;
}
  | {
  phase: "media-command-unsent";
  reason: "level2-key" | "address";
}
  | {
  phase: "path-stale";
  silentMs: number;
}
  | {
  outcome: "reasserted" | "declined";
  phase: "channel-silent";
  silentMs: number;
}
  | {
  attempts: number;
  errorName?: string;
  phase: "start-failed";
  reason: LiveStreamStartFailureReason;
  stage: LiveStreamStartStage;
};

One bounded startup observation.

Union Members ​

Type Literal ​

ts
{
  action: "start" | "keepalive";
  level2: boolean;
  phase: "media-command";
  topology: "attached" | "own";
}

A media start or keepalive was sent, with the topology and encryption level it was sent under.


Type Literal ​

ts
{
  action: "start";
  phase:   | "media-command-ack"
     | "media-command-retry"
     | "media-command-unacknowledged";
}

The device acknowledged a retained start, or that start was repeated / abandoned unacknowledged.


Type Literal ​

ts
{
  accepted: boolean;
  phase: "first-video-command";
  signCode: number;
}

The first inbound video command, and whether this stream's channel filter accepted it.


Type Literal ​

ts
{
  keyframe: boolean;
  phase: "first-video-unit";
}

The first reassembled video access unit, and whether it was decodable on its own.


Type Literal ​

ts
{
  phase: "first-keyframe";
}

The first keyframe reached the consumer.


Type Literal ​

ts
{
  media: "audio" | "video";
  phase: "first-foreign-media-command";
}

Media tagged for another camera on the same station arrived first.


Type Literal ​

ts
{
  phase: "video-decode-empty";
  signCode: number;
}

A video payload decoded to nothing, so no access unit could be built from it.


Type Literal ​

ts
{
  dataType: number;
  phase: "datagram-gap";
}

A datagram was missing on a data channel, discarding the logical frame being reassembled.


Type Literal ​

ts
{
  dataType: number;
  phase: "sequence-restart";
}

A data channel's numbering restarted mid-connection, so sequencing resynchronized onto it.


Type Literal ​

ts
{
  cloud: boolean;
  cloudMissing?: "dsk-key" | "cloud-addresses";
  local: boolean;
  phase: "lookup-channels";
}

Which lookup channels a connection can ask for the station on, before it asks.

A station is found by a local lookup, by a cloud lookup, or by both, and each needs something the other does not: the local one needs the station on this link, the cloud one needs both a key for the station and an address to ask. A connect that had one channel failed for that channel's reason alone, and a connect that had neither could not have succeeded — outcomes a station that is switched off is otherwise indistinguishable from, because nothing else in a failed connect states what was even attempted.

cloudMissing names the input the cloud channel lacked, present exactly when cloud is false: dsk-key where no key was held for the station, cloud-addresses where its record named no address to ask. The key is named where both are absent.


Type Literal ​

ts
{
  phase: "session-connect-wait";
  waitMs: number;
}

Work on a station is holding for its session to connect, with the milliseconds it will wait.

The earliest phase there is: nothing else on a station can be attempted until its session is up, and a caller whose own deadline expires inside this wait has this record and no other. Emitted only where a wait actually happens, so its absence states that the session was already connected.


Type Literal ​

ts
{
  phase: "session-connected";
  waitedMs: number;
}

The session connected, after this long.


Type Literal ​

ts
{
  phase: "session-unreachable";
  waitedMs: number;
}

The session did not connect within its wait, so nothing on this station can be attempted.

The one outcome that is otherwise indistinguishable from a station that answered and then refused: both leave a caller with no media and no phase naming a station.


Type Literal ​

ts
{
  phase: "level2-wait";
  waitMs: number;
}

A live start is holding for the station's level-2 key, with the milliseconds it will wait.

A start that looks slow is either waiting here, waiting for its session to connect, waiting for the station to serve the channel it was asked for, or being re-issued — and only these phases separate them.


Type Literal ​

ts
{
  cipherId: number;
  phase: "level2-ready";
}

The station's AES-256-GCM level-2 key was negotiated, under the cipher it selected.


Type Literal ​

ts
{
  cipherId: number;
  phase: "level2-set-payload-only";
}

The station's cipher negotiated only the AES-128-ECB level-2 key of an RSA cipher, under the cipher it selected. That key seals a level-2 set-payload and nothing else, so this is not level2-ready.


Type Literal ​

ts
{
  cipherId?: number;
  phase: "level2-unavailable";
  reason:   | "no-cipher-key"
     | "derivation-failed"
     | "not-negotiating"
     | "session-closed"
     | "grace-elapsed";
  waitedMs?: number;
}

The station's key is not coming, why, and the cipher where a station named one.

Every ending of a level-2 wait carries one of these reasons, so a start refused for want of a key is accounted for however it ended. grace-elapsed is a wait that ran out and states how long was waited; the rest are answered without waiting, because the negotiation is one-shot per connection and a concluded one is final. no-cipher-key and derivation-failed are about this account's cipher material, not-negotiating and session-closed about the station or its connection — and only a reason reached under a negotiation has a cipher to name.

A grace-elapsed start proceeds at level 1 where it has such a form, and not at all where it does not.


Type Literal ​

ts
{
  answeredCipherId: number;
  cipherId: number;
  phase: "cipher-fallback";
}

A station's cipher was answered with material for a DIFFERENT cipher, which was used in its place.

The one lookup outcome no other phase accounts for: material for the cipher the station named is followed by level2-ready, level2-set-payload-only or level2-unavailable with derivation-failed, an answer holding none by no-cipher-key, and a lookup that threw is reported as an error. Substituted material derives to nothing and otherwise reads as a station fault. cipherId is the cipher the station asked for, answeredCipherId the one whose material was used.


Type Literal ​

ts
{
  cipherId: number;
  phase: "level2-negotiating";
}

The station answered its gateway-info prompt, so a key derivation has begun under the cipher it named.

What separates a station that never answered the prompt from one that answered and produced no usable key: without it, level2-unavailable with not-negotiating covers both, and they are a station or network problem and an account cipher-material problem respectively.


Type Literal ​

ts
{
  channel: number;
  phase: "station-resolved";
  stationAdmin: "self" | "other" | "unstated";
  stationModel?: string;
  topology: "attached" | "own";
}

A station was resolved for a call, stating what the caller's device is on it and whose station it is.

Emitted before the session is waited on, so a station that is never reached still has this record: an attached camera's media start has no unencrypted form, so whether a device was taken as attached decides what its failure means. stationAdmin states whether the signed-in account is the station's administrator, which is what a key the account cannot resolve turns on; unstated is a device record that names no administrator, which is not the same as naming another. stationModel is the model of the station the call resolved — the base's for an attached camera, the device's own where it is its own station — absent where that record states none; without it a base this SDK reaches differently is indistinguishable from one that is switched off.


Type Literal ​

ts
{
  issue: "missing" | "shared";
  phase: "station-channel-unresolved";
}

The call's device has no usable channel on the station it resolved: its record states none (missing), or another device attached to the same station states the same one (shared). The call is refused with DeviceChannelUnresolvedError and nothing is sent.


Type Literal ​

ts
{
  deadlineMs: number;
  phase: "warming";
  retryMs: number;
}

A shared source began warming, with the interval it re-issues on and the deadline it fails at.


Type Literal ​

ts
{
  phase: "media-command-unsent";
  reason: "level2-key" | "address";
}

A media command was never put on the wire, and what it was missing.

The attached media start has no level-1 form, so without the station's level-2 key there is nothing to send. A command that was never sent is otherwise indistinguishable from one the station ignored, which is the difference between a key that never arrived and a station that is not answering.


Type Literal ​

ts
{
  phase: "path-stale";
  silentMs: number;
}

This connection's path has stopped answering the heartbeat, with how long it has been silent.

The station answers every PING with a PONG, so silence past several heartbeats is the path being gone. Stated only where a pong arrived: a station that has never answered says nothing by not answering now.


Type Literal ​

ts
{
  outcome: "reasserted" | "declined";
  phase: "channel-silent";
  silentMs: number;
}

A stream received nothing on its own channel for the stall window, and what was done about it.

A station that switches to a sibling leaves the stream it was serving with no frames, no error and no stop, so this silence is the only statement that it happened. reasserted re-issued the media start, which is the repair; declined left the channel alone because nothing is attached to this pull and taking the station back would take it from a camera someone is watching.

Media still arriving means this never fires, so a picture that stopped advancing while this is silent stopped for a reason upstream of the station's attention.


Type Literal ​

ts
{
  attempts: number;
  errorName?: string;
  phase: "start-failed";
  reason: LiveStreamStartFailureReason;
  stage: LiveStreamStartStage;
}

A live start failed, with the facts of the LiveStreamStartError it was refused with.

errorName is the upstream error's class name, present for source-error only; its message is never carried, because an upstream message can name a station.

Independent and unofficial. Not affiliated with, endorsed by, or sponsored by Anker Innovations, Anker eufy, or eufy. "Anker eufy", "eufy" and "Anker" are trademarks of their respective owners. Use responsibly — rapid or failed logins can trigger captcha or temporary cooldowns.