18
18
package board
19
19
20
20
import (
21
+ "encoding/json"
22
+ "fmt"
23
+ "io/ioutil"
24
+ "net/http"
25
+
26
+ "github.com/arduino/arduino-cli/cli/globals"
21
27
"github.com/arduino/arduino-cli/commands"
22
28
rpc "github.com/arduino/arduino-cli/rpc/commands"
23
29
"github.com/pkg/errors"
24
30
)
25
31
32
+ var (
33
+ // ErrNotFound is returned when the API returns 404
34
+ ErrNotFound = errors .New ("board not found" )
35
+ )
36
+
37
+ func apiByVidPid (url string ) ([]* rpc.BoardListItem , error ) {
38
+ retVal := []* rpc.BoardListItem {}
39
+ req , _ := http .NewRequest ("GET" , url , nil )
40
+ req .Header = globals .HTTPClientHeader
41
+ req .Header .Set ("Content-Type" , "application/json" )
42
+
43
+ if res , err := http .DefaultClient .Do (req ); err == nil {
44
+ if res .StatusCode >= 400 {
45
+ if res .StatusCode == 404 {
46
+ return nil , ErrNotFound
47
+ }
48
+ return nil , errors .Errorf ("the server responded with status %s" , res .Status )
49
+ }
50
+
51
+ body , _ := ioutil .ReadAll (res .Body )
52
+ res .Body .Close ()
53
+
54
+ var dat map [string ]interface {}
55
+ err = json .Unmarshal (body , & dat )
56
+ if err != nil {
57
+ return nil , errors .Wrap (err , "error processing response from server" )
58
+ }
59
+
60
+ name , nameFound := dat ["name" ].(string )
61
+ fqbn , fbqnFound := dat ["fqbn" ].(string )
62
+
63
+ if ! nameFound || ! fbqnFound {
64
+ return nil , errors .New ("wrong format in server response" )
65
+ }
66
+
67
+ retVal = append (retVal , & rpc.BoardListItem {
68
+ Name : name ,
69
+ FQBN : fqbn ,
70
+ })
71
+ } else {
72
+ return nil , errors .Wrap (err , "error querying Arduino Cloud Api" )
73
+ }
74
+
75
+ return retVal , nil
76
+ }
77
+
26
78
// List FIXMEDOC
27
- func List (instanceID int32 ) (* rpc.BoardListResp , error ) {
79
+ func List (instanceID int32 ) ([] * rpc.DetectedPort , error ) {
28
80
pm := commands .GetPackageManager (instanceID )
29
81
if pm == nil {
30
82
return nil , errors .New ("invalid instance" )
@@ -40,29 +92,51 @@ func List(instanceID int32) (*rpc.BoardListResp, error) {
40
92
}
41
93
defer serialDiscovery .Close ()
42
94
43
- resp := & rpc.BoardListResp {Ports : []* rpc.DetectedPort {}}
44
-
45
95
ports , err := serialDiscovery .List ()
46
96
if err != nil {
47
97
return nil , errors .Wrap (err , "error getting port list from serial-discovery" )
48
98
}
49
99
100
+ retVal := []* rpc.DetectedPort {}
50
101
for _ , port := range ports {
51
102
b := []* rpc.BoardListItem {}
103
+
104
+ // first query installed cores through the Package Manager
52
105
for _ , board := range pm .IdentifyBoard (port .IdentificationPrefs ) {
53
106
b = append (b , & rpc.BoardListItem {
54
107
Name : board .Name (),
55
108
FQBN : board .FQBN (),
56
109
})
57
110
}
111
+
112
+ // if installed cores didn't recognize the board, try querying
113
+ // the builder API
114
+ if len (b ) == 0 {
115
+ url := fmt .Sprintf ("https://builder.arduino.cc/v3/boards/byVidPid/%s/%s" ,
116
+ port .IdentificationPrefs .Get ("vid" ),
117
+ port .IdentificationPrefs .Get ("pid" ))
118
+ items , err := apiByVidPid (url )
119
+ if err == ErrNotFound {
120
+ // the board couldn't be detected, keep going with the next port
121
+ continue
122
+ } else if err != nil {
123
+ // this is bad, bail out
124
+ return nil , errors .Wrap (err , "error getting board info from Arduino Cloud" )
125
+ }
126
+
127
+ b = items
128
+ }
129
+
130
+ // boards slice can be empty at this point if neither the cores nor the
131
+ // API managed to recognize the connected board
58
132
p := & rpc.DetectedPort {
59
133
Address : port .Address ,
60
134
Protocol : port .Protocol ,
61
135
ProtocolLabel : port .ProtocolLabel ,
62
136
Boards : b ,
63
137
}
64
- resp . Ports = append (resp . Ports , p )
138
+ retVal = append (retVal , p )
65
139
}
66
140
67
- return resp , nil
141
+ return retVal , nil
68
142
}
0 commit comments