feat: 初始化小米 IoT (MIoT) 智能家居 Go 库
- 实现 MIoT 客户端核心功能(MQTT 连接、设备管理、属性读写) - 支持云端 API 调用与局域网设备发现(mDNS) - 集成国际化(i18n)多语言支持 - 添加 MIoT 设备规约解析器(spec_parser) - 包含单元测试与使用示例
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
// Package miot provides MIoT core client for Xiaomi Home devices.
|
||||
// This file is a direct port of py-miot/common.py utility functions.
|
||||
package miot
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// RootPath (aligned with Python MIOT_ROOT_PATH)
|
||||
// ============================================================================
|
||||
|
||||
// RootPath is the root directory of the miot package.
|
||||
// It mirrors Python's MIOT_ROOT_PATH = path.dirname(path.abspath(__file__)).
|
||||
// Callers should set this before using GenAbsolutePath.
|
||||
var RootPath string
|
||||
|
||||
func init() {
|
||||
// Attempt to discover the project root from the working directory.
|
||||
// Callers may override this after init.
|
||||
if wd, err := os.Getwd(); err == nil {
|
||||
RootPath = wd
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utility functions (aligned with py-miot/common.py)
|
||||
// ============================================================================
|
||||
|
||||
// GenAbsolutePath generates an absolute path relative to RootPath.
|
||||
// Aligns with Python: gen_absolute_path(relative_path).
|
||||
func GenAbsolutePath(relativePath string) string {
|
||||
return filepath.Join(RootPath, relativePath)
|
||||
}
|
||||
|
||||
// CalcGroupID calculates a group ID from a user ID and home ID.
|
||||
// Uses SHA1 with a "central_service" salt, returning the first 16 hex chars.
|
||||
// Aligns with Python: calc_group_id(uid, home_id).
|
||||
func CalcGroupID(uid, homeID string) string {
|
||||
src := fmt.Sprintf("%scentral_service%s", uid, homeID)
|
||||
h := sha1.Sum([]byte(src))
|
||||
return hex.EncodeToString(h[:])[:16]
|
||||
}
|
||||
|
||||
// LoadJSONFile loads and parses a JSON file.
|
||||
// Aligns with Python: load_json_file(json_file).
|
||||
func LoadJSONFile(filePath string) (map[string]interface{}, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read JSON file %s: %w", filePath, err)
|
||||
}
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(data, &result); err != nil {
|
||||
return nil, fmt.Errorf("parse JSON file %s: %w", filePath, err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// LoadYAMLFile loads and parses a YAML file.
|
||||
// Aligns with Python: load_yaml_file(yaml_file).
|
||||
func LoadYAMLFile(filePath string) (map[string]interface{}, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read YAML file %s: %w", filePath, err)
|
||||
}
|
||||
var result map[string]interface{}
|
||||
if err := yaml.Unmarshal(data, &result); err != nil {
|
||||
return nil, fmt.Errorf("parse YAML file %s: %w", filePath, err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// RandomizeInt randomizes an integer within the given ratio range.
|
||||
// Formula: int(value * (1 - ratio + random() * 2 * ratio))
|
||||
// Aligns with Python: randomize_int(value, ratio).
|
||||
func RandomizeInt(value int, ratio float64) int {
|
||||
return int(float64(value) * (1 - ratio + rand.Float64()*2*ratio))
|
||||
}
|
||||
|
||||
// RandomizeFloat randomizes a float64 within the given ratio range.
|
||||
// Formula: value * (1 - ratio + random() * 2 * ratio)
|
||||
// Aligns with Python: randomize_float(value, ratio).
|
||||
func RandomizeFloat(value float64, ratio float64) float64 {
|
||||
return value * (1 - ratio + rand.Float64()*2*ratio)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Slugify (aligned with py-miot/common.py slugify_name / slugify_did)
|
||||
// ============================================================================
|
||||
|
||||
// nonAlphaNum matches sequences of non-alphanumeric characters.
|
||||
var nonAlphaNum = regexp.MustCompile(`[^a-z0-9]+`)
|
||||
|
||||
// SlugifyName converts a name to a lowercase slug with underscore separators.
|
||||
// Aligns with Python: slugify_name(name, separator='_').
|
||||
func SlugifyName(name string) string {
|
||||
s := strings.ToLower(name)
|
||||
s = nonAlphaNum.ReplaceAllString(s, "_")
|
||||
s = strings.Trim(s, "_")
|
||||
return s
|
||||
}
|
||||
|
||||
// SlugifyDID creates a slug from a cloud server and device ID.
|
||||
// Pattern: f"{cloud_server}_{did}" then slugify.
|
||||
// Aligns with Python: slugify_did(cloud_server, did).
|
||||
func SlugifyDID(cloudServer, did string) string {
|
||||
return SlugifyName(cloudServer + "_" + did)
|
||||
}
|
||||
Reference in New Issue
Block a user