@@ -25,6 +25,7 @@ import (
25
25
"github.com/arduino/arduino-cli/cli/errorcodes"
26
26
"github.com/arduino/arduino-cli/cli/feedback"
27
27
"github.com/arduino/arduino-cli/cli/instance"
28
+ "github.com/arduino/arduino-cli/commands"
28
29
"github.com/arduino/arduino-cli/commands/board"
29
30
rpc "github.com/arduino/arduino-cli/rpc/commands"
30
31
"github.com/arduino/arduino-cli/table"
@@ -43,15 +44,29 @@ func initListCommand() *cobra.Command {
43
44
44
45
listCommand .Flags ().StringVar (& listFlags .timeout , "timeout" , "0s" ,
45
46
"The connected devices search timeout, raise it if your board doesn't show up (e.g. to 10s)." )
47
+ listCommand .Flags ().BoolVarP (& listFlags .watch , "watch" , "w" , false ,
48
+ "Command keeps running and prints list of connected boards whenever there is a change." )
49
+
46
50
return listCommand
47
51
}
48
52
49
53
var listFlags struct {
50
54
timeout string // Expressed in a parsable duration, is the timeout for the list and attach commands.
55
+ watch bool
51
56
}
52
57
53
58
// runListCommand detects and lists the connected arduino boards
54
59
func runListCommand (cmd * cobra.Command , args []string ) {
60
+ if listFlags .watch {
61
+ inst , err := instance .CreateInstance ()
62
+ if err != nil {
63
+ feedback .Errorf ("Error detecting boards: %v" , err )
64
+ os .Exit (errorcodes .ErrGeneric )
65
+ }
66
+ watchList (cmd , inst )
67
+ os .Exit (0 )
68
+ }
69
+
55
70
if timeout , err := time .ParseDuration (listFlags .timeout ); err != nil {
56
71
feedback .Errorf ("Invalid timeout: %v" , err )
57
72
os .Exit (errorcodes .ErrBadArgument )
@@ -74,6 +89,48 @@ func runListCommand(cmd *cobra.Command, args []string) {
74
89
feedback .PrintResult (result {ports })
75
90
}
76
91
92
+ func watchList (cmd * cobra.Command , inst * rpc.Instance ) {
93
+ pm := commands .GetPackageManager (inst .Id )
94
+ eventsChan , err := commands .WatchListBoards (pm )
95
+ if err != nil {
96
+ feedback .Errorf ("Error detecting boards: %v" , err )
97
+ os .Exit (errorcodes .ErrNetwork )
98
+ }
99
+
100
+ // This is done to avoid printing the header each time a new event is received
101
+ if feedback .GetFormat () == feedback .Text {
102
+ t := table .New ()
103
+ t .SetHeader ("Port" , "Type" , "Event" , "Board Name" , "FQBN" , "Core" )
104
+ feedback .Print (t .Render ())
105
+ }
106
+
107
+ for event := range eventsChan {
108
+ boards := []* rpc.BoardListItem {}
109
+ if event .Type == "add" {
110
+ boards , err = board .Identify (pm , & commands.BoardPort {
111
+ Address : event .Port .Address ,
112
+ Label : event .Port .AddressLabel ,
113
+ Prefs : event .Port .Properties ,
114
+ IdentificationPrefs : event .Port .IdentificationProperties ,
115
+ Protocol : event .Port .Protocol ,
116
+ ProtocolLabel : event .Port .ProtocolLabel ,
117
+ })
118
+ if err != nil {
119
+ feedback .Errorf ("Error identifying board: %v" , err )
120
+ os .Exit (errorcodes .ErrNetwork )
121
+ }
122
+ }
123
+
124
+ feedback .PrintResult (watchEvent {
125
+ Type : event .Type ,
126
+ Address : event .Port .Address ,
127
+ Protocol : event .Port .Protocol ,
128
+ ProtocolLabel : event .Port .ProtocolLabel ,
129
+ Boards : boards ,
130
+ })
131
+ }
132
+ }
133
+
77
134
// output from this command requires special formatting, let's create a dedicated
78
135
// feedback.Result implementation
79
136
type result struct {
@@ -134,3 +191,59 @@ func (dr result) String() string {
134
191
}
135
192
return t .Render ()
136
193
}
194
+
195
+ type watchEvent struct {
196
+ Type string `json:"type"`
197
+ Address string `json:"address,omitempty"`
198
+ Protocol string `json:"protocol,omitempty"`
199
+ ProtocolLabel string `json:"protocol_label,omitempty"`
200
+ Boards []* rpc.BoardListItem `json:"boards,omitempty"`
201
+ }
202
+
203
+ func (dr watchEvent ) Data () interface {} {
204
+ return dr
205
+ }
206
+
207
+ func (dr watchEvent ) String () string {
208
+ t := table .New ()
209
+
210
+ event := map [string ]string {
211
+ "add" : "Connected" ,
212
+ "remove" : "Disconnected" ,
213
+ }[dr .Type ]
214
+
215
+ address := fmt .Sprintf ("%s://%s" , dr .Protocol , dr .Address )
216
+ if dr .Protocol == "serial" || dr .Protocol == "" {
217
+ address = dr .Address
218
+ }
219
+ protocol := dr .ProtocolLabel
220
+ if boards := dr .Boards ; len (boards ) > 0 {
221
+ sort .Slice (boards , func (i , j int ) bool {
222
+ x , y := boards [i ], boards [j ]
223
+ return x .GetName () < y .GetName () || (x .GetName () == y .GetName () && x .GetFQBN () < y .GetFQBN ())
224
+ })
225
+ for _ , b := range boards {
226
+ board := b .GetName ()
227
+
228
+ // to improve the user experience, show on a dedicated column
229
+ // the name of the core supporting the board detected
230
+ var coreName = ""
231
+ fqbn , err := cores .ParseFQBN (b .GetFQBN ())
232
+ if err == nil {
233
+ coreName = fmt .Sprintf ("%s:%s" , fqbn .Package , fqbn .PlatformArch )
234
+ }
235
+
236
+ t .AddRow (address , protocol , event , board , fqbn , coreName )
237
+
238
+ // reset address and protocol, we only show them on the first row
239
+ address = ""
240
+ protocol = ""
241
+ }
242
+ } else {
243
+ board := ""
244
+ fqbn := ""
245
+ coreName := ""
246
+ t .AddRow (address , protocol , event , board , fqbn , coreName )
247
+ }
248
+ return t .Render ()
249
+ }
0 commit comments