Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
rekram1-node committed Jan 1, 2023
0 parents commit 05e8756
Show file tree
Hide file tree
Showing 15 changed files with 855 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# macOS artifacts
.DS_Store

# task artifacts
.task

# environment config
.env

# goreleaser artifacts
dist

# vscode artifacts
.vscode

# used for testing, not necessary for library
main.go

# binary
blinkgo
50 changes: 50 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
run:
go: 1.19
concurrency: 4
timeout: 5m
modules-download-mode: readonly
build-tags:
- codeanalysis

linters:
enable:
- bidichk
- dogsled
- errorlint
- exhaustive
- forbidigo
- gocritic
- gocyclo
- gofmt
- goimports
- gomoddirectives
- gosec
- ifshort
- ireturn
- makezero
- misspell
- nakedret
- nilnil
- nolintlint
- promlinter
- predeclared
- stylecheck
# - tagliatelle
- testpackage
- thelper
- tenv
- unused
- whitespace
disable:
- deadcode
- errcheck
- godot
fast: false

gosec:
includes:
- G401
- G306
- G101
excludes:
- G204
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# blinkgo

Simple library for interacting with blink cameras, mainly: authentication, listing devices/networks/clips, and downloading clips from local storage

This library was made for my purposes but if you would like to see more features open an issue and I will get to it

## Installation

```shell
go get -u github.com/rekram1-node/blinkgo/blink
```

## Table of contents

* [authentication](#auth)
* [fetch user info](#user)
* [list cameras](#camera)
* [download videos](#videos)

## Usage

```shell
example
```

## Local Storage

I did not discover this myself, this is from [blinkpy](https://github.com/fronzbot/blinkpy)

The steps for pulling videos from local storage

1. Query sync module for information regarding stored clips
2. Upload the clips to the cloud
3. Download the clips from a cloud URL

Beware the upload/download sequence, there must be a waiting period between the two as the operation is not instantenous

## Issues

If you have an issue: report it on the [issue tracker](https://github.com/rekram1-node/blinkgo/issues)
54 changes: 54 additions & 0 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# https://taskfile.dev

version: '3'

vars:
NAME: 'blinkgo'

tasks:
clean:
cmds:
- go clean

test:
desc: "runs unit tests"
cmds:
- go test -v
sources:
- ./**/*.go

build:
cmds:
- go build -trimpath -ldflags "{{.BUILD_FLAGS}}" -o {{.NAME}}{{exeExt}} {{.CLI_ARGS}}
sources:
- ./**/*.go
generates:
- '{{.NAME}}{{exeExt}}'
method: checksum
env:
CGO_ENABLED: 0
GOOS: '{{default "" .BUILD_OS}}'
GOARCH: '{{default "" .BUILD_ARCH}}'
vars:
BUILD_FLAGS: '{{default "" .BUILD_FLAGS}}'

run:
deps: [ build ]
sources:
- ./**/*
- ./*
cmds:
- ./{{.NAME}}{{exeExt}} {{.CLI_ARGS}}

format:
cmds:
- gofmt -s -w .
sources:
- ./**/*.go

lint:
cmds:
- cmd: golangci-lint run -v -c ./.golangci.yml
ignore_error: true
sources:
- ./**/*.go
21 changes: 21 additions & 0 deletions blink/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package blink

type Account struct {
Email string
Password string
AuthToken string
Tier string

ID int
ClientID int

SyncModules *[]SyncModule
Networks *[]Network
}

func NewAccount(email, pass string) *Account {
return &Account{
Email: email,
Password: pass,
}
}
44 changes: 44 additions & 0 deletions blink/camera.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package blink

import (
"time"
)

type Camera struct {
ID int `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Serial string `json:"serial"`
FwVersion string `json:"fw_version"`
Type string `json:"type"`
Enabled bool `json:"enabled"`
Thumbnail string `json:"thumbnail"`
Status string `json:"status"`
Battery string `json:"battery"`
UsageRate bool `json:"usage_rate"`
NetworkID int `json:"network_id"`
Issues []interface{} `json:"issues"`
Signals struct {
Lfr int `json:"lfr"`
Wifi int `json:"wifi"`
Temp int `json:"temp"`
Battery int `json:"battery"`
} `json:"signals"`
LocalStorageEnabled bool `json:"local_storage_enabled"`
LocalStorageCompatible bool `json:"local_storage_compatible"`
Snooze bool `json:"snooze"`
SnoozeTimeRemaining interface{} `json:"snooze_time_remaining"`
Revision string `json:"revision"`
Color string `json:"color"`
}

func (account *Account) GetCameras() ([]Camera, error) {
manifest, err := account.GetManifest()

if err != nil {
return nil, err
}

return manifest.Cameras, nil
}
101 changes: 101 additions & 0 deletions blink/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package blink

import (
"fmt"

"github.com/rekram1-node/blinkgo/internal/client"
)

type LoginResponse struct {
Account AccountInformation `json:"account"`
Auth Auth `json:"auth"`
Phone Phone `json:"phone"`
Verification Verification `json:"verification"`
LockoutTimeRemaining int `json:"lockout_time_remaining"`
ForcePasswordReset bool `json:"force_password_reset"`
AllowPinResendSeconds int `json:"allow_pin_resend_seconds"`
}

type AccountInformation struct {
AccountID int `json:"account_id"`
UserID int `json:"user_id"`
ClientID int `json:"client_id"`
ClientTrusted bool `json:"client_trusted"`
NewAccount bool `json:"new_account"`
Tier string `json:"tier"`
Region string `json:"region"`
AccountVerificationRequired bool `json:"account_verification_required"`
PhoneVerificationRequired bool `json:"phone_verification_required"`
ClientVerificationRequired bool `json:"client_verification_required"`
RequireTrustClientDevice bool `json:"require_trust_client_device"`
CountryRequired bool `json:"country_required"`
VerificationChannel string `json:"verification_channel"`
User User `json:"user"`
AmazonAccountLinked bool `json:"amazon_account_linked"`
}

type User struct {
UserID int `json:"user_id"`
Country string `json:"country"`
}

type Auth struct {
Token string `json:"token"`
}

type Phone struct {
Number string `json:"number"`
Last4Digits string `json:"last_4_digits"`
CountryCallingCode string `json:"country_calling_code"`
Valid bool `json:"valid"`
}

type Verification struct {
Email struct {
Required bool `json:"required"`
} `json:"email"`
Phone struct {
Required bool `json:"required"`
Channel string `json:"channel"`
} `json:"phone"`
}

func (account *Account) Login() (*LoginResponse, error) {
body := map[string]interface{}{
"reauth": true,
"email": account.Email,
"password": account.Password,
}

loginResp := &LoginResponse{}
c := client.Default()
url := client.LoginURL

resp, err := c.R().
SetBody(body).
SetResult(loginResp).
Post(url)

if err != nil {
return nil, err
} else if !resp.IsSuccess() {
return nil, fmt.Errorf("failed to login, status code: %d, response: %s", resp.StatusCode(), resp.String())
}

account.updateAccountInfo(loginResp)

return loginResp, nil
}

func (account *Account) updateAccountInfo(resp *LoginResponse) {
account.Tier = resp.Account.Tier
account.ID = resp.Account.AccountID
account.ClientID = resp.Account.ClientID
account.AuthToken = resp.Auth.Token
}

func (account *Account) RefreshToken() (*LoginResponse, error) {
// this method is incomplete
// this just for readability of code I created RefreshToken
return account.Login()
}
27 changes: 27 additions & 0 deletions blink/logout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package blink

import (
"fmt"

"github.com/rekram1-node/blinkgo/internal/client"
)

func (account *Account) Logout() error {
// logoutRes := &LogoutRes{}
c := client.New(account.AuthToken)
url := fmt.Sprintf("https://rest-%s.immedia-semi.com/api/v4/account/%d/client/%d/logout", account.Tier, account.ID, account.ClientID)
resp, err := c.R().
// SetResult(logoutRes).
Post(url)

if err != nil {
return err
} else if !resp.IsSuccess() {
return fmt.Errorf("failed to logout, status code: %d, response: %s", resp.StatusCode(), resp.String())
}

// TODO convert to object
fmt.Println(resp.String())
// return logoutRes, nil
return nil
}
Loading

0 comments on commit 05e8756

Please sign in to comment.