Add support Roborock source

This commit is contained in:
Alexey Khit
2023-03-19 12:14:20 +03:00
parent 12a7b96289
commit e728643aad
14 changed files with 1257 additions and 30 deletions
+28 -22
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/AlexxIT/go2rtc/cmd/app"
"github.com/AlexxIT/go2rtc/cmd/roborock"
"github.com/AlexxIT/go2rtc/cmd/streams"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/rs/zerolog"
@@ -26,13 +27,13 @@ func Init() {
// support load cameras from Hass config file
filename := path.Join(conf.Mod.Config, ".storage/core.config_entries")
data, err := os.ReadFile(filename)
b, err := os.ReadFile(filename)
if err != nil {
return
}
storage := new(entries)
if err = json.Unmarshal(data, storage); err != nil {
if err = json.Unmarshal(b, storage); err != nil {
return
}
@@ -48,22 +49,37 @@ func Init() {
for _, entrie := range storage.Data.Entries {
switch entrie.Domain {
case "generic":
if entrie.Options.StreamSource == "" {
var options struct {
StreamSource string `json:"stream_source"`
}
if err = json.Unmarshal(entrie.Data, &options); err != nil {
continue
}
urls[entrie.Title] = entrie.Options.StreamSource
urls[entrie.Title] = options.StreamSource
case "homekit_controller":
if entrie.Data.ClientID == "" {
var data struct {
ClientID string `json:"iOSPairingId"`
ClientPrivate string `json:"iOSDeviceLTSK"`
ClientPublic string `json:"iOSDeviceLTPK"`
DeviceID string `json:"AccessoryPairingID"`
DevicePublic string `json:"AccessoryLTPK"`
DeviceHost string `json:"AccessoryIP"`
DevicePort uint16 `json:"AccessoryPort"`
}
if err = json.Unmarshal(entrie.Data, &data); err != nil {
continue
}
urls[entrie.Title] = fmt.Sprintf(
"homekit://%s:%d?client_id=%s&client_private=%s%s&device_id=%s&device_public=%s",
entrie.Data.DeviceHost, entrie.Data.DevicePort,
entrie.Data.ClientID, entrie.Data.ClientPrivate, entrie.Data.ClientPublic,
entrie.Data.DeviceID, entrie.Data.DevicePublic,
data.DeviceHost, data.DevicePort,
data.ClientID, data.ClientPrivate, data.ClientPublic,
data.DeviceID, data.DevicePublic,
)
case "roborock":
_ = json.Unmarshal(entrie.Data, &roborock.Auth)
default:
continue
}
@@ -78,20 +94,10 @@ var log zerolog.Logger
type entries struct {
Data struct {
Entries []struct {
Title string `json:"title"`
Domain string `json:"domain"`
Data struct {
ClientID string `json:"iOSPairingId"`
ClientPrivate string `json:"iOSDeviceLTSK"`
ClientPublic string `json:"iOSDeviceLTPK"`
DeviceID string `json:"AccessoryPairingID"`
DevicePublic string `json:"AccessoryLTPK"`
DeviceHost string `json:"AccessoryIP"`
DevicePort uint16 `json:"AccessoryPort"`
} `json:"data"`
Options struct {
StreamSource string `json:"stream_source"`
}
Title string `json:"title"`
Domain string `json:"domain"`
Data json.RawMessage `json:"data"`
Options json.RawMessage `json:"options"`
} `json:"entries"`
} `json:"data"`
}
+120
View File
@@ -0,0 +1,120 @@
package roborock
import (
"encoding/json"
"fmt"
"github.com/AlexxIT/go2rtc/cmd/api"
"github.com/AlexxIT/go2rtc/cmd/streams"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/roborock"
"net/http"
)
func Init() {
streams.HandleFunc("roborock", handle)
api.HandleFunc("api/roborock", apiHandle)
}
func handle(url string) (core.Producer, error) {
conn := roborock.NewClient(url)
if err := conn.Dial(); err != nil {
return nil, err
}
if err := conn.Connect(); err != nil {
return nil, err
}
return conn, nil
}
var Auth struct {
UserData *roborock.UserInfo `json:"user_data"`
BaseURL string `json:"base_url"`
}
func apiHandle(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
if Auth.UserData == nil {
http.Error(w, "", http.StatusNotFound)
return
}
case "POST":
if err := r.ParseMultipartForm(1024); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
username := r.Form.Get("username")
password := r.Form.Get("password")
if username == "" || password == "" {
http.Error(w, "empty username or password", http.StatusBadRequest)
return
}
base, err := roborock.GetBaseURL(username)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
ui, err := roborock.Login(base, username, password)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
Auth.BaseURL = base
Auth.UserData = ui
default:
http.Error(w, "", http.StatusMethodNotAllowed)
return
}
homeID, err := roborock.GetHomeID(Auth.BaseURL, Auth.UserData.Token)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
devices, err := roborock.GetDevices(Auth.UserData, homeID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if len(devices) == 0 {
http.Error(w, "no devices in the account", http.StatusNotFound)
return
}
var response struct {
Devices []struct {
Name string `json:"name"`
Source string `json:"source"`
} `json:"devices"`
}
for _, device := range devices {
source := fmt.Sprintf(
"roborock://%s?u=%s&s=%s&k=%s&did=%s&key=%s&pin=",
Auth.UserData.IoT.URL.MQTT[6:],
Auth.UserData.IoT.User, Auth.UserData.IoT.Pass, Auth.UserData.IoT.Domain,
device.DID, device.Key,
)
response.Devices = append(response.Devices, struct {
Name string `json:"name"`
Source string `json:"source"`
}{
Name: device.Name,
Source: source,
})
}
if err = json.NewEncoder(w).Encode(response); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
+2 -2
View File
@@ -118,8 +118,8 @@ func apiHandle(w http.ResponseWriter, r *http.Request) {
}
// create new random share
share = core.RandString(16)
pwd := core.RandString(16)
share = core.RandString(10, 62)
pwd := core.RandString(10, 62)
srv.AddShare(share, pwd, src)
if shares == nil {