Created
September 26, 2023 14:37
-
-
Save kolypto/7ff7008d2267de424ccc95ac5b10fcba to your computer and use it in GitHub Desktop.
Revisions
-
kolypto created this gist
Sep 26, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,165 @@ # Starline API Чтобы получить доступ, нужно войти под своей учетной записью на my.starline.ru и перейти на страницу https://my.starline.ru/developer. После заполнения формы, заявку на предоставление доступа к API для аккаунта рассмотрят сотрудники StarLine. Получение кода приложения для дальнейшего получения токена. Срок годности кода приложения – 1 час. ```console $ http GET https://id.starline.ru/apiV3/application/getCode appId==123456 secret==(echo -n "<secret>" | md5sum | cut -d' ' -f1) { "desc": { "code": "9a28848db17ef477f4896ac4ecf360ad" }, "state": 1 } ``` Получение токена приложения для дальнейшей авторизации. Время жизни токена приложения – 4 часа. ```console $ http GET https://id.starline.ru/apiV3/application/getToken appId==123456 secret==(echo -n "<secret><code>" | md5sum | cut -d' ' -f1) { "desc": { "token": "b8acd7d7fc95d3bfe1e4228f6374738fb9591fd79fb1937d7258916935463276" }, "state": 1 } ``` Получение user_token: ```console $ http --form POST https://id.starline.ru/apiV3/user/login Token:b8acd7d7fc95d3bfe1e4228f6374738fb9591fd79fb1937d7258916935463276 login=user@example.com pass=(echo -n "<password>" | sha1sum | cut -d' ' -f1) { "desc": { "_check_password_strength": 1, "auth_contact_id": null, "avatar": "default", "company_name": "", "date_register": "2015-02-11 19:17:22", "first_name": "John", "gmt": "+3", "id": "111222", "lang": "ru", "last_auth_date": "2023-09-22 23:01:09", "last_auth_ip": "109.168.229.180", "last_name": "Smith", "login": "UserName", "middle_name": "", "roles": [ "user", "open-api-user" ], "sex": "M", "state": "ACTIVE", "subscription": null, "user_token": "11b1b9600d18d3e2c26777aea56ddd16:238311" }, "state": 1 } ``` Полученный в результате успешного выполнения команды cookie необходимо использовать в методах WebAPI. Данный токен действителен 24 часа. ```console $ http POST https://developer.starline.ru/json/v2/auth.slid slid_token=11b1b9600d18d3e2c26777aea56ddd16:111222 Set-Cookie: slnet=BA3D4AB891E5E576A12C57B0812D4F99; { "code": "200", "codestring": "OK", "nchan_id": "DF0EC51FE9481C57DBE4706E0BB25ED8", "realplexor_id": "DF0EC51FE9481C57DBE4706E0BB25ED8", "user_id": "111222" } ``` Теперь можно получать информацию о машине: ```console $ http GET https://developer.starline.ru/json/v1/user/<user_id>/user_info Cookie:slnet=BA3D4AB891E5E576A12C57B0812D4F99 { "code": 200, "codestring": "OK", "devices": [ { "alias": "Lada Kalina", "balance": 865, "battery": 12.73, "car_alr_state": { "add_h": null, "add_l": null, "door": 2, "hbrake": null, "hijack": null, "hood": 2, "ign": 2, "pbrake": null, "shock_h": null, "shock_l": null, "tilt": null, "trunk": 2 }, "car_state": { "add_sens_bpass": 0, "alarm": 2, "arm": 1, "door": 2, "dvr": 0, "hbrake": 2, "hijack": 2, "hood": 2, "ign": 2, "out": 2, "pbrake": 2, "r_start": 2, "relay": 0, "run": 2, "shock_bpass": 0, "tilt_bpass": 0, "trunk": 2, "valet": 2, "webasto": 2 }, "ctemp": 29, "device_id": "11223344", "diag": { "can_descr": "5271", "can_version": "4.4.0", "vin": "" }, "etemp": 74, "fw_version": "FG33-P4,GK74-P7", "gps_lvl": 0, "gsm_lvl": 6, "hchan_channel": null, "imei": "112233445566", "mayak_temp": 865, "mon_type": 2, "phone": "+79991112233", "position": { "dir": null, "r": 179, "s": null, "sat_qty": null, "ts": 748729163, "x": "00.000000", "y": "00.000000" }, "reg": null, "rpl_channel": null, "sn": null, "status": 2, "ts_activity": 748700007, "type": 10 } ], "shared_devices": [] } ``` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,265 @@ // LICENSE: MIT // Author: Mark Vartanyan <kolypto@gmail.com> package main import ( "crypto/md5" "crypto/sha1" "encoding/hex" "fmt" "strconv" "time" "github.com/cockroachdb/errors" "github.com/go-resty/resty/v2" ) func main() { for { starline := NewStarlineClient() err := starline.Authenticate(12345, "<app-secret>", "user@example.com", "<password>") if err != nil { fmt.Printf("err: %+v\n", err) // Sleep, reconnect time.Sleep(10 * time.Second) } for { info, err := starline.GetUserInfo() if errors.Is(err, ErrUnauthorized) { fmt.Printf("err: Unauthorized. Need to sign in again\n") fmt.Printf("err: %v\n", err) return } if err != nil { fmt.Printf("err: %+v\n", err) break } fmt.Printf("info: %+v\n", info) isCarRunning := info.Devices[0].CarState.Ignition == 0 fmt.Printf("isCarRunning: %v\n", isCarRunning) // {Ignition:2}: parked // Give it a break time.Sleep(60 * time.Second) } } } func NewStarlineClient() *StarlineClient { return &StarlineClient{ client: resty.New(), } } type StarlineClient struct { client *resty.Client // User id from authentication userId string } func (s *StarlineClient) Authenticate(appId int, appSecret string, login string, password string) error { // Получение кода приложения для дальнейшего получения токена. Срок годности кода приложения – 1 час. // $ http GET https://id.starline.ru/apiV3/application/getCode \ // appId==123456 secret==(echo -n "SeCrEt" | md5sum | cut -d' ' -f1) // { "desc": { "code": "9a28848db17ef477f4896ac4ecf360ad" }, "state": 1 } appSecretMd5Bytes := md5.Sum([]byte(appSecret)) appSecretMd5 := hex.EncodeToString(appSecretMd5Bytes[:]) var getCodeResult struct { // Result: 0 failure, 1 ok State int `json:"state"` Desc struct { // Success Code string `json:"code"` // Failure Message string `json:"message"` } `json:"desc"` } res, err := s.client.R(). SetResult(&getCodeResult). SetQueryParam("appId", strconv.Itoa(appId)). SetQueryParam("secret", appSecretMd5). Get("https://id.starline.ru/apiV3/application/getCode") if DEBUG { fmt.Printf("getCode:\n") fmt.Printf(" res.Request.URL: %v\n", res.Request.URL) fmt.Printf(" getCodeResult: %+v\n", getCodeResult) fmt.Printf(" res: %v\n", res) } if err != nil { return errors.Wrap(err, "failed to getCode") } if getCodeResult.State != 1 { return errors.Errorf("failed to getCode: %d %s", getCodeResult.State, getCodeResult.Desc.Message) } // Получение токена приложения для дальнейшей авторизации. Время жизни токена приложения – 4 часа. // $ http GET https://id.starline.ru/apiV3/application/getToken \ // appId==123456 secret==(echo -n "<secret><code>" | md5sum | cut -d' ' -f1) // { "desc": { "token": "b8acd7d7fc95d3bfe1e4228f6374738fb9591fd79fb1937d7258916935463276" }, "state": 1 } secretWithCodeBytes := md5.Sum([]byte(appSecret + getCodeResult.Desc.Code)) secretWithCode := hex.EncodeToString(secretWithCodeBytes[:]) var getTokenResult struct { // Result: 0 failure, 1 ok State int `json:"state"` Desc struct { // Success Token string `json:"token"` // Failure Message string `json:"message"` } `json:"desc"` } res, err = s.client.R(). SetResult(&getTokenResult). SetQueryParam("appId", strconv.Itoa(appId)). SetQueryParam("secret", secretWithCode). Get("https://id.starline.ru/apiV3/application/getToken") if DEBUG { fmt.Printf("getToken:\n") fmt.Printf(" res.Request.URL: %v\n", res.Request.URL) fmt.Printf(" getTokenResult: %+v\n", getTokenResult) fmt.Printf(" res: %v\n", res) } if err != nil { return errors.Wrap(err, "failed to getToken") } if getTokenResult.State != 1 { return errors.Errorf("failed to getToken: %d %s", getTokenResult.State, getTokenResult.Desc.Message) } // Получение user_token // $ http --form POST https://id.starline.ru/apiV3/user/login \ // Token:b8acd7d7fc95d3bfe1e4228f6374738fb9591fd79fb1937d7258916935463276 \ // login=user@example.com pass=(echo -n "PaSsWoRd" | sha1sum | cut -d' ' -f1) // { "desc": { "user_token": "11b1b9600d18d3e2c26777aea56ddd16:238311" }, "state": 1 } passwordSha1Bytes := sha1.Sum([]byte(password)) passwordSha1 := hex.EncodeToString(passwordSha1Bytes[:]) var loginResult struct { // Result: 0 failure, 1 ok State int `json:"state"` Desc struct { // Success UserToken string `json:"user_token"` // Failure Message string `json:"message"` } `json:"desc"` } res, err = s.client.R(). SetResult(&loginResult). SetHeader("Token", getTokenResult.Desc.Token). SetFormData(map[string]string{ "login": login, "pass": passwordSha1, }). Post("https://id.starline.ru/apiV3/user/login") if DEBUG { fmt.Printf("login:\n") fmt.Printf(" res.Request.URL: %v\n", res.Request.URL) fmt.Printf(" loginResult: %v\n", loginResult) fmt.Printf(" res: %v\n", res) } if err != nil { return errors.Wrap(err, "failed to login") } if loginResult.State != 1 { return errors.Errorf("failed to login: %d %s", loginResult.State, loginResult.Desc.Message) } // Полученный в результате успешного выполнения команды cookie необходимо использовать в методах WebAPI. // Данный токен действителен 24 часа. // $ http POST https://developer.starline.ru/json/v2/auth.slid \ // slid_token=11b1b9600d18d3e2c26777aea56ddd16:238311 // Set-Cookie: slnet=BA3D4AB891E5E576A12C57B0812D4F99; // { "code": "200", "codestring": "OK" } var authSlidResult struct { // "200" ok Code string `json:"code"` CodeString string `json:"codestring"` // Use in URL UserId string `json:"user_id"` } res, err = s.client.R(). SetResult(&authSlidResult). SetBody(struct { SlidToken string `json:"slid_token"` }{ SlidToken: loginResult.Desc.UserToken, }). Post("https://developer.starline.ru/json/v2/auth.slid") if DEBUG { fmt.Printf("auth.slid:\n") fmt.Printf(" res.Request.URL: %v\n", res.Request.URL) fmt.Printf(" authSlidResult: %v\n", authSlidResult) fmt.Printf(" res: %v\n", res) fmt.Printf(" res.Cookies(): %v\n", res.Cookies()) } if err != nil { return errors.Wrap(err, "failed to auth.slid") } if authSlidResult.Code != "200" { return errors.Errorf("failed to getToken: %d %s", getTokenResult.State, getTokenResult.Desc.Message) } s.userId = authSlidResult.UserId return nil } // Get info about cars func (s *StarlineClient) GetUserInfo() (result UserInfo, err error) { // Теперь можно получать информацию о машине: // $ http GET https://developer.starline.ru/json/v1/user/<user_id>/user_info \ // Cookie:slnet=BA3D4AB891E5E576A12C57B0812D4F99 res, err := s.client.R(). SetResult(&result). Get("https://developer.starline.ru/json/v1/user/" + s.userId + "/user_info") if DEBUG { fmt.Printf("user_info:\n") fmt.Printf(" res.Request.URL: %v\n", res.Request.URL) fmt.Printf(" result: %+v\n", result) fmt.Printf(" res: %v\n", res) fmt.Printf(" res.Request.Cookies: %v\n", res.Request.Cookies) } if result.Code == 403 { err = errors.CombineErrors(ErrUnauthorized, err) return } if result.Code != 200 { err = errors.Errorf("failed to get user info: %d %s", result.Code, result.CodeString) return } return } // Unauthorized. Need to sign in. var ErrUnauthorized = errors.New("Unauthorized") type UserInfo struct { Code int `json:"code"` CodeString string `json:"codestring"` // Cars Devices []StarlineDevice } type StarlineDevice struct { Alias string `json:"alias"` CarState struct { // Ignition running? // 2 = off, 1 = engine running Ignition int `json:"ign"` } `json:"car_state"` } // Is the engine running? func (d StarlineDevice) IsEngineRunning() bool { return d.CarState.Ignition == 1 } const DEBUG = false