Skip to main content

plm/
message.rs

1use std::{convert::TryFrom, fmt};
2
3use crate::error::*;
4use crate::frame::*;
5
6/// A [Command] (two, actually) is sent in a [Message].
7/// This type has some commonly used ones, but you can send
8/// arbitrary values via [Command::Other].
9#[derive(Copy, Clone, Debug, PartialEq)]
10pub enum Command {
11    /// When sent to a device, turns the device on.
12    /// When received, it indicates that the device was turned on by manipulation.
13    On,
14
15    /// When sent to a device, turns the device on faster, e.g. no ramping.
16    /// When received, it indicates that the device performed a "fast on",
17    /// usually by a double-tapped switch.
18    OnFast,
19
20    /// When sent to a device, turns the device off.
21    /// When received, it indicates that the device was turned off by manipulation.
22    Off,
23
24    /// When sent to a device, turns the device off faster, e.g. no ramping.
25    /// When received, it indicates that the device performed a "fast off",
26    /// usually by a double-tapped switch.
27    OffFast,
28
29    /// Ping the device.
30    Ping,
31
32    /// Retrieves the protocol version information.
33    VersionQuery,
34
35    /// Cancels linking mode for the device.
36    CancelLinking,
37
38    /// Starts linking mode for the device.
39    StartLinking,
40
41    /// Queries the status of the device.
42    StatusRequest,
43
44    /// Causes the device to beep once.
45    Beep,
46
47    /// Arbitrary commands not covered by one of the cases above.
48    Other(u8),
49
50    None,
51}
52
53impl Default for Command {
54    fn default() -> Self {
55        Command::None
56    }
57}
58
59impl fmt::Display for Command {
60    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        write!(f, "{:?}", self)
62    }
63}
64
65impl From<u8> for Command {
66    fn from(b: u8) -> Self {
67        use Command::*;
68        match b {
69            0x08u8 => CancelLinking,
70            0x09u8 => StartLinking,
71            0x0du8 => VersionQuery,
72            0x0fu8 => Ping,
73            0x19u8 => StatusRequest,
74            0x11u8 => On,
75            0x12u8 => OnFast,
76            0x13u8 => Off,
77            0x14u8 => OffFast,
78            0x30u8 => Beep,
79            0 => None,
80            _ => Other(b),
81        }
82    }
83}
84
85impl From<Command> for u8 {
86    fn from(c: Command) -> Self {
87        use Command::*;
88        match c {
89            On => 0x11u8,
90            OnFast => 0x12u8,
91            Off => 0x13u8,
92            OffFast => 0x14u8,
93            Ping => 0x0fu8,
94            VersionQuery => 0x0du8,
95            CancelLinking => 0x08u8,
96            StartLinking => 0x09u8,
97            StatusRequest => 0x19u8,
98            Beep => 0x30u8,
99            Other(cmd) => cmd,
100            None => 0u8,
101        }
102    }
103}
104
105/// A [Message] can be sent to a device with a given [Address].
106#[derive(Copy, Clone, Debug, PartialEq)]
107pub struct Message {
108    /// The address of the device that sent the `Message`.
109    pub from: Address,
110
111    /// The address of the receipient of the `Message`.
112    pub to: Address,
113
114    /// Flags describing various attributes of the `Message`.
115    pub flags: MessageFlags,
116
117    /// The number of hops remaining
118    pub hops_remaining: u8,
119
120    /// The maximum number of hops allowed for this `Message`.
121    pub max_hops: u8,
122
123    /// The first `Command` contained in the `Message`.
124    pub cmd1: Command,
125
126    /// The second `Command` contained in the `Message`. Often this is a group number or other
127    /// details accompanying `cmd1`.
128    pub cmd2: Command,
129
130    /// Arbitrary user data, only available in an extended `Message`.
131    pub data: [u8; 14],
132}
133
134impl Message {
135    /// Returns true if `other` is an ACK of `self`.
136    pub fn is_ack(&self, other: &Message) -> bool {
137        match *other {
138            Message { from, flags, .. } if self.to == from && flags.contains(MessageFlags::ACK) => {
139                true
140            }
141            _ => false,
142        }
143    }
144}
145
146impl Default for Message {
147    fn default() -> Self {
148        Message {
149            from: Address::default(),
150            to: Address::default(),
151            flags: MessageFlags::default(),
152            hops_remaining: 3,
153            max_hops: 3,
154            cmd1: Command::default(),
155            cmd2: Command::default(),
156            data: [0u8; 14],
157        }
158    }
159}
160
161impl From<(Address, Command)> for Message {
162    fn from(c: (Address, Command)) -> Self {
163        let mut msg = Message::default();
164        let (to, cmd1) = c;
165        msg.to = to;
166        msg.cmd1 = cmd1;
167        msg
168    }
169}
170
171impl From<(Address, Command, Command)> for Message {
172    fn from(c: (Address, Command, Command)) -> Self {
173        let mut msg = Message::default();
174        let (to, cmd1, cmd2) = c;
175        msg.to = to;
176        msg.cmd1 = cmd1;
177        msg.cmd2 = cmd2;
178        msg
179    }
180}
181
182impl From<(Address, Command, MessageFlags)> for Message {
183    fn from(c: (Address, Command, MessageFlags)) -> Self {
184        let mut msg = Message::default();
185        let (to, cmd1, flags) = c;
186        msg.to = to;
187        msg.cmd1 = cmd1;
188        msg.flags = flags;
189        msg
190    }
191}
192
193impl From<(Address, Command, Command, MessageFlags)> for Message {
194    fn from(c: (Address, Command, Command, MessageFlags)) -> Self {
195        let mut msg = Message::default();
196        let (to, cmd1, cmd2, flags) = c;
197        msg.to = to;
198        msg.cmd1 = cmd1;
199        msg.cmd2 = cmd2;
200        msg.flags = flags;
201        msg
202    }
203}
204
205impl TryFrom<Frame> for Message {
206    type Error = Error;
207
208    fn try_from(frame: Frame) -> Result<Self, Self::Error> {
209        match frame {
210            Frame::StandardInsteonReceive {
211                from,
212                to,
213                flags,
214                hops_remaining,
215                max_hops,
216                cmd1,
217                cmd2,
218            } => Ok(Message {
219                from,
220                to,
221                flags,
222                hops_remaining,
223                max_hops,
224                cmd1: cmd1.into(),
225                cmd2: cmd2.into(),
226                data: [0u8; 14],
227            }),
228            Frame::ExtendedInsteonReceive {
229                from,
230                to,
231                flags,
232                hops_remaining,
233                max_hops,
234                cmd1,
235                cmd2,
236                data,
237            } => Ok(Message {
238                from,
239                to,
240                flags,
241                hops_remaining,
242                max_hops,
243                cmd1: cmd1.into(),
244                cmd2: cmd2.into(),
245                data,
246            }),
247            _ => Err(Error::UnexpectedResponse),
248        }
249    }
250}