refactor
This commit is contained in:
+158
-289
@@ -17,25 +17,23 @@ import (
|
||||
)
|
||||
|
||||
type TuyaClient struct {
|
||||
httpClient *http.Client
|
||||
mqtt *TuyaMQTT
|
||||
apiURL string
|
||||
rtspURL string
|
||||
hlsURL string
|
||||
sessionId string
|
||||
clientId string
|
||||
clientSecret string
|
||||
deviceId string
|
||||
accessToken string
|
||||
refreshToken string
|
||||
expireTime int64
|
||||
uid string
|
||||
motoId string
|
||||
auth string
|
||||
skill *Skill
|
||||
iceServers []pionWebrtc.ICEServer
|
||||
medias []*core.Media
|
||||
hasBackchannel bool
|
||||
httpClient *http.Client
|
||||
mqtt *TuyaMQTT
|
||||
apiURL string
|
||||
rtspURL string
|
||||
hlsURL string
|
||||
sessionId string
|
||||
clientId string
|
||||
clientSecret string
|
||||
deviceId string
|
||||
accessToken string
|
||||
refreshToken string
|
||||
expireTime int64
|
||||
uid string
|
||||
motoId string
|
||||
auth string
|
||||
skill *Skill
|
||||
iceServers []pionWebrtc.ICEServer
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
@@ -159,21 +157,16 @@ type OpenIoTHubConfigResponse struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
defaultTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
func NewTuyaClient(openAPIURL string, deviceId string, uid string, clientId string, clientSecret string, streamMode string) (*TuyaClient, error) {
|
||||
func NewTuyaClient(openAPIURL string, deviceId string, uid string, clientId string, clientSecret string, streamMode string, streamRole string) (*TuyaClient, error) {
|
||||
client := &TuyaClient{
|
||||
httpClient: &http.Client{Timeout: defaultTimeout},
|
||||
mqtt: &TuyaMQTT{waiter: core.Waiter{}},
|
||||
apiURL: openAPIURL,
|
||||
sessionId: core.RandString(6, 62),
|
||||
clientId: clientId,
|
||||
deviceId: deviceId,
|
||||
clientSecret: clientSecret,
|
||||
uid: uid,
|
||||
hasBackchannel: false,
|
||||
httpClient: &http.Client{Timeout: 5 * time.Second},
|
||||
mqtt: &TuyaMQTT{waiter: core.Waiter{}},
|
||||
apiURL: openAPIURL,
|
||||
sessionId: core.RandString(6, 62),
|
||||
clientId: clientId,
|
||||
deviceId: deviceId,
|
||||
clientSecret: clientSecret,
|
||||
uid: uid,
|
||||
}
|
||||
|
||||
if err := client.InitToken(); err != nil {
|
||||
@@ -189,7 +182,7 @@ func NewTuyaClient(openAPIURL string, deviceId string, uid string, clientId stri
|
||||
return nil, fmt.Errorf("failed to get HLS URL: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := client.InitDevice(); err != nil {
|
||||
if err := client.InitDevice(streamRole); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize device: %w", err)
|
||||
}
|
||||
|
||||
@@ -206,6 +199,135 @@ func (c *TuyaClient) Close() {
|
||||
c.httpClient.CloseIdleConnections()
|
||||
}
|
||||
|
||||
func (c *TuyaClient) InitToken() (err error) {
|
||||
url := fmt.Sprintf("https://%s/v1.0/token?grant_type=1", c.apiURL)
|
||||
|
||||
c.accessToken = ""
|
||||
c.refreshToken = ""
|
||||
|
||||
body, err := c.Request("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tokenResponse TokenResponse
|
||||
err = json.Unmarshal(body, &tokenResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !tokenResponse.Success {
|
||||
return fmt.Errorf(tokenResponse.Msg)
|
||||
}
|
||||
|
||||
c.accessToken = tokenResponse.Result.AccessToken
|
||||
c.refreshToken = tokenResponse.Result.RefreshToken
|
||||
c.expireTime = tokenResponse.Result.ExpireTime
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) InitDevice(streamRole string) (err error) {
|
||||
url := fmt.Sprintf("https://%s/v1.0/users/%s/devices/%s/webrtc-configs", c.apiURL, c.uid, c.deviceId)
|
||||
|
||||
body, err := c.Request("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var webRTCConfigResponse WebRTCConfigResponse
|
||||
err = json.Unmarshal(body, &webRTCConfigResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !webRTCConfigResponse.Success {
|
||||
return fmt.Errorf(webRTCConfigResponse.Msg)
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(webRTCConfigResponse.Result.Skill), &c.skill)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iceServers, err := json.Marshal(&webRTCConfigResponse.Result.P2PConfig.Ices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.iceServers, err = webrtc.UnmarshalICEServers(iceServers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.motoId = webRTCConfigResponse.Result.MotoID
|
||||
c.auth = webRTCConfigResponse.Result.Auth
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) GetStreamUrl(streamType string) (err error) {
|
||||
url := fmt.Sprintf("https://%s/v1.0/devices/%s/stream/actions/allocate", c.apiURL, c.deviceId)
|
||||
|
||||
request := &AllocateRequest{
|
||||
Type: streamType,
|
||||
}
|
||||
|
||||
body, err := c.Request("POST", url, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var allosResponse AllocateResponse
|
||||
err = json.Unmarshal(body, &allosResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !allosResponse.Success {
|
||||
return fmt.Errorf(allosResponse.Msg)
|
||||
}
|
||||
|
||||
switch streamType {
|
||||
case "rtsp":
|
||||
c.rtspURL = allosResponse.Result.URL
|
||||
case "hls":
|
||||
c.hlsURL = allosResponse.Result.URL
|
||||
default:
|
||||
return fmt.Errorf("unsupported stream type: %s", streamType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) LoadHubConfig() (config *OpenIoTHubConfig, err error) {
|
||||
url := fmt.Sprintf("https://%s/v2.0/open-iot-hub/access/config", c.apiURL)
|
||||
|
||||
request := &OpenIoTHubConfigRequest{
|
||||
UID: c.uid,
|
||||
UniqueID: uuid.New().String(),
|
||||
LinkType: "mqtt",
|
||||
Topics: "ipc",
|
||||
}
|
||||
|
||||
body, err := c.Request("POST", url, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var openIoTHubConfigResponse OpenIoTHubConfigResponse
|
||||
err = json.Unmarshal(body, &openIoTHubConfigResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !openIoTHubConfigResponse.Success {
|
||||
return nil, fmt.Errorf(openIoTHubConfigResponse.Msg)
|
||||
}
|
||||
|
||||
return &openIoTHubConfigResponse.Result, nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) Request(method string, url string, body any) ([]byte, error) {
|
||||
var bodyReader io.Reader
|
||||
if body != nil {
|
||||
@@ -253,224 +375,7 @@ func (c *TuyaClient) Request(method string, url string, body any) ([]byte, error
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) InitToken() (err error) {
|
||||
url := fmt.Sprintf("https://%s/v1.0/token?grant_type=1", c.apiURL)
|
||||
|
||||
c.accessToken = ""
|
||||
c.refreshToken = ""
|
||||
|
||||
body, err := c.Request("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tokenResponse TokenResponse
|
||||
err = json.Unmarshal(body, &tokenResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !tokenResponse.Success {
|
||||
return fmt.Errorf("error: %s", tokenResponse.Msg)
|
||||
}
|
||||
|
||||
c.accessToken = tokenResponse.Result.AccessToken
|
||||
c.refreshToken = tokenResponse.Result.RefreshToken
|
||||
c.expireTime = tokenResponse.Result.ExpireTime
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) InitDevice() (err error) {
|
||||
url := fmt.Sprintf("https://%s/v1.0/users/%s/devices/%s/webrtc-configs", c.apiURL, c.uid, c.deviceId)
|
||||
|
||||
body, err := c.Request("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var webRTCConfigResponse WebRTCConfigResponse
|
||||
err = json.Unmarshal(body, &webRTCConfigResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !webRTCConfigResponse.Success {
|
||||
return fmt.Errorf("error: %s", webRTCConfigResponse.Msg)
|
||||
}
|
||||
|
||||
c.motoId = webRTCConfigResponse.Result.MotoID
|
||||
c.auth = webRTCConfigResponse.Result.Auth
|
||||
|
||||
c.skill = &Skill{
|
||||
WebRTC: 3, // basic webrtc
|
||||
Audios: make([]AudioSkill, 0),
|
||||
Videos: make([]VideoSkill, 0),
|
||||
}
|
||||
|
||||
if webRTCConfigResponse.Result.Skill != "" {
|
||||
_ = json.Unmarshal([]byte(webRTCConfigResponse.Result.Skill), c.skill)
|
||||
}
|
||||
|
||||
c.hasBackchannel = contains(webRTCConfigResponse.Result.AudioAttributes.CallMode, 2) &&
|
||||
contains(webRTCConfigResponse.Result.AudioAttributes.HardwareCapability, 1)
|
||||
|
||||
c.medias = make([]*core.Media, 0)
|
||||
|
||||
if len(c.skill.Audios) > 0 {
|
||||
direction := core.DirectionRecvonly
|
||||
if c.hasBackchannel {
|
||||
direction = core.DirectionSendRecv
|
||||
}
|
||||
|
||||
codecs := make([]*core.Codec, 0)
|
||||
for _, audio := range c.skill.Audios {
|
||||
codecs = append(codecs, &core.Codec{
|
||||
Name: getAudioCodec(audio.CodecType),
|
||||
ClockRate: uint32(audio.SampleRate),
|
||||
Channels: uint8(audio.Channels),
|
||||
})
|
||||
}
|
||||
|
||||
c.medias = append(c.medias, &core.Media{
|
||||
Kind: core.KindAudio,
|
||||
Direction: direction,
|
||||
Codecs: codecs,
|
||||
})
|
||||
} else {
|
||||
// Use default values for Audio
|
||||
c.medias = append(c.medias, &core.Media{
|
||||
Kind: core.KindAudio,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{
|
||||
{
|
||||
Name: core.CodecPCMU,
|
||||
ClockRate: uint32(8000),
|
||||
Channels: uint8(1),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if len(c.skill.Videos) > 0 {
|
||||
codecs := make([]*core.Codec, 0)
|
||||
for _, video := range c.skill.Videos {
|
||||
if video.CodecType == 2 {
|
||||
codecs = append(codecs, &core.Codec{
|
||||
Name: core.CodecH264,
|
||||
ClockRate: uint32(video.SampleRate),
|
||||
PayloadType: 96,
|
||||
})
|
||||
} else if video.CodecType == 4 {
|
||||
codecs = append(codecs, &core.Codec{
|
||||
Name: core.CodecH265,
|
||||
ClockRate: uint32(video.SampleRate),
|
||||
PayloadType: 96,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
c.medias = append(c.medias, &core.Media{
|
||||
Kind: core.KindVideo,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: codecs,
|
||||
})
|
||||
} else {
|
||||
// Use default values for Video
|
||||
c.medias = append(c.medias, &core.Media{
|
||||
Kind: core.KindVideo,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{
|
||||
{
|
||||
Name: core.CodecH264,
|
||||
ClockRate: uint32(90000),
|
||||
PayloadType: 96,
|
||||
},
|
||||
{
|
||||
Name: core.CodecH265,
|
||||
ClockRate: uint32(90000),
|
||||
PayloadType: 96,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
iceServersBytes, err := json.Marshal(&webRTCConfigResponse.Result.P2PConfig.Ices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.iceServers, err = webrtc.UnmarshalICEServers([]byte(iceServersBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) GetStreamUrl(streamType string) (err error) {
|
||||
url := fmt.Sprintf("https://%s/v1.0/devices/%s/stream/actions/allocate", c.apiURL, c.deviceId)
|
||||
|
||||
request := &AllocateRequest{
|
||||
Type: streamType,
|
||||
}
|
||||
|
||||
body, err := c.Request("POST", url, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var allosResponse AllocateResponse
|
||||
err = json.Unmarshal(body, &allosResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !allosResponse.Success {
|
||||
return fmt.Errorf("error: %s", allosResponse.Msg)
|
||||
}
|
||||
|
||||
switch streamType {
|
||||
case "rtsp":
|
||||
c.rtspURL = allosResponse.Result.URL
|
||||
case "hls":
|
||||
c.hlsURL = allosResponse.Result.URL
|
||||
default:
|
||||
return fmt.Errorf("unsupported stream type: %s", streamType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) LoadHubConfig() (config *OpenIoTHubConfig, err error) {
|
||||
url := fmt.Sprintf("https://%s/v2.0/open-iot-hub/access/config", c.apiURL)
|
||||
|
||||
request := &OpenIoTHubConfigRequest{
|
||||
UID: c.uid,
|
||||
UniqueID: uuid.New().String(),
|
||||
LinkType: "mqtt",
|
||||
Topics: "ipc",
|
||||
}
|
||||
|
||||
body, err := c.Request("POST", url, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var openIoTHubConfigResponse OpenIoTHubConfigResponse
|
||||
err = json.Unmarshal(body, &openIoTHubConfigResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !openIoTHubConfigResponse.Success {
|
||||
return nil, fmt.Errorf("error: %s", openIoTHubConfigResponse.Msg)
|
||||
}
|
||||
|
||||
return &openIoTHubConfigResponse.Result, nil
|
||||
}
|
||||
|
||||
func (c *TuyaClient) getStreamType(streamChoice string) int {
|
||||
func (c *TuyaClient) getStreamType(streamRole string) int {
|
||||
// Default streamType if nothing is found
|
||||
defaultStreamType := 1
|
||||
|
||||
@@ -501,7 +406,7 @@ func (c *TuyaClient) getStreamType(streamChoice string) int {
|
||||
}
|
||||
|
||||
// Return the streamType based on the selection
|
||||
switch streamChoice {
|
||||
switch streamRole {
|
||||
case "main":
|
||||
return highestResType
|
||||
case "sub":
|
||||
@@ -511,29 +416,6 @@ func (c *TuyaClient) getStreamType(streamChoice string) int {
|
||||
}
|
||||
}
|
||||
|
||||
func getAudioCodec(codecType int) string {
|
||||
switch codecType {
|
||||
// case 100:
|
||||
// return "ADPCM"
|
||||
case 101:
|
||||
return core.CodecPCM
|
||||
case 102, 103, 104:
|
||||
return core.CodecAAC
|
||||
case 105:
|
||||
return core.CodecPCMU
|
||||
case 106:
|
||||
return core.CodecPCMA
|
||||
// case 107:
|
||||
// return "G726-32"
|
||||
// case 108:
|
||||
// return "SPEEX"
|
||||
case 109:
|
||||
return core.CodecMP3
|
||||
default:
|
||||
return core.CodecPCMU
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TuyaClient) isHEVC(streamType int) bool {
|
||||
for _, video := range c.skill.Videos {
|
||||
if video.StreamType == streamType {
|
||||
@@ -544,22 +426,9 @@ func (c *TuyaClient) isHEVC(streamType int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *TuyaClient) isClaritySupported(webrtcValue int) bool {
|
||||
return (webrtcValue & (1 << 5)) != 0
|
||||
}
|
||||
|
||||
func (c *TuyaClient) calBusinessSign(ts int64) string {
|
||||
data := fmt.Sprintf("%s%s%s%d", c.clientId, c.accessToken, c.clientSecret, ts)
|
||||
val := md5.Sum([]byte(data))
|
||||
res := fmt.Sprintf("%X", val)
|
||||
return res
|
||||
}
|
||||
|
||||
func contains(slice []int, val int) bool {
|
||||
for _, item := range slice {
|
||||
if item == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user