pub struct Remote { /* private fields */ }Expand description
A client on the MX Remote network.
Create one with Remote::new, then Remote::start it. Discovery runs
on threads this client owns until Remote::close, or until the Remote
is dropped.
Implementations§
Source§impl Remote
impl Remote
Sourcepub fn select_video_source(
&self,
sink: BayUid,
source_port: u16,
) -> Result<(), ControlError>
pub fn select_video_source( &self, sink: BayUid, source_port: u16, ) -> Result<(), ControlError>
Routes this V2IP sink’s video to the stream a source port advertises.
Sourcepub fn select_audio_source(
&self,
sink: BayUid,
source_port: u16,
) -> Result<(), ControlError>
pub fn select_audio_source( &self, sink: BayUid, source_port: u16, ) -> Result<(), ControlError>
Routes this V2IP sink’s audio to the stream a source port advertises.
Sourcepub fn select_video_source_by_name(
&self,
sink: BayUid,
name: &str,
) -> Result<(), ControlError>
pub fn select_video_source_by_name( &self, sink: BayUid, name: &str, ) -> Result<(), ControlError>
Routes this V2IP sink’s video to the input bay with the given user-assigned name.
Sourcepub fn select_audio_source_addr(
&self,
sink: BayUid,
audio_ip: Ipv4Addr,
audio_port: Option<u16>,
format: Option<V2ipAudioFormat>,
) -> Result<(), ControlError>
pub fn select_audio_source_addr( &self, sink: BayUid, audio_ip: Ipv4Addr, audio_port: Option<u16>, format: Option<V2ipAudioFormat>, ) -> Result<(), ControlError>
Routes this V2IP sink’s audio to a multicast address directly, leaving its video and ancillary streams alone.
An unset port is the standard V2IP audio port. A format overrides the sample rate and channel count the receiver would otherwise assume.
Sourcepub fn select_source_addr(
&self,
sink: BayUid,
route: V2ipRoute,
format: Option<V2ipAudioFormat>,
) -> Result<(), ControlError>
pub fn select_source_addr( &self, sink: BayUid, route: V2ipRoute, format: Option<V2ipAudioFormat>, ) -> Result<(), ControlError>
Routes this V2IP sink’s video, audio and ancillary streams to multicast groups the caller names.
This is the only way to reach a stream no device on the mesh advertises, such as one the host is transmitting itself; a route by source port can only name a stream some bay has announced.
Set all three groups. The firmware decides whether a sink has a manual route by looking at the video and ancillary groups, so a route that leaves either unset does not register as one and the sink falls back to the audio source its mesh picks.
An unset format sends V2ipAudioFormat::STANDARD rather than
omitting the trailer. The firmware stores whatever this frame carries
and hands it to the FPGA unexamined, so a frame without one leaves a
zero rate and zero channel count there, which the FPGA rejects and
which takes the switch down with it.
Sourcepub fn select_audio_source_by_name(
&self,
sink: BayUid,
name: &str,
format: Option<V2ipAudioFormat>,
) -> Result<(), ControlError>
pub fn select_audio_source_by_name( &self, sink: BayUid, name: &str, format: Option<V2ipAudioFormat>, ) -> Result<(), ControlError>
Routes this V2IP sink’s audio from the input bay with the given user-assigned name.
A format is carried on the manual switch frame, which is the only form that can override the receiver’s sample rate and channel count.
Sourcepub fn set_bay_name(&self, bay: BayUid, name: &str) -> Result<(), ControlError>
pub fn set_bay_name(&self, bay: BayUid, name: &str) -> Result<(), ControlError>
Renames a bay.
Hides a bay from the pickers that list it, or shows it again.
Sourcepub fn select_edid_profile(
&self,
bay: BayUid,
profile: EdidProfile,
) -> Result<(), ControlError>
pub fn select_edid_profile( &self, bay: BayUid, profile: EdidProfile, ) -> Result<(), ControlError>
Sets the EDID profile an input presents to the source attached to it.
Sourcepub fn send_action(
&self,
bay: BayUid,
action: RcAction,
) -> Result<(), ControlError>
pub fn send_action( &self, bay: BayUid, action: RcAction, ) -> Result<(), ControlError>
Sends a remote-control action to whatever is attached to a bay.
Sourcepub fn send_key(&self, bay: BayUid, key: RcKey) -> Result<(), ControlError>
pub fn send_key(&self, bay: BayUid, key: RcKey) -> Result<(), ControlError>
Sends a remote-control key press to whatever is attached to a bay.
The device forwards it over CEC, infrared or IP, whichever that bay is
configured for; the caller does not choose. An action from
Remote::send_action names an outcome instead, and the device
decides which keys reach it.
Sourcepub fn power_on(&self, bay: BayUid) -> Result<(), ControlError>
pub fn power_on(&self, bay: BayUid) -> Result<(), ControlError>
Powers on the device attached to a bay.
Sourcepub fn power_off(&self, bay: BayUid) -> Result<(), ControlError>
pub fn power_off(&self, bay: BayUid) -> Result<(), ControlError>
Powers off the device attached to a bay.
Sourcepub fn set_volume(
&self,
bay: BayUid,
volume: u8,
muted: Option<bool>,
) -> Result<(), ControlError>
pub fn set_volume( &self, bay: BayUid, volume: u8, muted: Option<bool>, ) -> Result<(), ControlError>
Sets a bay’s volume, as a percentage, and optionally its mute state.
Both channels are set together: the wire carries them separately, but nothing on this surface splits them.
A bay with no volume control of its own is set through its
linked_bay, so an output wired to an
amplifier zone reaches that zone. volume_up,
volume_down and set_muted
follow the same link, and read the volume they step from through it.
Sourcepub fn volume_up(&self, bay: BayUid) -> Result<(), ControlError>
pub fn volume_up(&self, bay: BayUid) -> Result<(), ControlError>
Raises a bay’s volume by one percent.
Sourcepub fn volume_down(&self, bay: BayUid) -> Result<(), ControlError>
pub fn volume_down(&self, bay: BayUid) -> Result<(), ControlError>
Lowers a bay’s volume by one percent.
Sourcepub fn set_muted(&self, bay: BayUid, muted: bool) -> Result<(), ControlError>
pub fn set_muted(&self, bay: BayUid, muted: bool) -> Result<(), ControlError>
Mutes or unmutes a bay, keeping the volume it is set to.
Sourcepub fn set_amp_zone_settings(
&self,
bay: BayUid,
settings: AmpZoneSettings,
) -> Result<(), ControlError>
pub fn set_amp_zone_settings( &self, bay: BayUid, settings: AmpZoneSettings, ) -> Result<(), ControlError>
Applies amplifier settings to a zone.
Sourcepub fn set_audio_endpoint_muted(
&self,
device: DeviceUid,
endpoint: u16,
muted: bool,
) -> Result<(), ControlError>
pub fn set_audio_endpoint_muted( &self, device: DeviceUid, endpoint: u16, muted: bool, ) -> Result<(), ControlError>
Mutes or unmutes an audio endpoint.
Sourcepub fn set_audio_endpoint_trigger(
&self,
device: DeviceUid,
endpoint: u16,
active: bool,
) -> Result<(), ControlError>
pub fn set_audio_endpoint_trigger( &self, device: DeviceUid, endpoint: u16, active: bool, ) -> Result<(), ControlError>
Sets an audio endpoint’s trigger output.
Sourcepub fn set_audio_endpoint_locked(
&self,
device: DeviceUid,
endpoint: u8,
locked: bool,
) -> Result<(), ControlError>
pub fn set_audio_endpoint_locked( &self, device: DeviceUid, endpoint: u8, locked: bool, ) -> Result<(), ControlError>
Locks or unlocks the audio source of an audio endpoint: while it is locked, a video route change leaves the endpoint’s audio source alone.
Refused unless the endpoint reports
AudioFeatures::AUDIO_LOCK, which
is the only one the device acts on. The device reports its endpoints
again once the lock has changed;
AudioEndpoints::status reads it.
Sourcepub fn set_audio_endpoint_volume(
&self,
device: DeviceUid,
endpoint: u16,
volume: u32,
) -> Result<(), ControlError>
pub fn set_audio_endpoint_volume( &self, device: DeviceUid, endpoint: u16, volume: u32, ) -> Result<(), ControlError>
Sets an audio endpoint’s volume.
The audio module has no receiver for this command and ignores it.
It builds and sends the same shape as
Self::set_audio_endpoint_muted, and the send succeeds, because
nothing on these paths is acknowledged - so a caller sees success and no
change. The module dispatches this sub-command to the branch it uses for
one it does not recognise.
It is kept because the command is defined and the module transmits it itself, so a receiver may appear; read the endpoint back rather than assuming either way.
Sourcepub fn select_audio_endpoint_input(
&self,
sink: DeviceUid,
sink_endpoint: u16,
source: DeviceUid,
source_endpoint: u16,
) -> Result<(), ControlError>
pub fn select_audio_endpoint_input( &self, sink: DeviceUid, sink_endpoint: u16, source: DeviceUid, source_endpoint: u16, ) -> Result<(), ControlError>
Routes a source endpoint on one device to a sink endpoint on another.
Sourcepub fn subscribe_v2ip_stats(
&self,
device: DeviceUid,
subscribe: bool,
) -> Result<(), ControlError>
pub fn subscribe_v2ip_stats( &self, device: DeviceUid, subscribe: bool, ) -> Result<(), ControlError>
Starts or stops a V2IP device reporting its transport statistics.
There is no free-running mode: a device reports only while a subscription is live, at 1Hz, and the subscription lapses after a minute. A caller that wants a continuous feed re-sends inside the minute; nothing here re-arms it.
Reports reach crate::EventHandler::on_v2ip_stats_changed and read
back through Remote::v2ip_stats. A device new enough to send it also
carries what the sink’s decoder recovered, in
crate::V2ipDeviceStats::decoder.
Sourcepub fn request_edid(
&self,
device: DeviceUid,
output: bool,
) -> Result<(), ControlError>
pub fn request_edid( &self, device: DeviceUid, output: bool, ) -> Result<(), ControlError>
Asks a device for an EDID: the one the display on its output publishes, or the one it presents to the source on its input.
The device answers with a frame the receive path decodes, so the bytes
arrive at crate::EventHandler::on_edid_received and stay readable
through Remote::edid.
Only V2IP hardware handles this opcode. A matrix or an amplifier accepts the frame and answers nothing, at any protocol version, so the silence that follows is permanent rather than a reply still to come. This call cannot tell the two apart and does not try: it reports what was sent, and a caller polling for an EDID should ask a device that can answer rather than wait on one that cannot.
Sourcepub fn request_signal_status(
&self,
device: Option<DeviceUid>,
) -> Result<(), ControlError>
pub fn request_signal_status( &self, device: Option<DeviceUid>, ) -> Result<(), ControlError>
Asks for a detailed signal report from every bay of one device, or - with no device named - from every bay on the network.
Devices report on their own when a signal changes, so this is what a client that has just started needs: without it, a bay that has been showing the same picture for an hour says nothing until it changes.
Sourcepub fn reboot(&self, device: DeviceUid) -> Result<(), ControlError>
pub fn reboot(&self, device: DeviceUid) -> Result<(), ControlError>
Reboots a device.
The device is marked as rebooting once the frame is away, so the silence that follows does not read as one that went offline. A device on protocol 0x2C or later takes it only from its mesh controller or a management application, which this client announces itself as.
Sourcepub fn ping(&self, device: DeviceUid) -> Result<(), ControlError>
pub fn ping(&self, device: DeviceUid) -> Result<(), ControlError>
Asks a device to announce itself now.
Its hello marks it online again, so a device suspected to be gone is confirmed or ruled out within a second or two rather than at the end of its silence window. A device that stays silent is not taken offline here: that remains the silence window’s call.
Sourcepub fn auto_assign_v2ip_source_addresses(
&self,
device: DeviceUid,
) -> Result<(), ControlError>
pub fn auto_assign_v2ip_source_addresses( &self, device: DeviceUid, ) -> Result<(), ControlError>
Hands a V2IP source’s stream addresses back to automatic assignment, undoing addresses that were set on it by hand.
The device takes this only from management, which this client
announces itself as. Nothing acknowledges it: the device’s next
configuration report carries the addresses it ends up with, and
Remote::v2ip_details reads them.
Refused for a device that is not a V2IP source, and for one below protocol 0x2B, which predates the operation and ignores it.
Sourcepub fn clear_unit_status(&self, device: DeviceUid) -> Result<(), ControlError>
pub fn clear_unit_status(&self, device: DeviceUid) -> Result<(), ControlError>
Clears a unit’s current system status.
The terms of Remote::upgrade_unit_fpga apply.
Sourcepub fn upgrade_unit_fpga(
&self,
device: DeviceUid,
force: bool,
) -> Result<(), ControlError>
pub fn upgrade_unit_fpga( &self, device: DeviceUid, force: bool, ) -> Result<(), ControlError>
Upgrades a unit’s video processor to the latest image it holds, or
flashes that image again when force is set and it already runs it.
The unit takes a command only from its mesh controller or a management
application, which this client announces itself as. Nothing
acknowledges it: Remote::unit_status reads what the unit reports
next. Refused for a device that is not a V2IP unit, and for one below
protocol 0x2C, which predates the command.
Sourcepub fn set_unit_dhcp(&self, device: DeviceUid) -> Result<(), ControlError>
pub fn set_unit_dhcp(&self, device: DeviceUid) -> Result<(), ControlError>
Switches a unit to an IP configuration from DHCP.
The terms of Remote::set_unit_network apply.
Sourcepub fn set_unit_network(
&self,
device: DeviceUid,
network: UnitNetConfig,
) -> Result<(), ControlError>
pub fn set_unit_network( &self, device: DeviceUid, network: UnitNetConfig, ) -> Result<(), ControlError>
Gives a unit a static IP configuration.
The address must be one a unit can hold and the netmask a run of ones from the top; anything else is refused before it is sent.
The unit applies the change at once and reverts it unless its mesh
controller, hearing the unit report it, confirms it in time.
UnitStatus::network_pending says
whether it still waits, and
Remote::confirm_unit_network confirms it. Otherwise as
Remote::upgrade_unit_fpga.
Sourcepub fn confirm_unit_network(
&self,
device: DeviceUid,
) -> Result<(), ControlError>
pub fn confirm_unit_network( &self, device: DeviceUid, ) -> Result<(), ControlError>
Keeps a unit’s pending IP configuration, as its mesh controller does when it hears the unit at its new address.
The terms of Remote::upgrade_unit_fpga apply.
Sourcepub fn set_mesh_time_zone(
&self,
zone: &str,
rule: &str,
) -> Result<(), ControlError>
pub fn set_mesh_time_zone( &self, zone: &str, rule: &str, ) -> Result<(), ControlError>
Sets the time zone of every device that hears it.
zone is an IANA name such as Europe/Amsterdam and rule the POSIX
TZ rule the devices keep time by, such as CET-1CEST,M3.5.0,M10.5.0/3.
A device applies the rule and shows the name.
Neither may be empty, hold a NUL, or be longer than its field on the
wire leaves room for; Remote::clear_mesh_time_zone sends both empty.
The mesh controller takes it too, and announces it from then on with every periodic broadcast. A device takes it only from a management application or the controller, and this client announces itself as a management application.
Sourcepub fn clear_mesh_time_zone(&self) -> Result<(), ControlError>
pub fn clear_mesh_time_zone(&self) -> Result<(), ControlError>
Clears the time zone of every device that hears it, which then keeps
UTC. Sent, received and announced as Remote::set_mesh_time_zone
with an empty name and rule.
Sourcepub fn set_mesh_time(&self, time: SystemTime) -> Result<(), ControlError>
pub fn set_mesh_time(&self, time: SystemTime) -> Result<(), ControlError>
Sets the clock of every device that hears it to time.
A device keeps its own clock where that is within 2s of time. It takes
the time only from a management application or the controller; see
Remote::set_mesh_time_zone. A time before 1970 or past what 32 bits
of seconds hold, in 2106, is refused.
Sourcepub fn send_monitoring_pulse(&self) -> Result<(), ControlError>
pub fn send_monitoring_pulse(&self) -> Result<(), ControlError>
Asks every peer to report its monitoring data now rather than on its own schedule.
Sourcepub fn set_v2ip_auto_scaling(
&self,
device: DeviceUid,
enabled: bool,
) -> Result<(), ControlError>
pub fn set_v2ip_auto_scaling( &self, device: DeviceUid, enabled: bool, ) -> Result<(), ControlError>
Turns a V2IP sink’s automatic scaling on or off.
Automatic scaling and a configured output mode are separate reasons for
a sink to scale, and this moves only the first: a sink with a mode
configured goes on scaling to it with automatic scaling off. Turning
both off is this call plus Remote::clear_v2ip_output_mode.
Nothing acknowledges the frame. Read the sink back through
Remote::v2ip_details to learn what it did, and treat the block as
meaningful only where crate::DeviceInfo::config_initialised is set.
Read any route you still need before writing. The sink rebuilds
and rebroadcasts its subscription in response, and that report can
arrive empty for up to a minute; crate::DeviceV2ipSink says when
and why.
Sourcepub fn set_v2ip_output_mode(
&self,
device: DeviceUid,
mode: V2ipOutputMode,
) -> Result<(), ControlError>
pub fn set_v2ip_output_mode( &self, device: DeviceUid, mode: V2ipOutputMode, ) -> Result<(), ControlError>
Sets the output format a V2IP sink scales to.
The mode is checked here and nothing is sent if it fails, because every value a sink refuses it refuses in silence. Passing that check is not a guarantee: the sink also weighs the format against the display’s EDID and against what its own output stage can produce.
Turn automatic scaling off first if it is on. A sink refuses a mode whose format the attached display does not list while it is scaling automatically, and refuses it silently. Setting a mode and then turning automatic scaling back on is the order that survives, because the mode is checked while automatic scaling is still off.
Configuring a mode is itself a reason to scale, so a sink with one scales whether or not automatic scaling is on.
Pass a descriptor and a refresh rate that agree. A sink stores both
halves and, with its match-source setting on as it ships, reports back
the descriptor matching the refresh it holds: a 60Hz descriptor written
with a refresh of 50 reads back as that descriptor’s 50Hz sibling, once,
and stays there. A sink with match-source off reports the descriptor it
was given. Either way a pair that agrees reads back unchanged, and the
format driven is the same - so this costs a caller nothing except a
descriptor it did not write. That substitution shows up on the sink’s
next report rather than immediately, because
crate::V2ipScalingSettings holds what was written until then.
A mode read from the sink’s own web interface is not interchangeable
with this pair. That interface reports the descriptor’s 60Hz sibling and
carries the refresh in a field of its own, so writing back what it shows
as the mode, on its own, changes the setting rather than restoring it.
Read any route you still need before writing. The sink rebuilds
and rebroadcasts its subscription in response, and that report can
arrive empty for up to a minute; crate::DeviceV2ipSink says when
and why.
Sourcepub fn clear_v2ip_output_mode(
&self,
device: DeviceUid,
) -> Result<(), ControlError>
pub fn clear_v2ip_output_mode( &self, device: DeviceUid, ) -> Result<(), ControlError>
Clears the output format a V2IP sink is configured to scale to.
The sink stops scaling for that reason and keeps its automatic scaling
setting, so a sink scaling for both reasons goes on scaling until
Remote::set_v2ip_auto_scaling turns the other one off.
This is the only way to express “no mode configured”, and it is what a
caller restoring a sink that had none has to send: a sink reports no
mode by leaving the mode’s valid bit clear, which is not something a
write can say.
Read any route you still need before writing. The sink rebuilds
and rebroadcasts its subscription in response, and that report can
arrive empty for up to a minute; crate::DeviceV2ipSink says when
and why.
Sourcepub fn set_v2ip_device_setting(
&self,
device: DeviceUid,
setting: V2ipDeviceSetting,
enabled: bool,
) -> Result<(), ControlError>
pub fn set_v2ip_device_setting( &self, device: DeviceUid, setting: V2ipDeviceSetting, enabled: bool, ) -> Result<(), ControlError>
Switches on/off settings of a V2IP device, all to the same value.
setting names one or more of V2ipDeviceSetting::SWITCHES, and
each must be one the device has reported: a device ignores a setting it
does not have, so a write for one would read back as applied here and
change nothing there.
Nothing acknowledges the frame. The device answers by reporting its
settings, and until then Remote::v2ip_device_settings reads back
what was written.
Sourcepub fn set_v2ip_ir_profile(
&self,
device: DeviceUid,
profile: i8,
) -> Result<(), ControlError>
pub fn set_v2ip_ir_profile( &self, device: DeviceUid, profile: i8, ) -> Result<(), ControlError>
Sets the infrared profile of a V2IP device’s global infrared port.
profile is below V2IP_IR_PROFILE_MAX, and is checked here because
a device ignores one out of range. The terms of
Remote::set_v2ip_device_setting apply.
Sourcepub fn set_v2ip_sink_ir_profile(
&self,
device: DeviceUid,
profile: i8,
) -> Result<(), ControlError>
pub fn set_v2ip_sink_ir_profile( &self, device: DeviceUid, profile: i8, ) -> Result<(), ControlError>
Sets the infrared profile of a V2IP device’s output infrared port.
V2IP_IR_PROFILE_NOT_SET makes the port follow the global one.
Otherwise as Remote::set_v2ip_ir_profile.
Sourcepub fn set_v2ip_auto_power_save(
&self,
device: DeviceUid,
minutes: u16,
) -> Result<(), ControlError>
pub fn set_v2ip_auto_power_save( &self, device: DeviceUid, minutes: u16, ) -> Result<(), ControlError>
Sets how many idle minutes a V2IP device waits before it powers down by
itself, 0 for never. The terms of Remote::set_v2ip_device_setting
apply.
Sourcepub fn set_v2ip_power_save_schedule(
&self,
device: DeviceUid,
schedule: V2ipPowerSaveSchedule,
) -> Result<(), ControlError>
pub fn set_v2ip_power_save_schedule( &self, device: DeviceUid, schedule: V2ipPowerSaveSchedule, ) -> Result<(), ControlError>
Sets a V2IP device’s daily power save windows.
Every time is below V2IP_MINUTES_PER_DAY,
and is checked here.
The windows are kept in the device’s own time zone. The terms of
Remote::set_v2ip_device_setting apply.
Sourcepub fn set_v2ip_ptp_mode(
&self,
device: DeviceUid,
mode: V2ipPtpMode,
) -> Result<(), ControlError>
pub fn set_v2ip_ptp_mode( &self, device: DeviceUid, mode: V2ipPtpMode, ) -> Result<(), ControlError>
Sets the PTP mode a V2IP device runs. The terms of
Remote::set_v2ip_device_setting apply.
Sourcepub fn set_v2ip_ptp_domain(
&self,
device: DeviceUid,
domain: u8,
) -> Result<(), ControlError>
pub fn set_v2ip_ptp_domain( &self, device: DeviceUid, domain: u8, ) -> Result<(), ControlError>
Sets the PTP domain a V2IP device runs in, at most
V2IP_PTP_DOMAIN_MAX. The terms of
Remote::set_v2ip_device_setting apply.
Sourcepub fn set_v2ip_ptp_priority1(
&self,
device: DeviceUid,
priority1: u8,
) -> Result<(), ControlError>
pub fn set_v2ip_ptp_priority1( &self, device: DeviceUid, priority1: u8, ) -> Result<(), ControlError>
Sets the priority1 a V2IP device announces as a PTP grandmaster, lower
winning. The terms of Remote::set_v2ip_device_setting apply.
Sourcepub fn set_all_v2ip_device_settings(
&self,
settings: V2ipDeviceSettings,
) -> Result<(), ControlError>
pub fn set_all_v2ip_device_settings( &self, settings: V2ipDeviceSettings, ) -> Result<(), ControlError>
Changes settings on every V2IP device of the mesh with one frame.
settings carries the settings behind their bits in
valid, as a device reports them. Each
device applies those it has and ignores the rest, and none that
predates the frame applies any. A setting only a device reports about
itself, a profile, PTP mode or PTP domain out of range and a schedule
time that is not a time of day are refused here, since every device
would ignore them.
Nothing is cached: each device that applies a change reports its
settings, and Remote::v2ip_device_settings reads that.
Sourcepub fn set_v2ip_vlan(
&self,
device: DeviceUid,
vlan: V2ipVlan,
) -> Result<(), ControlError>
pub fn set_v2ip_vlan( &self, device: DeviceUid, vlan: V2ipVlan, ) -> Result<(), ControlError>
Changes a V2IP device’s VLAN configuration.
Writes the VLAN ids, the pinned uplink and the
TRUNK flag of vlan; its other flags and the
fields only the device reports are not sent. Refused before anything is
sent unless the device announces DeviceFeature::VLAN and has
reported its configuration, every id is at most
V2IP_VLAN_ID_MAX, and the uplink is
detected or names a port the device has.
The device applies the change at once and reverts it unless the mesh
controller, hearing the device report it, confirms it. Nothing is
cached here: Remote::v2ip_vlan reads what the device reports, and
V2ipVlan::is_pending whether it is still to be confirmed.
Sourcepub fn request_v2ip_testcard(
&self,
device: DeviceUid,
) -> Result<(), ControlError>
pub fn request_v2ip_testcard( &self, device: DeviceUid, ) -> Result<(), ControlError>
Asks a V2IP sink for its test card, which it reports straight back;
Remote::v2ip_testcard reads it.
Refused unless the sink’s video processor has reported
SINK_TEST_PATTERN. A sink with
that feature but without the module that draws the test card does not
answer.
Sourcepub fn set_v2ip_test_pattern(
&self,
device: DeviceUid,
pattern: V2ipTestPattern,
colour: u32,
) -> Result<(), ControlError>
pub fn set_v2ip_test_pattern( &self, device: DeviceUid, pattern: V2ipTestPattern, colour: u32, ) -> Result<(), ControlError>
Shows a test pattern on a V2IP sink’s output, or none for
V2ipTestPattern::OFF. colour is 0xRRGGBB, used by
V2ipTestPattern::FLAT.
A pattern runs until it is turned off, and holds the output on while it
does. The sink reports its test card in answer. Refused as
Remote::request_v2ip_testcard is, and for a pattern this library
does not name or a colour above 0xFFFFFF.
Sourcepub fn set_v2ip_test_tone(
&self,
device: DeviceUid,
tone: V2ipTestTone,
) -> Result<(), ControlError>
pub fn set_v2ip_test_tone( &self, device: DeviceUid, tone: V2ipTestTone, ) -> Result<(), ControlError>
Plays a test tone on a V2IP sink’s output.
Every value is checked here, as the sink ignores a tone that is not
V2ipTestTone::is_valid; V2ipToneMode::OFF stops it whatever the
rest holds, and the sink keeps those values as its last ones. Otherwise
as Remote::set_v2ip_test_pattern.
Sourcepub fn set_v2ip_test_sync(
&self,
device: DeviceUid,
sync: V2ipTestSync,
) -> Result<(), ControlError>
pub fn set_v2ip_test_sync( &self, device: DeviceUid, sync: V2ipTestSync, ) -> Result<(), ControlError>
Sets a V2IP sink’s lip-sync flash.
Checked here as V2ipTestSync::is_valid, since the sink ignores
settings that are not. Otherwise as Remote::set_v2ip_test_pattern.
Sourcepub fn preview_video_wall(
&self,
sink: DeviceUid,
window: VideoWallWindow,
) -> Result<(), ControlError>
pub fn preview_video_wall( &self, sink: DeviceUid, window: VideoWallWindow, ) -> Result<(), ControlError>
Shows a window on a sink’s video wall without persisting it.
The window survives until the sink is told otherwise or restarts.
Remote::revert_video_wall puts back whatever was stored.
Pass crate::VIDEO_WALL_CLEARED to show the whole frame again.
Sourcepub fn store_video_wall(
&self,
sink: DeviceUid,
window: VideoWallWindow,
) -> Result<(), ControlError>
pub fn store_video_wall( &self, sink: DeviceUid, window: VideoWallWindow, ) -> Result<(), ControlError>
Persists a window as a sink’s video wall.
The geometry is checked here, before anything is sent, because the sink is not guaranteed to check it. A sink running a video-wall module older than 2026083100 writes the window to its configuration before asking its video processor to apply it, and the processor’s refusal does not undo that write - so an out-of-spec window survives a reboot and is re-offered on every stream restart until something else replaces it. A power cycle does not clear it.
Nothing acknowledges this frame either way, so an Ok says only that
it was sent. Read the sink’s state back to learn what it did.
Pass crate::VIDEO_WALL_CLEARED to store “show the whole frame”.
Sourcepub fn revert_video_wall(&self, sink: DeviceUid) -> Result<(), ControlError>
pub fn revert_video_wall(&self, sink: DeviceUid) -> Result<(), ControlError>
Restores the window a sink has stored, discarding a preview.
Carries no window of its own: the sink already holds the one this puts back.
Sourcepub fn set_multiviewer_view_mode(
&self,
device: DeviceUid,
mode: MultiviewerViewMode,
) -> Result<(), ControlError>
pub fn set_multiviewer_view_mode( &self, device: DeviceUid, mode: MultiviewerViewMode, ) -> Result<(), ControlError>
Sets the window layout.
Sourcepub fn set_multiviewer_video_source(
&self,
device: DeviceUid,
screen: u8,
source: MultiviewerSource,
) -> Result<(), ControlError>
pub fn set_multiviewer_video_source( &self, device: DeviceUid, screen: u8, source: MultiviewerSource, ) -> Result<(), ControlError>
Assigns a source to one window, counting windows from zero.
A window index the multiviewer is not currently showing is refused rather than sent: firmware accepts an index one past the last window and writes through the end of the array it indexes, so the frame that would carry it is the one frame this library must never put on the wire. The bound comes from the layout in the multiviewer’s last status report, so a multiviewer that has reported none can only be given window zero, which every layout has.
Sourcepub fn set_multiviewer_audio_source(
&self,
device: DeviceUid,
source: MultiviewerSource,
) -> Result<(), ControlError>
pub fn set_multiviewer_audio_source( &self, device: DeviceUid, source: MultiviewerSource, ) -> Result<(), ControlError>
Selects which window’s audio is output.
Sourcepub fn set_multiviewer_audio_volume(
&self,
device: DeviceUid,
volume: u8,
muted: bool,
) -> Result<(), ControlError>
pub fn set_multiviewer_audio_volume( &self, device: DeviceUid, volume: u8, muted: bool, ) -> Result<(), ControlError>
Sets the output volume, as a percentage, and the mute state.
A volume above 100 is refused rather than sent. What a multiviewer does with one depends on its module version: from 2026083100 it drops the whole frame, and before that it dropped the volume alone and still acted on the mute beside it. Neither is what the caller asked for, and neither is reported back.
Sourcepub fn set_multiviewer_edid_template(
&self,
device: DeviceUid,
template: MultiviewerEdidTemplate,
) -> Result<(), ControlError>
pub fn set_multiviewer_edid_template( &self, device: DeviceUid, template: MultiviewerEdidTemplate, ) -> Result<(), ControlError>
Sets the EDID template presented to the sources.
Sourcepub fn set_multiviewer_remote_control(
&self,
device: DeviceUid,
source: MultiviewerSource,
) -> Result<(), ControlError>
pub fn set_multiviewer_remote_control( &self, device: DeviceUid, source: MultiviewerSource, ) -> Result<(), ControlError>
Selects which window receives remote-control passthrough.
Sourcepub fn set_multiviewer_pip_size(
&self,
device: DeviceUid,
size: MultiviewerPipSize,
) -> Result<(), ControlError>
pub fn set_multiviewer_pip_size( &self, device: DeviceUid, size: MultiviewerPipSize, ) -> Result<(), ControlError>
Sets how large the picture-in-picture window is.
Sourcepub fn set_multiviewer_pip_position(
&self,
device: DeviceUid,
position: MultiviewerPipPosition,
) -> Result<(), ControlError>
pub fn set_multiviewer_pip_position( &self, device: DeviceUid, position: MultiviewerPipPosition, ) -> Result<(), ControlError>
Sets which corner the picture-in-picture window sits in.
Sourcepub fn set_multiviewer_aspect_ratio(
&self,
device: DeviceUid,
aspect: MultiviewerAspectRatio,
) -> Result<(), ControlError>
pub fn set_multiviewer_aspect_ratio( &self, device: DeviceUid, aspect: MultiviewerAspectRatio, ) -> Result<(), ControlError>
Sets the aspect ratio the windows are scaled to.
Sourcepub fn set_multiviewer_auto_switch(
&self,
device: DeviceUid,
enable: bool,
) -> Result<(), ControlError>
pub fn set_multiviewer_auto_switch( &self, device: DeviceUid, enable: bool, ) -> Result<(), ControlError>
Enables or disables switching windows on its own.
Sourcepub fn set_multiviewer_output_mode(
&self,
device: DeviceUid,
mode: MultiviewerOutputMode,
) -> Result<(), ControlError>
pub fn set_multiviewer_output_mode( &self, device: DeviceUid, mode: MultiviewerOutputMode, ) -> Result<(), ControlError>
Sets the output resolution and refresh rate.
Sourcepub fn set_multiviewer_output_itc(
&self,
device: DeviceUid,
mode: MultiviewerItcMode,
) -> Result<(), ControlError>
pub fn set_multiviewer_output_itc( &self, device: DeviceUid, mode: MultiviewerItcMode, ) -> Result<(), ControlError>
Sets the IT-content flag on the output.
Sourcepub fn set_multiviewer_hdcp_mode(
&self,
device: DeviceUid,
mode: MultiviewerHdcpMode,
) -> Result<(), ControlError>
pub fn set_multiviewer_hdcp_mode( &self, device: DeviceUid, mode: MultiviewerHdcpMode, ) -> Result<(), ControlError>
Sets the HDCP version negotiated on the output.
Sourcepub fn set_multiviewer_input_source(
&self,
device: DeviceUid,
input: u8,
source: DeviceUid,
) -> Result<(), ControlError>
pub fn set_multiviewer_input_source( &self, device: DeviceUid, input: u8, source: DeviceUid, ) -> Result<(), ControlError>
Maps a source device onto one of the multiviewer’s inputs, counting inputs from zero.
DeviceUid::ZERO clears the mapping on a multiviewer running module
version 2026083100 or newer, and is stored as a mapping like any other
on anything older. No version checks that a mapping names a device on
the mesh.
Which of the two happened shows in mappings on a later status report,
where a cleared input reads as DeviceUid::ZERO only from that same
version. It will not be the next frame this multiviewer sends: this is
one of the two settings that schedule no status broadcast of their own,
so the answer arrives whenever something else prompts one.
Sourcepub fn multiviewer_auto_route(
&self,
device: DeviceUid,
) -> Result<(), ControlError>
pub fn multiviewer_auto_route( &self, device: DeviceUid, ) -> Result<(), ControlError>
Asks the multiviewer to route its sources itself.
Source§impl Remote
impl Remote
Sourcepub fn new(config: Config, handler: Arc<dyn EventHandler>) -> Result<Self>
pub fn new(config: Config, handler: Arc<dyn EventHandler>) -> Result<Self>
Builds a client, loading or generating its identifier.
Nothing is sent and no socket is opened until Remote::start.
Examples found in repository?
59fn main() -> std::io::Result<()> {
60 let mut config = Config::default();
61 config.name = Some("discover".to_owned());
62 if let Some(address) = std::env::args().nth(1) {
63 config.local_ip = Some(address.parse().map_err(|_| {
64 std::io::Error::new(std::io::ErrorKind::InvalidInput, "not an IPv4 address")
65 })?);
66 }
67
68 let remote = Arc::new(Remote::new(config, Arc::new(Printer))?);
69 let _ = CLIENT.set(Arc::clone(&remote));
70 remote.start()?;
71
72 if let Some(target) = remote.target() {
73 println!("listening, sending to {target}. Ctrl-C to stop.");
74 }
75
76 let running = Arc::new(AtomicBool::new(true));
77 let stop = Arc::clone(&running);
78 ctrl_c(move || stop.store(false, Ordering::Relaxed));
79 while running.load(Ordering::Relaxed) {
80 std::thread::sleep(Duration::from_millis(200));
81 }
82
83 let devices = remote.devices();
84 println!("\n{} device(s):", devices.len());
85 for device in devices {
86 Printer.on_device_update(device);
87 }
88
89 remote.close();
90 Ok(())
91}Sourcepub fn start(&self) -> Result<()>
pub fn start(&self) -> Result<()>
Opens the socket, announces this client and begins discovery.
Returns once the receive thread is running; discovery continues in the background.
Examples found in repository?
59fn main() -> std::io::Result<()> {
60 let mut config = Config::default();
61 config.name = Some("discover".to_owned());
62 if let Some(address) = std::env::args().nth(1) {
63 config.local_ip = Some(address.parse().map_err(|_| {
64 std::io::Error::new(std::io::ErrorKind::InvalidInput, "not an IPv4 address")
65 })?);
66 }
67
68 let remote = Arc::new(Remote::new(config, Arc::new(Printer))?);
69 let _ = CLIENT.set(Arc::clone(&remote));
70 remote.start()?;
71
72 if let Some(target) = remote.target() {
73 println!("listening, sending to {target}. Ctrl-C to stop.");
74 }
75
76 let running = Arc::new(AtomicBool::new(true));
77 let stop = Arc::clone(&running);
78 ctrl_c(move || stop.store(false, Ordering::Relaxed));
79 while running.load(Ordering::Relaxed) {
80 std::thread::sleep(Duration::from_millis(200));
81 }
82
83 let devices = remote.devices();
84 println!("\n{} device(s):", devices.len());
85 for device in devices {
86 Printer.on_device_update(device);
87 }
88
89 remote.close();
90 Ok(())
91}Sourcepub fn close(&self)
pub fn close(&self)
Stops discovery, closes the socket and waits for the threads to finish.
Calling it more than once, or before Remote::start, does nothing.
Examples found in repository?
59fn main() -> std::io::Result<()> {
60 let mut config = Config::default();
61 config.name = Some("discover".to_owned());
62 if let Some(address) = std::env::args().nth(1) {
63 config.local_ip = Some(address.parse().map_err(|_| {
64 std::io::Error::new(std::io::ErrorKind::InvalidInput, "not an IPv4 address")
65 })?);
66 }
67
68 let remote = Arc::new(Remote::new(config, Arc::new(Printer))?);
69 let _ = CLIENT.set(Arc::clone(&remote));
70 remote.start()?;
71
72 if let Some(target) = remote.target() {
73 println!("listening, sending to {target}. Ctrl-C to stop.");
74 }
75
76 let running = Arc::new(AtomicBool::new(true));
77 let stop = Arc::clone(&running);
78 ctrl_c(move || stop.store(false, Ordering::Relaxed));
79 while running.load(Ordering::Relaxed) {
80 std::thread::sleep(Duration::from_millis(200));
81 }
82
83 let devices = remote.devices();
84 println!("\n{} device(s):", devices.len());
85 for device in devices {
86 Printer.on_device_update(device);
87 }
88
89 remote.close();
90 Ok(())
91}Sourcepub fn target(&self) -> Option<SocketAddrV4>
pub fn target(&self) -> Option<SocketAddrV4>
The address frames are being sent to, once started.
Examples found in repository?
59fn main() -> std::io::Result<()> {
60 let mut config = Config::default();
61 config.name = Some("discover".to_owned());
62 if let Some(address) = std::env::args().nth(1) {
63 config.local_ip = Some(address.parse().map_err(|_| {
64 std::io::Error::new(std::io::ErrorKind::InvalidInput, "not an IPv4 address")
65 })?);
66 }
67
68 let remote = Arc::new(Remote::new(config, Arc::new(Printer))?);
69 let _ = CLIENT.set(Arc::clone(&remote));
70 remote.start()?;
71
72 if let Some(target) = remote.target() {
73 println!("listening, sending to {target}. Ctrl-C to stop.");
74 }
75
76 let running = Arc::new(AtomicBool::new(true));
77 let stop = Arc::clone(&running);
78 ctrl_c(move || stop.store(false, Ordering::Relaxed));
79 while running.load(Ordering::Relaxed) {
80 std::thread::sleep(Duration::from_millis(200));
81 }
82
83 let devices = remote.devices();
84 println!("\n{} device(s):", devices.len());
85 for device in devices {
86 Printer.on_device_update(device);
87 }
88
89 remote.close();
90 Ok(())
91}Sourcepub fn devices(&self) -> Vec<DeviceUid>
pub fn devices(&self) -> Vec<DeviceUid>
Every device heard from, in no particular order.
Examples found in repository?
59fn main() -> std::io::Result<()> {
60 let mut config = Config::default();
61 config.name = Some("discover".to_owned());
62 if let Some(address) = std::env::args().nth(1) {
63 config.local_ip = Some(address.parse().map_err(|_| {
64 std::io::Error::new(std::io::ErrorKind::InvalidInput, "not an IPv4 address")
65 })?);
66 }
67
68 let remote = Arc::new(Remote::new(config, Arc::new(Printer))?);
69 let _ = CLIENT.set(Arc::clone(&remote));
70 remote.start()?;
71
72 if let Some(target) = remote.target() {
73 println!("listening, sending to {target}. Ctrl-C to stop.");
74 }
75
76 let running = Arc::new(AtomicBool::new(true));
77 let stop = Arc::clone(&running);
78 ctrl_c(move || stop.store(false, Ordering::Relaxed));
79 while running.load(Ordering::Relaxed) {
80 std::thread::sleep(Duration::from_millis(200));
81 }
82
83 let devices = remote.devices();
84 println!("\n{} device(s):", devices.len());
85 for device in devices {
86 Printer.on_device_update(device);
87 }
88
89 remote.close();
90 Ok(())
91}Sourcepub fn device(&self, uid: DeviceUid) -> Option<DeviceInfo>
pub fn device(&self, uid: DeviceUid) -> Option<DeviceInfo>
A snapshot of one device.
Examples found in repository?
28 fn on_device_update(&self, device: DeviceUid) {
29 let Some(remote) = CLIENT.get() else { return };
30 let Some(info) = remote.device(device) else {
31 return;
32 };
33 println!(
34 " {device} {:<16} {:<12} {:<16} protocol {:#04x}, {} bays{}",
35 info.model,
36 info.serial,
37 info.name,
38 info.supported_protocol,
39 info.bays.len(),
40 if info.online { "" } else { " (offline)" },
41 );
42 }Sourcepub fn device_by_serial(&self, serial: &str) -> Option<DeviceUid>
pub fn device_by_serial(&self, serial: &str) -> Option<DeviceUid>
The device with the given serial number.
Sourcepub fn resolve_device(&self, name: &str) -> Option<DeviceUid>
pub fn resolve_device(&self, name: &str) -> Option<DeviceUid>
Resolves a device from its dotted-hex identifier, falling back to a serial-number match.
Sourcepub fn bay(&self, uid: BayUid) -> Option<BayInfo>
pub fn bay(&self, uid: BayUid) -> Option<BayInfo>
A snapshot of one bay.
Examples found in repository?
44 fn on_bay_update(&self, bay: BayUid) {
45 let Some(remote) = CLIENT.get() else { return };
46 let Some(info) = remote.bay(bay) else { return };
47 println!(
48 " bay {} {:<16} {}",
49 bay.port,
50 info.user_name,
51 match info.signal_detected {
52 Some(true) => "signal",
53 _ => "no signal",
54 },
55 );
56 }Sourcepub fn bay_by_name(&self, device: DeviceUid, port_name: &str) -> Option<BayUid>
pub fn bay_by_name(&self, device: DeviceUid, port_name: &str) -> Option<BayUid>
The bay on device with the given port name, such as Output 1.
Sourcepub fn bay_by_stream_ip(&self, ip: Ipv4Addr, audio: bool) -> Option<BayUid>
pub fn bay_by_stream_ip(&self, ip: Ipv4Addr, audio: bool) -> Option<BayUid>
The source bay advertising the given multicast group, for the video or the audio stream.
Sourcepub fn v2ip_sources(&self, uid: DeviceUid) -> Option<Vec<V2ipStreamSources>>
pub fn v2ip_sources(&self, uid: DeviceUid) -> Option<Vec<V2ipStreamSources>>
The V2IP streams a device advertises.
Sourcepub fn v2ip_details(&self, uid: DeviceUid) -> Option<DeviceV2ipDetails>
pub fn v2ip_details(&self, uid: DeviceUid) -> Option<DeviceV2ipDetails>
A V2IP device’s own encoder configuration.
Sourcepub fn v2ip_sink(&self, uid: DeviceUid) -> Option<DeviceV2ipSink>
pub fn v2ip_sink(&self, uid: DeviceUid) -> Option<DeviceV2ipSink>
The streams a V2IP sink is subscribed to.
Sourcepub fn v2ip_stats(&self, uid: DeviceUid) -> Option<V2ipDeviceStats>
pub fn v2ip_stats(&self, uid: DeviceUid) -> Option<V2ipDeviceStats>
Transport statistics a V2IP device reports.
Sourcepub fn v2ip_features(&self, uid: DeviceUid) -> Option<V2ipFpgaFeature>
pub fn v2ip_features(&self, uid: DeviceUid) -> Option<V2ipFpgaFeature>
What a V2IP device’s video processor supports.
None until the device has reported a non-empty mask about itself.
A device reports nothing at all while its processor has yet to answer,
and an older processor answers with none of the optional commands, so
“no features” and “not yet known” are the same bytes on the wire and
both read as None here. A mask that has arrived only gains bits.
Sourcepub fn v2ip_device_settings(&self, uid: DeviceUid) -> Option<V2ipDeviceSettings>
pub fn v2ip_device_settings(&self, uid: DeviceUid) -> Option<V2ipDeviceSettings>
A V2IP device’s settings, None until it has reported any.
A device reports only the settings it has, so one missing from
V2ipDeviceSettings::valid once the rest have arrived is one the
device does not have.
Sourcepub fn v2ip_vlan(&self, uid: DeviceUid) -> Option<V2ipVlan>
pub fn v2ip_vlan(&self, uid: DeviceUid) -> Option<V2ipVlan>
The VLAN configuration a V2IP device last reported about itself,
None until it has reported one.
Sourcepub fn unit_status(&self, uid: DeviceUid) -> Option<UnitStatus>
pub fn unit_status(&self, uid: DeviceUid) -> Option<UnitStatus>
The health and IP configuration a unit last reported, None until it
has reported them.
Sourcepub fn v2ip_ptp(&self, uid: DeviceUid) -> Option<V2ipPtpState>
pub fn v2ip_ptp(&self, uid: DeviceUid) -> Option<V2ipPtpState>
The PTP state a V2IP device last reported about itself, None until
it has reported one, and while it has no PTP to report: none on its
video processor, or in power save.
Sourcepub fn v2ip_testcard(&self, uid: DeviceUid) -> Option<V2ipTestcard>
pub fn v2ip_testcard(&self, uid: DeviceUid) -> Option<V2ipTestcard>
The test card a V2IP sink last reported, None until it has answered
Remote::request_v2ip_testcard or a change.
Sourcepub fn time_zone(&self, uid: DeviceUid) -> Option<TimeZone>
pub fn time_zone(&self, uid: DeviceUid) -> Option<TimeZone>
The time zone a device announced for its mesh, None until it has.
The mesh controller announces it with every periodic broadcast, empty when it has none.
Sourcepub fn device_clock(&self, uid: DeviceUid) -> Option<SystemTime>
pub fn device_clock(&self, uid: DeviceUid) -> Option<SystemTime>
A device’s clock as of now: the time it last announced, advanced by
how long ago that arrived. None until it has announced one.
The mesh controller announces its clock with every periodic broadcast, and only once it has been set.
Sourcepub fn v2ip_tiling(&self, uid: DeviceUid) -> Option<V2ipTilingConfig>
pub fn v2ip_tiling(&self, uid: DeviceUid) -> Option<V2ipTilingConfig>
The video-wall tiling a V2IP device is configured for.
Sourcepub fn audio_endpoints(&self, uid: DeviceUid) -> Option<AudioEndpoints>
pub fn audio_endpoints(&self, uid: DeviceUid) -> Option<AudioEndpoints>
The audio endpoint tree a device exposes.
Sourcepub fn multiviewer_status(&self, uid: DeviceUid) -> Option<MultiviewerStatus>
pub fn multiviewer_status(&self, uid: DeviceUid) -> Option<MultiviewerStatus>
The multiviewer layout a device is showing.
Sourcepub fn dolby_settings(&self, uid: DeviceUid) -> Option<AmpDolbySettings>
pub fn dolby_settings(&self, uid: DeviceUid) -> Option<AmpDolbySettings>
An amplifier’s Dolby decoder settings.
Sourcepub fn rc_settings(&self, uid: DeviceUid) -> Option<RcSettings>
pub fn rc_settings(&self, uid: DeviceUid) -> Option<RcSettings>
A device’s remote-control settings.
Sourcepub fn network_status(&self, uid: DeviceUid) -> Vec<NetworkPortStatus>
pub fn network_status(&self, uid: DeviceUid) -> Vec<NetworkPortStatus>
Every network port a device reports, in port order.
Sourcepub fn topology(&self, uid: DeviceUid) -> Vec<TopologyEntry>
pub fn topology(&self, uid: DeviceUid) -> Vec<TopologyEntry>
The mesh topology a device reports.
Sourcepub fn edid(&self, uid: DeviceUid, output: bool) -> Option<Vec<u8>>
pub fn edid(&self, uid: DeviceUid, output: bool) -> Option<Vec<u8>>
The EDID a device last reported: the display on its output, or the one it presents to the source on its input.
Filled in by a device’s answer to Remote::request_edid, and by any
answer to a peer’s request that this client happened to hear.
Sourcepub fn frames_received(&self) -> u64
pub fn frames_received(&self) -> u64
How many frames from other senders have parsed since this client started.
It separates a mesh with nothing on it from an interface nothing is on:
a client that has discovered no device but is counting frames is
hearing traffic it cannot get answers from, which on a multi-homed host
is what a wrong Config::local_ip looks like. Frames this client
sent are not counted, because the host loops its own multicast back
whichever interface was selected.
Sourcepub fn firmware(&self, uid: DeviceUid) -> Vec<(FirmwareType, FirmwareVersion)>
pub fn firmware(&self, uid: DeviceUid) -> Vec<(FirmwareType, FirmwareVersion)>
Every firmware image a device reports a version for.