@@ -25,6 +25,7 @@ import (
2525 "github.com/arduino/arduino-cli/cli/errorcodes"
2626 "github.com/arduino/arduino-cli/cli/feedback"
2727 "github.com/arduino/arduino-cli/cli/instance"
28+ "github.com/arduino/arduino-cli/commands"
2829 "github.com/arduino/arduino-cli/commands/board"
2930 rpc "github.com/arduino/arduino-cli/rpc/commands"
3031 "github.com/arduino/arduino-cli/table"
@@ -43,15 +44,29 @@ func initListCommand() *cobra.Command {
4344
4445 listCommand .Flags ().StringVar (& listFlags .timeout , "timeout" , "0s" ,
4546 "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+
4650 return listCommand
4751}
4852
4953var listFlags struct {
5054 timeout string // Expressed in a parsable duration, is the timeout for the list and attach commands.
55+ watch bool
5156}
5257
5358// runListCommand detects and lists the connected arduino boards
5459func 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+
5570 if timeout , err := time .ParseDuration (listFlags .timeout ); err != nil {
5671 feedback .Errorf ("Invalid timeout: %v" , err )
5772 os .Exit (errorcodes .ErrBadArgument )
@@ -74,6 +89,48 @@ func runListCommand(cmd *cobra.Command, args []string) {
7489 feedback .PrintResult (result {ports })
7590}
7691
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+
77134// output from this command requires special formatting, let's create a dedicated
78135// feedback.Result implementation
79136type result struct {
@@ -134,3 +191,59 @@ func (dr result) String() string {
134191 }
135192 return t .Render ()
136193}
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