Skip to main content

chessnut_move/protocol/
event.rs

1// Copyright 2026 Daymon Littrell-Reyes
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Decoded board and tracked-piece status values.
16
17use crate::protocol::{Color, Piece, PieceKind, Position};
18#[cfg(doc)]
19use crate::{protocol, transport};
20
21/// A decoded board-state or status notification.
22///
23/// # Examples
24///
25/// ```
26/// use chessnut_move::protocol::{BoardEvent, File, Rank, Square};
27///
28/// fn report(event: BoardEvent) {
29///     match event {
30///         BoardEvent::PositionChanged(position) => {
31///             let e4 = Square::new(File::E, Rank::Four);
32///             println!("e4 contains {:?}", position.piece_at(e4));
33///         }
34///         BoardEvent::BatteryStatus(status) => {
35///             println!("board battery: {}%", status.percentage);
36///         }
37///         BoardEvent::PieceStatus(status) => {
38///             let unavailable = status
39///                 .pieces
40///                 .iter()
41///                 .filter(|piece| piece.battery_percentage.is_none())
42///                 .count();
43///             println!("{unavailable} piece batteries are unavailable");
44///         }
45///     }
46/// }
47/// # let _ = report;
48/// ```
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
50pub enum BoardEvent {
51  /// The occupancy or piece arrangement of the 64 squares changed.
52  ///
53  /// This event is only fired when [realtime updates] is enabled, and there's not an active
54  /// [auto-move] occurring.
55  ///
56  /// [realtime updates]: protocol::Command::enable_realtime_updates
57  /// [auto-move]: protocol::Command::auto_move
58  PositionChanged(Position),
59
60  /// The board responded to a [battery-status query].
61  ///
62  /// [battery-status query]: protocol::Command::read_battery_level
63  BatteryStatus(BatteryStatus),
64
65  /// The board responded to a [piece-status query].
66  ///
67  /// [piece-status query]: protocol::Command::read_piece_status
68  PieceStatus(PieceStatus),
69}
70
71/// Charging state and battery percentage reported by the board.
72///
73/// This value is carried by [`BoardEvent::BatteryStatus`] after a
74/// [`Command::read_battery_level`][protocol::Command::read_battery_level]
75/// response is decoded.
76#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
77pub struct BatteryStatus {
78  /// Whether the board is actively plugged in and charging.
79  pub charging: bool,
80
81  /// The battery percentage of the board.
82  ///
83  /// The decoder guarantees a value from `0` through `100`.
84  pub percentage: u8,
85}
86
87/// Status reported for one tracked physical chess piece.
88///
89/// Coordinates use the board firmware's normalized `0..=255` coordinate
90/// system and are independent of [`Square`] indexes.
91///
92/// [`Default`] produces an unavailable white-pawn placeholder used to
93/// initialize fixed response arrays. A default value is not an observation
94/// received from a board.
95///
96/// [`Square`]: protocol::Square
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
98pub struct TrackedPieceStatus {
99  /// The chess piece that this status report represents.
100  pub piece: Piece,
101
102  /// The normalized X coordinate of the piece on the board.
103  ///
104  /// Values range from `0` through `255`.
105  pub x: u8,
106
107  /// The normalized Y coordinate of the piece on the board.
108  ///
109  /// Values range from `0` through `255`.
110  pub y: u8,
111
112  /// The piece's reported battery percentage.
113  ///
114  /// This is `None` when the board reports `0xff`, which indicates that the
115  /// piece's battery level is currently unavailable.
116  pub battery_percentage: Option<u8>,
117}
118
119impl Default for TrackedPieceStatus {
120  fn default() -> Self {
121    Self {
122      piece: Piece {
123        color: Color::White,
124        kind: PieceKind::Pawn,
125      },
126      x: 0,
127      y: 0,
128      battery_percentage: None,
129    }
130  }
131}
132
133/// Number of physical piece records in a tracked-piece response.
134///
135/// The Move protocol reports eight pawns, two rooks, two knights, two bishops,
136/// two queens, and one king for each color.
137pub const TRACKED_PIECE_COUNT: usize = 34;
138
139/// Status records for every physical piece tracked by the board.
140///
141/// Records are ordered as white pawns, rooks, knights, bishops, queens, king,
142/// followed by the corresponding black pieces.
143///
144/// This value is carried by [`BoardEvent::PieceStatus`] after
145/// [`Command::read_piece_status`][protocol::Command::read_piece_status] is sent.
146/// Low-level transport implementations can obtain it through
147/// [`decode_response_notification`][protocol::decode_response_notification].
148#[cfg_attr(
149  feature = "tokio",
150  doc = r#"
151# Examples
152
153Query the running board actor with
154[`BoardHandle::piece_status`](transport::tokio::BoardHandle::piece_status),
155then inspect the records returned by the board:
156
157```no_run
158use chessnut_move::transport::tokio::{BoardHandle, HandleError};
159
160async fn report_piece_batteries(board: &BoardHandle) -> Result<(), HandleError> {
161    let status = board.piece_status().await?;
162
163    for tracked in &status.pieces {
164        match tracked.battery_percentage {
165            Some(percentage) => {
166                println!("{:?}: {percentage}%", tracked.piece);
167            }
168            None => println!("{:?}: unavailable", tracked.piece),
169        }
170    }
171
172    Ok(())
173}
174```
175"#
176)]
177#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
178pub struct PieceStatus {
179  /// Piece records in the order defined by the Move protocol.
180  pub pieces: [TrackedPieceStatus; TRACKED_PIECE_COUNT],
181}