diff --git a/.github/workflows/quality-assurance.yaml b/.github/workflows/quality-assurance.yaml index 4a3d6b4..2c7f464 100644 --- a/.github/workflows/quality-assurance.yaml +++ b/.github/workflows/quality-assurance.yaml @@ -28,6 +28,7 @@ jobs: - name: Check code style with revive uses: docker://morphy/revive-action:v2 with: + exclude: "./examples/..." config: revive.toml path: "./..." test: diff --git a/Makefile b/Makefile index be6f73f..6db3069 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ GOLANGCI_LINT_VERSION = v1.56.2 REVIVE_VERSION = v1.3.7 GIT_CHGLOG_VERSION = v0.15.4 GO_BIN_PATH := $(shell go env GOPATH)/bin -TEST_MODULES := $(shell go list ./... | grep -v /cmd/) +TEST_MODULES := $(shell go list ./... | grep -v /examples/) define build_app - cd "cmd/$1"; go build -o "../../build/nuga-$1" + cd "examples/$1"; go build -o "../../build/nuga-$1" endef .PHONY: setup @@ -18,8 +18,8 @@ setup: .PHONY: lint lint: - golangci-lint run ./... - revive -config ./revive.toml ./... + golangci-lint run --skip-dirs ./examples ./... + revive -exclude ./examples/... -config ./revive.toml ./... .PHONY: test test: diff --git a/cmd/dump/main.go b/cmd/dump/main.go deleted file mode 100644 index d833576..0000000 --- a/cmd/dump/main.go +++ /dev/null @@ -1,71 +0,0 @@ -// Example app for dumping and restoring device state -package main - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/mishamyrt/nuga-lib" - "github.com/mishamyrt/nuga-lib/dump" - "github.com/mishamyrt/nuga-lib/hid" -) - -const usage = "Usage: nuga-dump [restore|dump] " - -func main() { - if len(os.Args) < 3 { - fmt.Println("Too few arguments") - fmt.Println(usage) - os.Exit(1) - } - command := os.Args[1] - filePath := os.Args[2] - err := nuga.Init() - if err != nil { - die("Error initializing: %v", err) - } - handle, err := hid.Open() - if err != nil { - die("Error opening device: %v", err) - } - switch command { - case "restore": - err = restoreDump(handle, filePath) - case "dump": - err = saveDump(handle, filePath) - } - if err != nil { - die("Error: %v", err) - } -} - -func saveDump(d *hid.Device, path string) error { - state, err := dump.Collect(d) - if err != nil { - return err - } - data, err := json.Marshal(&state) - if err != nil { - return err - } - return os.WriteFile(path, data, 0644) -} - -func restoreDump(d *hid.Device, path string) error { - data, err := os.ReadFile(path) - if err != nil { - return err - } - var state dump.State - err = json.Unmarshal(data, &state) - if err != nil { - return err - } - return dump.Restore(d, &state) -} - -func die(format string, a ...any) { - fmt.Printf(format+"\n", a...) - os.Exit(1) -} diff --git a/dump/collect.go b/dump/collect.go index 4383687..96fbb23 100644 --- a/dump/collect.go +++ b/dump/collect.go @@ -3,68 +3,28 @@ package dump import ( "github.com/mishamyrt/nuga-lib/device" - "github.com/mishamyrt/nuga-lib/features/keys" - "github.com/mishamyrt/nuga-lib/features/light" + "github.com/mishamyrt/nuga-lib/features" "github.com/mishamyrt/nuga-lib/hid" - "github.com/mishamyrt/nuga-lib/internal/slices" ) // Collect device state -func Collect(h hid.Handler) (State, error) { - lightsState, err := collectLights(h) +func Collect(dev *hid.Device) (*State, error) { + model := device.Model(dev.Info.Model) + f := features.New(dev, model) + lights, err := f.Light.GetStateData() if err != nil { - return State{}, err + return nil, err } - keysState, err := collectKeys(h) + keys, err := f.Keys.GetStateData() if err != nil { - return State{}, err + return nil, err } - info := h.GetInfo() - return State{ - Name: device.Model(info.Model), - Firmware: info.Firmware, - Lights: lightsState, - Keys: keysState, + return &State{ + Model: model, + Firmware: dev.Info.Firmware, + Data: features.StateData{ + Lights: lights, + Keys: keys, + }, }, nil } - -func collectLights(h hid.Handler) (light.State, error) { - var state light.State - var err error - lightsFeature := light.New(h, nil) - params, err := lightsFeature.GetRawEffects() - if err != nil { - return state, err - } - colors, err := lightsFeature.GetRawColors() - if err != nil { - return state, err - } - state.Params = slices.Cast[byte, int](params) - state.Colors = slices.Cast[byte, int](colors) - return state, nil -} - -func collectKeys(h hid.Handler) (keys.State, error) { - var state keys.State - var err error - keysFeature := keys.New(h, nil) - state.Mac, err = keysFeature.GetMacCodes() - if err != nil { - return state, err - } - state.Win, err = keysFeature.GetWinCodes() - if err != nil { - return state, err - } - macros, err := keysFeature.GetMacros() - if err != nil { - return state, err - } - macrosData, err := macros.Bytes() - if err != nil { - return state, err - } - state.Macros = macrosData - return state, nil -} diff --git a/dump/defaults.go b/dump/defaults.go index 46288d3..699b92a 100644 --- a/dump/defaults.go +++ b/dump/defaults.go @@ -13,7 +13,7 @@ var defaults embed.FS // GetDefaults returns default state for given model func GetDefaults(model device.Model) (*State, error) { - filePath := fmt.Sprintf("defaults/%v.json", string(model)) + filePath := fmt.Sprintf("defaults/%v.nugafile", string(model)) data, err := defaults.ReadFile(filePath) if err != nil { return nil, err diff --git a/dump/defaults/Halo65.json b/dump/defaults/Halo65.json deleted file mode 100644 index 29c250d..0000000 --- a/dump/defaults/Halo65.json +++ /dev/null @@ -1 +0,0 @@ -{"version":"v2.1.1","name":"Halo65","firmware":"1.1.8","lights":{"colors":[136,0,0,0,0,0,0,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,228,144,14,183,240,163,240,144,13,206,224,255,19,19,19,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"params":[131,182,0,0,0,0,0,1,0,0,0,0,0,90,165,3,3,1,0,0,12,32,1,0,0,0,0,5,0,4,0,1,0,4,0,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,4,4,4,4,4,4,4,4,4,4,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,165,3,3,1,0,0,12,32,1,0,0,0,0,5,0,4,0,1,0,4,0,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,4,4,4,4,4,4,4,4,4,4,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,165,0,0,0,0,0,0,0,0]},"keys":{"mac":[687865856,251658242,721420288,956301312,3774873606,3758096390,973078528,536870914,419430402,369098754,486539264,3791650822,989855744,553648130,436207618,352321538,486539266,3808428038,1006632960,570425346,452984834,117440512,503316482,2432696320,1023410176,587202562,469762050,738197506,520093698,2332033024,1040187392,603979778,385875968,218103810,67108866,738197504,1056964608,620756994,469762048,83886082,100663298,2315255808,1073741824,637534210,402653184,218103808,268435456,2415919104,1090519040,654311426,201326592,234881024,16777218,3875536902,1107296256,671088642,301989888,251658240,33554434,32,1124073472,687865858,318767104,855638016,50331650,3825205254,1140850688,704643074,788529152,872415232,2264924160,2281701376,1157627904,721420290,788529154,754974722,1677721600,3875536902,1174405120,704643072,754974722,671088640,3841982470,301989890,1275068416,0,0,0,268435458,285212674,1241513984,1275068416,1258291200,1308622848,1291845632,318767106,1291845632,1409286144,1610612736,1560281088,1509949440,1644167168,1258291200,1426063360,1627389952,1577058304,1526726656,1660944384,1308622848,1442840576,1459617792,2231369728,1476395008,0,0,1426063360,1627389952,1577058304,1526726656,1660944384,0,1442840576,1459617792,2231369728,1476395008,0,1869373768,857748790,1968058414,544827472,1869373768,2110774,268435456,905969664,922746880,939524096,83886080,184549376,285212672,1090519040,1107296256,1124073472,1140850688,1157627904,3808428038,167772160,1241513984,687865856,1375731712,1358954496,1342177280,1325400064,436207616,369098752,67108864,117440512,721420288,335544320,436207616,134217728,352321536,452984832,100663296,419430400,503316480,520093696,536870912,553648128,570425344,587202560,603979776,620756992,637534208,654311424,754974720,771751936,150994944,822083584,788529152,805306368,402653198,131085,65549,385875982,83886094,402653198,503316494,1090519040,1107296256,1124073472,1140850688,1157627904,16777230,268435470,67108878,889192448,65548,131084,196619,196626,436207616,1,67108864,117440512,335544334,201326606,218103822,234881038,251658254,335544334,469762062,486539278,973078528,989855744,1006632960,1023410176,1040187392,1056964608,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,452984846,436207630,788529152,419430414,3083010020,3263227203,3083009719,3263227203,3760582583,92483839,2164195652,4266979076,2145735679,3993941970,1407125684,3084025829,101058082,101058054,84215046,67372037,459780,33619968,537921540,33652800,67207172,272637960,3833610272,4026863504,459314917,3773173392,3020120319,244322049],"win":[687865856,251658242,721420288,956301312,3774873606,3758096390,973078528,536870914,419430402,369098754,486539264,201326594,989855744,553648130,436207618,352321538,486539266,3791650822,1006632960,570425346,452984834,117440512,503316482,2432696320,1023410176,587202562,469762050,738197506,520093698,2332033024,1040187392,603979778,385875968,218103810,67108866,738197504,1056964608,620756994,469762048,83886082,100663298,2315255808,1073741824,637534210,402653184,218103808,268435456,2415919104,1090519040,654311426,201326592,234881024,16777218,3858759686,1107296256,671088642,301989888,251658240,33554434,32,1124073472,687865858,318767104,855638016,50331650,3825205254,1140850688,704643074,788529152,872415232,2264924160,2281701376,1157627904,721420290,788529154,754974722,1677721600,3875536902,1174405120,704643072,754974722,671088640,3841982470,301989890,1275068416,0,0,0,268435458,285212674,1241513984,1275068416,1258291200,1308622848,1291845632,318767106,1291845632,1409286144,1610612736,1560281088,1509949440,1644167168,1258291200,1426063360,1627389952,1577058304,1526726656,1660944384,1308622848,1442840576,1459617792,2231369728,1476395008,0,0,1426063360,1627389952,1577058304,1526726656,1660944384,0,1442840576,1459617792,2231369728,1476395008,0,176101330,316846866,176102912,316846866,176102912,14856962,268435456,905969664,922746880,939524096,83886080,184549376,285212672,3774873606,3841982470,1124073472,1140850688,1157627904,3808428038,167772160,1241513984,687865856,1375731712,1358954496,1342177280,1325400064,436207616,369098752,67108864,117440512,721420288,335544320,436207616,134217728,352321536,452984832,100663296,419430400,503316480,520093696,536870912,553648128,570425344,587202560,603979776,620756992,637534208,654311424,754974720,771751936,150994944,822083584,788529152,805306368,402653198,131085,65549,385875982,83886094,402653198,503316494,520093710,520093710,1124073472,1140850688,1157627904,16777230,268435470,67108878,889192448,65548,131084,196619,196626,436207616,369098752,16777217,117440512,335544334,201326606,218103822,234881038,251658254,335544334,469762062,486539278,973078528,989855744,1006632960,1023410176,1040187392,1056964608,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,452984846,436207630,788529152,419430414,1957170832,3524456458,302677911,3541230259,3058635404,3767011472,244320352,4026627240,176096640,2430776082,350267406,3597721840,3121516578,2146437748,3803386378,311218962,143701583,140566664,1958350480,142667777,3004303999,2547127010,3770289808,1893789716,29500118,1388446418,2399703268,8944]}} \ No newline at end of file diff --git a/dump/defaults/Halo65.nugafile b/dump/defaults/Halo65.nugafile new file mode 100644 index 0000000..24bc162 --- /dev/null +++ b/dump/defaults/Halo65.nugafile @@ -0,0 +1 @@ +{"model":"Halo65","firmware":"1.1.8","state":{"lights":{"colors":"/wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP//////5JAOt/Cj8JANzuD/ExMTVA==","effects":"AwMBAAAMIAEAAAAABQAEAAEABAD//wdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRP//B0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QEBAQEBAQEBAQEAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAFql","custom_effect":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAD/AAAAAAAAAP//////AAAAAAAAAAAA//8AAAAA//////8AAAAAAAAAAAAA//8AAAAAAP///wD/AAAAAAAAAAAAAAAAAAAA/wAAAAAA/wAAAAAAAAAAAAAAAAAA/wD/AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="},"keys":{"mac":"AAAAKQIAAA8AAAArAAAAOQYAAOEGAADgAAAAOgIAACACAAAZAgAAFgAAAB0GAADiAAAAOwIAACECAAAaAgAAFQIAAB0GAADjAAAAPAIAACICAAAbAAAABwIAAB4AAACRAAAAPQIAACMCAAAcAgAALAIAAB8AAACLAAAAPgIAACQAAAAXAgAADQIAAAQAAAAsAAAAPwIAACUAAAAcAgAABQIAAAYAAACKAAAAQAIAACYAAAAYAAAADQAAABAAAACQAAAAQQIAACcAAAAMAAAADgIAAAEGAADnAAAAQgIAACgAAAASAAAADwIAAAIgAAAAAAAAQwIAACkAAAATAAAAMwIAAAMGAADkAAAARAIAACoAAAAvAAAANAAAAIcAAACIAAAARQIAACsCAAAvAgAALQAAAGQGAADnAAAARgAAACoCAAAtAAAAKAYAAOUCAAASAAAATAAAAAAAAAAAAAAAAAIAABACAAARAAAASgAAAEwAAABLAAAATgAAAE0CAAATAAAATQAAAFQAAABgAAAAXQAAAFoAAABiAAAASwAAAFUAAABhAAAAXgAAAFsAAABjAAAATgAAAFYAAABXAAAAhQAAAFgAAAAAAAAAAAAAAFUAAABhAAAAXgAAAFsAAABjAAAAAAAAAFYAAABXAAAAhQAAAFgAAAAASGFsbzY1IDMuME51UGh5IEhhbG82NSAAAAAAEAAAADYAAAA3AAAAOAAAAAUAAAALAAAAEQAAAEEAAABCAAAAQwAAAEQAAABFBgAA4wAAAAoAAABKAAAAKQAAAFIAAABRAAAAUAAAAE8AAAAaAAAAFgAAAAQAAAAHAAAAKwAAABQAAAAaAAAACAAAABUAAAAbAAAABgAAABkAAAAeAAAAHwAAACAAAAAhAAAAIgAAACMAAAAkAAAAJQAAACYAAAAnAAAALQAAAC4AAAAJAAAAMQAAAC8AAAAwDgAAGA0AAgANAAEADgAAFw4AAAUOAAAYDgAAHgAAAEEAAABCAAAAQwAAAEQAAABFDgAAAQ4AABAOAAAEAAAANQwAAQAMAAIACwADABIAAwAAAAAaAQAAAAAAAAQAAAAHDgAAFA4AAAwOAAANDgAADg4AAA8OAAAUDgAAHA4AAB0AAAA6AAAAOwAAADwAAAA9AAAAPgAAAD8AAABAAAAAQQAAAEIAAABDAAAARAAAAEUOAAAbDgAAGgAAAC8OAAAZ5P/Ct0PlgMK3/sK3Q+WAwrfvJeD/MIMFRAH/gATvVP7/U+V/0rcO7rQI31Plf9K3IgYGBgYGBgYGBQUFBQQEBAQEBwAAAAECBAgQIECAAQIEgAEECCBAECBAgOSQDwXw5ZZgG5AO5uD/YAO0AQ+QDg==","win":"AAAAKQIAAA8AAAArAAAAOQYAAOEGAADgAAAAOgIAACACAAAZAgAAFgAAAB0CAAAMAAAAOwIAACECAAAaAgAAFQIAAB0GAADiAAAAPAIAACICAAAbAAAABwIAAB4AAACRAAAAPQIAACMCAAAcAgAALAIAAB8AAACLAAAAPgIAACQAAAAXAgAADQIAAAQAAAAsAAAAPwIAACUAAAAcAgAABQIAAAYAAACKAAAAQAIAACYAAAAYAAAADQAAABAAAACQAAAAQQIAACcAAAAMAAAADgIAAAEGAADmAAAAQgIAACgAAAASAAAADwIAAAIgAAAAAAAAQwIAACkAAAATAAAAMwIAAAMGAADkAAAARAIAACoAAAAvAAAANAAAAIcAAACIAAAARQIAACsCAAAvAgAALQAAAGQGAADnAAAARgAAACoCAAAtAAAAKAYAAOUCAAASAAAATAAAAAAAAAAAAAAAAAIAABACAAARAAAASgAAAEwAAABLAAAATgAAAE0CAAATAAAATQAAAFQAAABgAAAAXQAAAFoAAABiAAAASwAAAFUAAABhAAAAXgAAAFsAAABjAAAATgAAAFYAAABXAAAAhQAAAFgAAAAAAAAAAAAAAFUAAABhAAAAXgAAAFsAAABjAAAAAAAAAFYAAABXAAAAhQAAAFgAAAAA0hd/ChKz4hIAHn8KErPiEgAefwoCs+IAAAAAEAAAADYAAAA3AAAAOAAAAAUAAAALAAAAEQYAAOEGAADlAAAAQwAAAEQAAABFBgAA4wAAAAoAAABKAAAAKQAAAFIAAABRAAAAUAAAAE8AAAAaAAAAFgAAAAQAAAAHAAAAKwAAABQAAAAaAAAACAAAABUAAAAbAAAABgAAABkAAAAeAAAAHwAAACAAAAAhAAAAIgAAACMAAAAkAAAAJQAAACYAAAAnAAAALQAAAC4AAAAJAAAAMQAAAC8AAAAwDgAAGA0AAgANAAEADgAAFw4AAAUOAAAYDgAAHg4AAB8OAAAfAAAAQwAAAEQAAABFDgAAAQ4AABAOAAAEAAAANQwAAQAMAAIACwADABIAAwAAAAAaAAAAFgEAAAEAAAAHDgAAFA4AAAwOAAANDgAADg4AAA8OAAAUDgAAHA4AAB0AAAA6AAAAOwAAADwAAAA9AAAAPgAAAD8AAABAAAAAQQAAAEIAAABDAAAARAAAAEUOAAAbDgAAGgAAAC8OAAAZkA6odArwEtKXfwoSs+IS04wST7aQCIjgYAiQDqh0AfCABX8KErPikA6o4BTw4HDWIpAOunQK8H8KErPiEtOMEk+2kAiI4GAIkA66dAHwgAh/ChKz4hLSl5AOuuAU8OBw1iLCAdICwlLkkAiP8CIAAA==","macros":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="}}} \ No newline at end of file diff --git a/dump/defaults/Halo75.json b/dump/defaults/Halo75.json deleted file mode 100644 index 880bd7b..0000000 --- a/dump/defaults/Halo75.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"Halo75","firmware":"1.0.5","lights":{"colors":[136,0,0,0,0,0,0,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,144,14,191,239,240,194,5,228,163,240,228,144,14,193,240,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"params":[131,182,0,0,0,0,0,1,0,0,0,0,0,90,165,3,3,1,0,0,12,32,1,0,0,0,0,5,0,4,0,1,0,4,0,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,4,4,4,4,4,4,4,4,4,4,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,165,3,3,1,0,0,12,32,1,0,0,0,0,5,0,4,0,1,0,4,0,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,255,255,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,4,4,4,4,4,4,4,4,4,4,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,165,0,0,0,0,0,0,0,0]},"keys":{"mac":[251658242,889192448,721420288,956301312,3774873606,3758096390,2,536870914,335544320,67108864,486539264,3791650822,16777218,553648130,436207616,369098752,486539266,3808428038,33554434,570425346,134217728,117440512,503316482,2432696320,50331650,587202562,352321536,738197506,520093698,2332033024,67108866,570425344,385875968,218103810,620756994,738197504,83886082,587202560,469762048,603979778,637534210,2315255808,100663298,603979776,402653184,218103808,268435456,2415919104,117440514,620756992,201326592,234881024,654311426,3875536902,134217730,637534208,301989888,251658240,671088642,32,150994946,654311424,318767104,855638016,687865858,3825205254,167772162,754974720,788529152,872415232,2264924160,2281701376,184549378,771751936,788529154,754974722,1677721600,3875536902,402653186,704643072,754974722,671088640,3841982470,301989890,1275068416,0,0,0,268435458,285212674,1224736768,1241513984,1291845632,1258291200,1308622848,318767106,1291845632,1409286144,1610612736,1560281088,1509949440,1644167168,1258291200,1426063360,1627389952,1577058304,1526726656,1660944384,1308622848,1442840576,1459617792,2231369728,1476395008,0,0,1426063360,1627389952,1577058304,1526726656,1660944384,0,1442840576,1459617792,2231369728,1476395008,0,1869373768,857748791,1968058414,544827472,1869373768,2110775,973078528,989855744,1006632960,1023410176,3472883716,536870926,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,3808428038,167772160,1241513984,687865856,1375731712,1358954496,1342177280,1325400064,436207616,369098752,67108864,117440512,554303488,335544320,436207616,134217728,352321536,452984832,100663296,419430400,503316480,520093696,536870912,553648128,184549376,83886080,285212672,905969664,922746880,939524096,754974720,771751936,150994944,822083584,788529152,805306368,973078528,989855744,1006632960,1023410176,1040187392,1056964608,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,16777230,268435470,67108878,889192448,65548,131084,196619,196626,436207616,369098752,16777217,117440512,537526272,201326606,218103822,234881038,251658254,335544334,469762062,486539278,201326606,218103822,234881038,251658254,402653198,83886094,503316494,131085,65549,385875982,754974720,771751936,452984846,436207630,788529152,419430414,1946619792,1956900881,244379656,2432688381,4242540303,2197151907,4042228620,1978702675,2537750939,261104132,4026692614,1978702675,1956839835,1956900881,261156872,2751389703,2390947296,1139860611,304219287,1230544,2129952534,3249541633,317893906,2115120082,3520205524,309580306,3506624466,244372337],"win":[251658242,889192448,721420288,956301312,3774873606,3758096390,2,536870914,335544320,67108864,486539264,201326594,16777218,553648130,436207616,369098752,486539266,3791650822,33554434,570425346,134217728,117440512,503316482,2432696320,50331650,587202562,352321536,738197506,520093698,2332033024,67108866,570425344,385875968,218103810,620756994,738197504,83886082,587202560,469762048,603979778,637534210,2315255808,100663298,603979776,402653184,218103808,268435456,2415919104,117440514,620756992,201326592,234881024,654311426,3858759686,134217730,637534208,301989888,251658240,671088642,32,150994946,654311424,318767104,855638016,687865858,3825205254,167772162,754974720,788529152,872415232,2264924160,2281701376,184549378,771751936,788529154,754974722,1677721600,3875536902,1174405120,704643072,754974722,671088640,3841982470,301989890,1275068416,0,0,0,268435458,285212674,1224736768,1241513984,1291845632,1258291200,1308622848,318767106,1291845632,1409286144,1610612736,1560281088,1509949440,1644167168,1258291200,1426063360,1627389952,1577058304,1526726656,1660944384,1308622848,1442840576,1459617792,2231369728,1476395008,0,0,1426063360,1627389952,1577058304,1526726656,1660944384,0,1442840576,1459617792,2231369728,1476395008,0,176103378,304009234,176102912,304009234,176102912,2019330,973078528,989855744,1006632960,1023410176,1040187392,1056964608,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,3808428038,167772160,1241513984,687865856,1375731712,1358954496,1342177280,1325400064,436207616,369098752,67108864,117440512,721420288,335544320,436207616,134217728,352321536,452984832,100663296,419430400,503316480,520093696,536870912,553648128,184549376,83886080,285212672,905969664,922746880,939524096,754974720,771751936,150994944,822083584,788529152,805306368,1879048196,1862270980,1006632960,1023410176,1040187392,1056964608,3053453316,3439329284,3036676100,3791650820,3925868548,3909091332,16777230,268435470,67108878,889192448,65548,131084,196619,196626,436207616,369098752,16777217,117440512,335544334,201326606,218103822,234881038,251658254,335544334,469762062,486539278,201326606,218103822,234881038,251658254,402653198,83886094,503316494,131085,65549,385875982,754974720,771751936,452984846,436207630,788529152,419430414,2835976420,552641520,227545122,160489679,227602601,160489680,819130538,3765240720,143656544,4027610232,3829387810,4034922640,143661266,4027610232,3769893264,4026798448,537663008,2635204943,143725120,572391442,1972827620,2507497364,3297867127,2416218435,4026855694,4042460816,4010709220,586195952]}} \ No newline at end of file diff --git a/dump/defaults/Halo75.nugafile b/dump/defaults/Halo75.nugafile new file mode 100644 index 0000000..90d20ac --- /dev/null +++ b/dump/defaults/Halo75.nugafile @@ -0,0 +1 @@ +{"model":"Halo75","firmware":"1.0.5","state":{"lights":{"colors":"/wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP//////kA6/7/DCBeSj8OSQDsHwkA==","effects":"AwMBAAAMIAEAAAAABQAEAAEABAD//wdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRP//B0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QEBAQEBAQEBAQEAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAFql","custom_effect":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAD/AAAAAAAAAP//////AAAAAAAAAAAA//8AAAAA//////8AAAAAAAAAAAAA//8AAAAAAP///wD/AAAAAAAAAAAAAAAAAAAA/wAAAAAA/wAAAAAAAAAAAAAAAAAA/wD/AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="},"keys":{"mac":"AgAADwAAADUAAAArAAAAOQYAAOEGAADgAgAAAAIAACAAAAAUAAAABAAAAB0GAADiAgAAAQIAACEAAAAaAAAAFgIAAB0GAADjAgAAAgIAACIAAAAIAAAABwIAAB4AAACRAgAAAwIAACMAAAAVAgAALAIAAB8AAACLAgAABAAAACIAAAAXAgAADQIAACUAAAAsAgAABQAAACMAAAAcAgAAJAIAACYAAACKAgAABgAAACQAAAAYAAAADQAAABAAAACQAgAABwAAACUAAAAMAAAADgIAACcGAADnAgAACAAAACYAAAASAAAADwIAACggAAAAAgAACQAAACcAAAATAAAAMwIAACkGAADkAgAACgAAAC0AAAAvAAAANAAAAIcAAACIAgAACwAAAC4CAAAvAgAALQAAAGQGAADnAgAAGAAAACoCAAAtAAAAKAYAAOUCAAASAAAATAAAAAAAAAAAAAAAAAIAABACAAARAAAASQAAAEoAAABNAAAASwAAAE4CAAATAAAATQAAAFQAAABgAAAAXQAAAFoAAABiAAAASwAAAFUAAABhAAAAXgAAAFsAAABjAAAATgAAAFYAAABXAAAAhQAAAFgAAAAAAAAAAAAAAFUAAABhAAAAXgAAAFsAAABjAAAAAAAAAFYAAABXAAAAhQAAAFgAAAAASGFsbzc1IDMuME51UGh5IEhhbG83NSAAAAAAOgAAADsAAAA8AAAAPQQAAM8OAAAgAAAAQAAAAEEAAABCAAAAQwAAAEQAAABFBgAA4wAAAAoAAABKAAAAKQAAAFIAAABRAAAAUAAAAE8AAAAaAAAAFgAAAAQAAAAHAAAKIQAAABQAAAAaAAAACAAAABUAAAAbAAAABgAAABkAAAAeAAAAHwAAACAAAAAhAAAACwAAAAUAAAARAAAANgAAADcAAAA4AAAALQAAAC4AAAAJAAAAMQAAAC8AAAAwAAAAOgAAADsAAAA8AAAAPQAAAD4AAAA/AAAAQAAAAEEAAABCAAAAQwAAAEQAAABFDgAAAQ4AABAOAAAEAAAANQwAAQAMAAIACwADABIAAwAAAAAaAAAAFgEAAAEAAAAHAAAKIA4AAAwOAAANDgAADg4AAA8OAAAUDgAAHA4AAB0OAAAMDgAADQ4AAA4OAAAPDgAAGA4AAAUOAAAeDQACAA0AAQAOAAAXAAAALQAAAC4OAAAbDgAAGgAAAC8OAAAZkA8HdBHwo3QI8JAO/eD/kA8H4Pyj4PWCjIPv8FOb8HWbAUOXBCKQDwZ0AvBTm/B1mwGjdBHwo3QI8JAPB+D+o+D1go6D5PBDlwQiEtDGEgAWf/R+ARKwwRKt8hLSKxJ+1BLS0RLScxLS1wLRcdOQDg==","win":"AgAADwAAADUAAAArAAAAOQYAAOEGAADgAgAAAAIAACAAAAAUAAAABAAAAB0CAAAMAgAAAQIAACEAAAAaAAAAFgIAAB0GAADiAgAAAgIAACIAAAAIAAAABwIAAB4AAACRAgAAAwIAACMAAAAVAgAALAIAAB8AAACLAgAABAAAACIAAAAXAgAADQIAACUAAAAsAgAABQAAACMAAAAcAgAAJAIAACYAAACKAgAABgAAACQAAAAYAAAADQAAABAAAACQAgAABwAAACUAAAAMAAAADgIAACcGAADmAgAACAAAACYAAAASAAAADwIAACggAAAAAgAACQAAACcAAAATAAAAMwIAACkGAADkAgAACgAAAC0AAAAvAAAANAAAAIcAAACIAgAACwAAAC4CAAAvAgAALQAAAGQGAADnAAAARgAAACoCAAAtAAAAKAYAAOUCAAASAAAATAAAAAAAAAAAAAAAAAIAABACAAARAAAASQAAAEoAAABNAAAASwAAAE4CAAATAAAATQAAAFQAAABgAAAAXQAAAFoAAABiAAAASwAAAFUAAABhAAAAXgAAAFsAAABjAAAATgAAAFYAAABXAAAAhQAAAFgAAAAAAAAAAAAAAFUAAABhAAAAXgAAAFsAAABjAAAAAAAAAFYAAABXAAAAhQAAAFgAAAAA0h9/ChLQHhIAHn8KEtAeEgAefwoC0B4AAAAAOgAAADsAAAA8AAAAPQAAAD4AAAA/AAAAQAAAAEEAAABCAAAAQwAAAEQAAABFBgAA4wAAAAoAAABKAAAAKQAAAFIAAABRAAAAUAAAAE8AAAAaAAAAFgAAAAQAAAAHAAAAKwAAABQAAAAaAAAACAAAABUAAAAbAAAABgAAABkAAAAeAAAAHwAAACAAAAAhAAAACwAAAAUAAAARAAAANgAAADcAAAA4AAAALQAAAC4AAAAJAAAAMQAAAC8AAAAwBAAAcAQAAG8AAAA8AAAAPQAAAD4AAAA/BAAAtgQAAM0EAAC1BAAA4gQAAOoEAADpDgAAAQ4AABAOAAAEAAAANQwAAQAMAAIACwADABIAAwAAAAAaAAAAFgEAAAEAAAAHDgAAFA4AAAwOAAANDgAADg4AAA8OAAAUDgAAHA4AAB0OAAAMDgAADQ4AAA4OAAAPDgAAGA4AAAUOAAAeDQACAA0AAQAOAAAXAAAALQAAAC4OAAAbDgAAGgAAAC8OAAAZ5JAJqfCj8CAiEJANz+CQCanwkA3Q4JAJqvDSMJADbeBgBpAIeHQQ8CLSP+SQCIDw0hiQCHh0EPCQAbTgcBEE8CAWDCBPCRKdQBKRCBIAHiLk9ZZ1lF91lXd1kcRDkQSQDvEE8JAO8/DkkA7v8KPwIg==","macros":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="}}} \ No newline at end of file diff --git a/dump/defaults/Halo96.json b/dump/defaults/Halo96.json deleted file mode 100644 index cb0b267..0000000 --- a/dump/defaults/Halo96.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"Halo96","firmware":"1.0.5","lights":{"colors":[136,0,0,0,0,0,0,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,0,255,0,255,0,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,0,255,15,0,255,0,64,89,255,255,255,0,0,175,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,197,0,255,36,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,255,0,0,0,255,0,0,0,255,255,255,0,255,0,255,0,255,255,255,255,255,144,14,191,239,240,194,5,228,163,240,228,144,14,193,240,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"params":[131,182,0,0,0,0,0,1,0,0,0,0,0,90,165,3,3,3,0,0,12,32,0,0,0,0,0,1,0,4,0,1,0,4,2,255,255,0,68,7,68,0,68,7,68,7,68,0,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,4,4,4,4,4,4,4,4,4,4,0,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,165,3,3,3,0,0,12,32,0,0,0,0,0,1,0,4,0,1,0,4,2,255,255,0,68,7,68,0,68,7,68,7,68,0,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,7,68,4,4,4,4,4,4,4,4,4,4,0,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,165,0,0,0,0,0,0,0,0]},"keys":{"mac":[251658242,889192448,721420288,956301312,3774873606,3758096390,2,536870914,335544320,67108864,486539264,3791650822,16777218,553648130,436207616,369098752,486539266,3808428038,33554434,570425346,134217728,117440512,503316482,2432696320,50331650,587202562,352321536,738197506,520093698,2332033024,67108866,570425344,385875968,218103810,620756994,738197504,83886082,587202560,469762048,603979778,637534210,2315255808,100663298,603979776,402653184,218103808,268435456,2415919104,117440514,620756992,201326592,234881024,654311426,3875536902,134217730,637534208,301989888,251658240,671088642,32,150994946,654311424,318767104,855638016,687865858,3825205254,167772162,754974720,788529152,872415232,2264924160,2281701376,184549378,771751936,788529154,754974722,1677721600,3875536902,402653186,704643072,754974722,671088640,3841982470,301989890,1275068416,0,0,0,268435458,285212674,1224736768,1241513984,1291845632,1258291200,1308622848,318767106,1291845632,1409286144,1610612736,1560281088,1509949440,1644167168,1258291200,1426063360,1627389952,1577058304,1526726656,1660944384,1308622848,1442840576,1459617792,2231369728,1476395008,0,0,1426063360,1627389952,1577058304,1526726656,1660944384,0,1442840576,1459617792,2231369728,1476395008,0,1869373768,857748791,1968058414,544827472,1869373768,2110775,973078528,989855744,1006632960,1023410176,3472883716,536870926,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,3808428038,167772160,1241513984,687865856,1375731712,1358954496,1342177280,1325400064,436207616,369098752,67108864,117440512,554303488,335544320,436207616,134217728,352321536,452984832,100663296,419430400,503316480,520093696,536870912,553648128,184549376,83886080,285212672,905969664,922746880,939524096,754974720,771751936,150994944,822083584,788529152,805306368,973078528,989855744,1006632960,1023410176,1040187392,1056964608,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,16777230,268435470,67108878,889192448,65548,131084,196619,196626,436207616,369098752,16777217,117440512,537526272,201326606,218103822,234881038,251658254,335544334,469762062,486539278,201326606,218103822,234881038,251658254,402653198,83886094,503316494,131085,65549,385875982,754974720,771751936,452984846,436207630,788529152,419430414,1946619792,1956900881,244379656,2432688381,4242540303,2197151907,4042228620,1978702675,2537750939,261104132,4026692614,1978702675,1956839835,1956900881,261156872,2751389703,2390947296,1139860611,304219287,1230544,2129952534,3249541633,317893906,2115120082,3520205524,309580306,3506624466,244372337],"win":[251658242,889192448,721420288,956301312,3774873606,3758096390,2,536870914,335544320,67108864,486539264,201326594,16777218,553648130,436207616,369098752,486539266,3791650822,33554434,570425346,134217728,117440512,503316482,2432696320,50331650,587202562,352321536,738197506,520093698,2332033024,67108866,570425344,385875968,218103810,620756994,738197504,83886082,587202560,469762048,603979778,637534210,2315255808,100663298,603979776,402653184,218103808,268435456,2415919104,117440514,620756992,201326592,234881024,654311426,3858759686,134217730,637534208,301989888,251658240,671088642,32,150994946,654311424,318767104,855638016,687865858,3825205254,167772162,754974720,788529152,872415232,2264924160,2281701376,184549378,771751936,788529154,754974722,1677721600,3875536902,1174405120,704643072,754974722,671088640,3841982470,301989890,1275068416,0,0,0,268435458,285212674,1224736768,1241513984,1291845632,1258291200,1308622848,318767106,1291845632,1409286144,1610612736,1560281088,1509949440,1644167168,1258291200,1426063360,1627389952,1577058304,1526726656,1660944384,1308622848,1442840576,1459617792,2231369728,1476395008,0,0,1426063360,1627389952,1577058304,1526726656,1660944384,0,1442840576,1459617792,2231369728,1476395008,0,176103378,304009234,176102912,304009234,176102912,2019330,973078528,989855744,1006632960,1023410176,1040187392,1056964608,1073741824,1090519040,1107296256,1124073472,1140850688,1157627904,3808428038,167772160,1241513984,687865856,1375731712,1358954496,1342177280,1325400064,436207616,369098752,67108864,117440512,721420288,335544320,436207616,134217728,352321536,452984832,100663296,419430400,503316480,520093696,536870912,553648128,184549376,83886080,285212672,905969664,922746880,939524096,754974720,771751936,150994944,822083584,788529152,805306368,1879048196,1862270980,1006632960,1023410176,1040187392,1056964608,3053453316,3439329284,3036676100,3791650820,3925868548,3909091332,16777230,268435470,67108878,889192448,65548,131084,196619,196626,436207616,369098752,16777217,117440512,335544334,201326606,218103822,234881038,251658254,335544334,469762062,486539278,201326606,218103822,234881038,251658254,402653198,83886094,503316494,131085,65549,385875982,754974720,771751936,452984846,436207630,788529152,419430414,2835976420,552641520,227545122,160489679,227602601,160489680,819130538,3765240720,143656544,4027610232,3829387810,4034922640,143661266,4027610232,3769893264,4026798448,537663008,2635204943,143725120,572391442,1972827620,2507497364,3297867127,2416218435,4026855694,4042460816,4010709220,586195952]}} \ No newline at end of file diff --git a/dump/defaults/Halo96.nugafile b/dump/defaults/Halo96.nugafile new file mode 100644 index 0000000..b611b4e --- /dev/null +++ b/dump/defaults/Halo96.nugafile @@ -0,0 +1 @@ +{"model":"Halo96","firmware":"1.0.5","state":{"lights":{"colors":"/wAAAP8AAAD///8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAAD/AP8A//8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP//////AP8PAP8AQFn///8AAK//AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wDFAP8k/////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP///////wAAAP8AAAD///8A/wD/AP//////kA6/7/DCBeSj8OSQDsHwkA==","effects":"AwMDAAAMIAAAAAAAAQAEAAEABAL//wBEB0QARAdEB0QARAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QHRAdEB0QEBAQEBAQEBAQEAAAAAAEBAAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAFql","custom_effect":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAD/AAAAAAAAAP//////AAAAAAAAAAAA//8AAAAA//////8AAAAAAAAAAAAA//8AAAAAAP///wD/AAAAAAAAAAAAAAAAAAAA/wAAAAAA/wAAAAAAAAAAAAAAAAAA/wD/AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="},"keys":{"mac":"AgAADwAAADUAAAArAAAAOQYAAOEGAADgAgAAAAIAACAAAAAUAAAABAAAAB0GAADiAgAAAQIAACEAAAAaAAAAFgIAAB0GAADjAgAAAgIAACIAAAAIAAAABwIAAB4AAACRAgAAAwIAACMAAAAVAgAALAIAAB8AAACLAgAABAAAACIAAAAXAgAADQIAACUAAAAsAgAABQAAACMAAAAcAgAAJAIAACYAAACKAgAABgAAACQAAAAYAAAADQAAABAAAACQAgAABwAAACUAAAAMAAAADgIAACcGAADnAgAACAAAACYAAAASAAAADwIAACggAAAAAgAACQAAACcAAAATAAAAMwIAACkGAADkAgAACgAAAC0AAAAvAAAANAAAAIcAAACIAgAACwAAAC4CAAAvAgAALQAAAGQGAADnAgAAGAAAACoCAAAtAAAAKAYAAOUCAAASAAAATAAAAAAAAAAAAAAAAAIAABACAAARAAAASQAAAEoAAABNAAAASwAAAE4CAAATAAAATQAAAFQAAABgAAAAXQAAAFoAAABiAAAASwAAAFUAAABhAAAAXgAAAFsAAABjAAAATgAAAFYAAABXAAAAhQAAAFgAAAAAAAAAAAAAAFUAAABhAAAAXgAAAFsAAABjAAAAAAAAAFYAAABXAAAAhQAAAFgAAAAASGFsbzc1IDMuME51UGh5IEhhbG83NSAAAAAAOgAAADsAAAA8AAAAPQQAAM8OAAAgAAAAQAAAAEEAAABCAAAAQwAAAEQAAABFBgAA4wAAAAoAAABKAAAAKQAAAFIAAABRAAAAUAAAAE8AAAAaAAAAFgAAAAQAAAAHAAAKIQAAABQAAAAaAAAACAAAABUAAAAbAAAABgAAABkAAAAeAAAAHwAAACAAAAAhAAAACwAAAAUAAAARAAAANgAAADcAAAA4AAAALQAAAC4AAAAJAAAAMQAAAC8AAAAwAAAAOgAAADsAAAA8AAAAPQAAAD4AAAA/AAAAQAAAAEEAAABCAAAAQwAAAEQAAABFDgAAAQ4AABAOAAAEAAAANQwAAQAMAAIACwADABIAAwAAAAAaAAAAFgEAAAEAAAAHAAAKIA4AAAwOAAANDgAADg4AAA8OAAAUDgAAHA4AAB0OAAAMDgAADQ4AAA4OAAAPDgAAGA4AAAUOAAAeDQACAA0AAQAOAAAXAAAALQAAAC4OAAAbDgAAGgAAAC8OAAAZkA8HdBHwo3QI8JAO/eD/kA8H4Pyj4PWCjIPv8FOb8HWbAUOXBCKQDwZ0AvBTm/B1mwGjdBHwo3QI8JAPB+D+o+D1go6D5PBDlwQiEtDGEgAWf/R+ARKwwRKt8hLSKxJ+1BLS0RLScxLS1wLRcdOQDg==","win":"AgAADwAAADUAAAArAAAAOQYAAOEGAADgAgAAAAIAACAAAAAUAAAABAAAAB0CAAAMAgAAAQIAACEAAAAaAAAAFgIAAB0GAADiAgAAAgIAACIAAAAIAAAABwIAAB4AAACRAgAAAwIAACMAAAAVAgAALAIAAB8AAACLAgAABAAAACIAAAAXAgAADQIAACUAAAAsAgAABQAAACMAAAAcAgAAJAIAACYAAACKAgAABgAAACQAAAAYAAAADQAAABAAAACQAgAABwAAACUAAAAMAAAADgIAACcGAADmAgAACAAAACYAAAASAAAADwIAACggAAAAAgAACQAAACcAAAATAAAAMwIAACkGAADkAgAACgAAAC0AAAAvAAAANAAAAIcAAACIAgAACwAAAC4CAAAvAgAALQAAAGQGAADnAAAARgAAACoCAAAtAAAAKAYAAOUCAAASAAAATAAAAAAAAAAAAAAAAAIAABACAAARAAAASQAAAEoAAABNAAAASwAAAE4CAAATAAAATQAAAFQAAABgAAAAXQAAAFoAAABiAAAASwAAAFUAAABhAAAAXgAAAFsAAABjAAAATgAAAFYAAABXAAAAhQAAAFgAAAAAAAAAAAAAAFUAAABhAAAAXgAAAFsAAABjAAAAAAAAAFYAAABXAAAAhQAAAFgAAAAA0h9/ChLQHhIAHn8KEtAeEgAefwoC0B4AAAAAOgAAADsAAAA8AAAAPQAAAD4AAAA/AAAAQAAAAEEAAABCAAAAQwAAAEQAAABFBgAA4wAAAAoAAABKAAAAKQAAAFIAAABRAAAAUAAAAE8AAAAaAAAAFgAAAAQAAAAHAAAAKwAAABQAAAAaAAAACAAAABUAAAAbAAAABgAAABkAAAAeAAAAHwAAACAAAAAhAAAACwAAAAUAAAARAAAANgAAADcAAAA4AAAALQAAAC4AAAAJAAAAMQAAAC8AAAAwBAAAcAQAAG8AAAA8AAAAPQAAAD4AAAA/BAAAtgQAAM0EAAC1BAAA4gQAAOoEAADpDgAAAQ4AABAOAAAEAAAANQwAAQAMAAIACwADABIAAwAAAAAaAAAAFgEAAAEAAAAHDgAAFA4AAAwOAAANDgAADg4AAA8OAAAUDgAAHA4AAB0OAAAMDgAADQ4AAA4OAAAPDgAAGA4AAAUOAAAeDQACAA0AAQAOAAAXAAAALQAAAC4OAAAbDgAAGgAAAC8OAAAZ5JAJqfCj8CAiEJANz+CQCanwkA3Q4JAJqvDSMJADbeBgBpAIeHQQ8CLSP+SQCIDw0hiQCHh0EPCQAbTgcBEE8CAWDCBPCRKdQBKRCBIAHiLk9ZZ1lF91lXd1kcRDkQSQDvEE8JAO8/DkkA7v8KPwIg==","macros":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="}}} \ No newline at end of file diff --git a/dump/defaults_test.go b/dump/defaults_test.go index e3a4b2f..4cb3e0f 100644 --- a/dump/defaults_test.go +++ b/dump/defaults_test.go @@ -16,7 +16,6 @@ func TestGetDefaults(t *testing.T) { {device.Halo75, false}, {device.Halo65, false}, {"WrongModel", true}, - // Add more test cases as needed } // Run tests @@ -31,8 +30,8 @@ func TestGetDefaults(t *testing.T) { return } - if state.Name != test.model { - t.Errorf("Unexpected device name '%v'. Expected '%v'", state.Name, test.model) + if state.Model != test.model { + t.Errorf("Unexpected device name '%v'. Expected '%v'", state.Model, test.model) } }) diff --git a/dump/restore.go b/dump/restore.go index c5673c4..7c31660 100644 --- a/dump/restore.go +++ b/dump/restore.go @@ -1,40 +1,17 @@ package dump import ( - "github.com/mishamyrt/nuga-lib/features/keys" - "github.com/mishamyrt/nuga-lib/features/light" + "github.com/mishamyrt/nuga-lib/device" + "github.com/mishamyrt/nuga-lib/features" "github.com/mishamyrt/nuga-lib/hid" ) // Restore device state -func Restore(h hid.Handler, s *State) error { - k := keys.New(h, nil) - l := light.New(h, nil) - colors := light.ParseColorsState(s.Lights.Colors) - effects := light.ParseParamsState(s.Lights.Params) - err := l.SetBacklightColors(colors) - if err != nil { +func Restore(dev *hid.Device, s *State) error { + model := device.Model(dev.Info.Model) + f := features.New(dev, model) + if err := f.Light.SetStateData(s.Data.Lights); err != nil { return err } - macros, err := keys.ParseHeadlessMacros(s.Keys.Macros) - if err != nil { - return err - } - err = k.SetMacros(macros) - if err != nil { - return err - } - err = k.SetMacCodes(s.Keys.Mac) - if err != nil { - return err - } - err = k.SetWinCodes(s.Keys.Win) - if err != nil { - return err - } - err = l.SetEffects(effects) - if err != nil { - return err - } - return nil + return f.Keys.SetStateData(s.Data.Keys) } diff --git a/dump/simulation.go b/dump/simulation.go deleted file mode 100644 index c7edc0e..0000000 --- a/dump/simulation.go +++ /dev/null @@ -1,28 +0,0 @@ -package dump - -import ( - "github.com/mishamyrt/nuga-lib" - "github.com/mishamyrt/nuga-lib/features" - "github.com/mishamyrt/nuga-lib/features/keys" - "github.com/mishamyrt/nuga-lib/features/light" -) - -// OpenSimulation opens simulated keyboard -func OpenSimulation(s *State) (*nuga.Device, error) { - capabilities := nuga.GetCapabilities(s.Name) - repo := simulateFeatures(s) - return &nuga.Device{ - Name: s.Name, - Path: "/simulated/device/path", - Firmware: s.Firmware, - Features: repo, - Capabilities: capabilities, - }, nil -} - -func simulateFeatures(s *State) *features.Features { - return &features.Features{ - Light: light.NewSimulation(&s.Lights, &s.Name), - Keys: keys.NewSimulation(&s.Keys, &s.Name), - } -} diff --git a/dump/state.go b/dump/state.go index 0d9911b..00e071c 100644 --- a/dump/state.go +++ b/dump/state.go @@ -2,14 +2,12 @@ package dump import ( "github.com/mishamyrt/nuga-lib/device" - "github.com/mishamyrt/nuga-lib/features/keys" - "github.com/mishamyrt/nuga-lib/features/light" + "github.com/mishamyrt/nuga-lib/features" ) -// State represents device state. It contains data of all supported features +// State represents raw device state. It contains data of all supported features type State struct { - Name device.Model `json:"name"` - Firmware string `json:"firmware"` - Lights light.State `json:"lights"` - Keys keys.State `json:"keys"` + Model device.Model `json:"model"` + Firmware string `json:"firmware"` + Data features.StateData `json:"state"` } diff --git a/examples/cc/main.go b/examples/cc/main.go new file mode 100644 index 0000000..8e354dc --- /dev/null +++ b/examples/cc/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/mishamyrt/nuga-lib/features/keys" + "github.com/mishamyrt/nuga-lib/internal/hex" +) + +// { +// "model": "Halo75", +// "firmware": "1.0.5", +// "state": { +// "lights": { +// "colors": "", +// "effects": "", +// "custom_effect": "" +// }, +// "keys": { +// "mac": "", +// "win": "", +// "macros": "" +// } +// } +// } + +type NewDump struct { + Model string `json:"model"` + Firmware string `json:"firmware"` + State struct { + Lights struct { + Colors []byte `json:"colors"` + Params []byte `json:"effects"` + Custom []byte `json:"custom_effect"` + } `json:"lights"` + Keys struct { + Mac []byte `json:"mac"` + Win []byte `json:"win"` + Macros []byte `json:"macros"` + } `json:"keys"` + } `json:"state"` +} + +type OldDump struct { + Name string `json:"name"` + Firmware string `json:"firmware"` + Lights struct { + Colors []byte `json:"colors"` + Params []byte `json:"params"` + } + Keys struct { + Mac []uint32 `json:"mac"` + Win []uint32 `json:"win"` + } +} + +func main() { + if len(os.Args) < 3 { + fmt.Println("Too few arguments") + fmt.Println("Usage: nuga-cc ") + os.Exit(1) + } + + path := os.Args[1] + data, err := os.ReadFile(path) + must("read file", err) + + var oldDump OldDump + err = json.Unmarshal(data, &oldDump) + must("unmarshal file", err) + + var newDump NewDump + newDump.Model = oldDump.Name + newDump.Firmware = oldDump.Firmware + newDump.State.Lights.Colors = oldDump.Lights.Colors[7:1031] + newDump.State.Lights.Params = oldDump.Lights.Params[15:138] + newDump.State.Lights.Custom = make([]byte, 1024) + newDump.State.Keys.Mac = keys.UnpackKeyCodes(oldDump.Keys.Mac) + newDump.State.Keys.Win = keys.UnpackKeyCodes(oldDump.Keys.Win) + newDump.State.Keys.Macros = make([]byte, 1024) + + hex.PrintBytes(newDump.State.Lights.Params) + // fmt.Println(len(newDump.State.Lights.Params)) + data, err = json.Marshal(newDump) + must("marshal file", err) + + err = os.WriteFile(os.Args[2], data, 0644) + must("write file", err) +} + +func must(message string, err error) { + if err != nil { + fmt.Printf("app must %s, but got error: %v\n", message, err.Error()) + os.Exit(1) + } +} diff --git a/cmd/custom-effect/gostdin/stdin.go b/examples/custom-effect/gostdin/stdin.go similarity index 100% rename from cmd/custom-effect/gostdin/stdin.go rename to examples/custom-effect/gostdin/stdin.go diff --git a/cmd/custom-effect/main.go b/examples/custom-effect/main.go similarity index 90% rename from cmd/custom-effect/main.go rename to examples/custom-effect/main.go index 2496543..0622b68 100644 --- a/cmd/custom-effect/main.go +++ b/examples/custom-effect/main.go @@ -8,8 +8,8 @@ import ( "syscall" "github.com/mishamyrt/nuga-lib" - "github.com/mishamyrt/nuga-lib/cmd/custom-effect/gostdin" - "github.com/mishamyrt/nuga-lib/cmd/custom-effect/wizard" + "github.com/mishamyrt/nuga-lib/examples/custom-effect/gostdin" + "github.com/mishamyrt/nuga-lib/examples/custom-effect/wizard" "github.com/mishamyrt/nuga-lib/features/light" ) @@ -65,7 +65,7 @@ func recordMap(dev *nuga.Device) { } func saveDump(d *nuga.Device, path string) { - state, err := d.Features.Light.GetCustomEffectColors() + state, err := d.Features.Light.GetCustomEffect() if err != nil { die("Error getting state: %v", err) } @@ -84,12 +84,12 @@ func restoreDump(d *nuga.Device, path string) { if err != nil { die("Error reading file: %v", err) } - var state light.CustomBacklightMap + var state light.CustomEffectMap err = json.Unmarshal(data, &state) if err != nil { die("Error unmarshalling: %v", err) } - err = d.Features.Light.SetCustomEffectColors(&state) + err = d.Features.Light.SetCustomEffect(&state) if err != nil { die("Error restoring state: %v", err) } diff --git a/cmd/custom-effect/wizard/request.go b/examples/custom-effect/wizard/request.go similarity index 100% rename from cmd/custom-effect/wizard/request.go rename to examples/custom-effect/wizard/request.go diff --git a/cmd/custom-effect/wizard/wizard.go b/examples/custom-effect/wizard/wizard.go similarity index 100% rename from cmd/custom-effect/wizard/wizard.go rename to examples/custom-effect/wizard/wizard.go diff --git a/cmd/describe/main.go b/examples/describe/main.go similarity index 100% rename from cmd/describe/main.go rename to examples/describe/main.go diff --git a/examples/dump/main.go b/examples/dump/main.go new file mode 100644 index 0000000..0d461a7 --- /dev/null +++ b/examples/dump/main.go @@ -0,0 +1,89 @@ +// Example app for dumping and restoring device state +package main + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/mishamyrt/nuga-lib" + "github.com/mishamyrt/nuga-lib/dump" + "github.com/mishamyrt/nuga-lib/features/keys" + "github.com/mishamyrt/nuga-lib/hid" + "github.com/mishamyrt/nuga-lib/internal/hex" +) + +const usage = "Usage: nuga-dump [restore|dump|describe] " + +func saveDump(d *hid.Device, path string) { + state, err := dump.Collect(d) + must("collect device state", err) + data, err := json.Marshal(&state) + must("marshal device state", err) + err = os.WriteFile(path, data, 0644) + must("write file", err) +} + +func restoreDump(d *hid.Device, path string) { + data, err := os.ReadFile(path) + must("read file", err) + var state dump.State + err = json.Unmarshal(data, &state) + must("unmarshal file", err) + err = dump.Restore(d, &state) + must("restore device state", err) +} + +func describeDumb(path string) { + data, err := os.ReadFile(path) + must("read file", err) + var state dump.State + err = json.Unmarshal(data, &state) + must("unmarshal file", err) + fmt.Printf("Model: %v\nFirmware: %v\n", state.Model, state.Firmware) + fmt.Println("Lights ") + fmt.Println("- Colors") + hex.PrintBytes(state.Data.Lights.Colors) + fmt.Println("- Params") + hex.PrintBytes(state.Data.Lights.Params) + fmt.Println("- Custom effect") + hex.PrintBytes(state.Data.Lights.CustomEffect) + fmt.Println("Keys") + fmt.Println("- Mac") + // hex.PrintBytes(state.State.Keys.Mac) + fmt.Println("- Win") + fmt.Println(len(keys.PackKeyCodes(state.Data.Keys.Win))) + // hex.PrintBytes(state.Data.Keys.Win) + fmt.Println("- Macros") + // fmt.Println(len(state.Data.Keys.Macros)) + // hex.PrintBytes(state.State.Keys.Macros) +} + +func main() { + if len(os.Args) < 3 { + fmt.Println("Too few arguments") + fmt.Println(usage) + os.Exit(1) + } + command := os.Args[1] + filePath := os.Args[2] + err := nuga.Init() + must("initialize connection", err) + handle, err := hid.Open() + must("open device", err) + switch command { + case "restore": + restoreDump(handle, filePath) + case "dump": + saveDump(handle, filePath) + case "describe": + describeDumb(filePath) + } +} + +func must(message string, err error) { + if err != nil { + fmt.Printf("app must %s, but got error: %v\n", message, err.Error()) + os.Exit(1) + } +} diff --git a/cmd/keycodes/annotation/print.go b/examples/keycodes/annotation/print.go similarity index 100% rename from cmd/keycodes/annotation/print.go rename to examples/keycodes/annotation/print.go diff --git a/cmd/keycodes/keymap/categories.go b/examples/keycodes/keymap/categories.go similarity index 100% rename from cmd/keycodes/keymap/categories.go rename to examples/keycodes/keymap/categories.go diff --git a/cmd/keycodes/keymap/extract.go b/examples/keycodes/keymap/extract.go similarity index 100% rename from cmd/keycodes/keymap/extract.go rename to examples/keycodes/keymap/extract.go diff --git a/cmd/keycodes/keymap/print.go b/examples/keycodes/keymap/print.go similarity index 100% rename from cmd/keycodes/keymap/print.go rename to examples/keycodes/keymap/print.go diff --git a/cmd/keycodes/main.go b/examples/keycodes/main.go similarity index 64% rename from cmd/keycodes/main.go rename to examples/keycodes/main.go index f92f129..e5efa8c 100644 --- a/cmd/keycodes/main.go +++ b/examples/keycodes/main.go @@ -6,9 +6,10 @@ import ( "fmt" "os" - "github.com/mishamyrt/nuga-lib/cmd/keycodes/annotation" - "github.com/mishamyrt/nuga-lib/cmd/keycodes/keymap" "github.com/mishamyrt/nuga-lib/dump" + "github.com/mishamyrt/nuga-lib/examples/keycodes/annotation" + "github.com/mishamyrt/nuga-lib/examples/keycodes/keymap" + "github.com/mishamyrt/nuga-lib/features/keys" "github.com/mishamyrt/nuga-lib/layout" ) @@ -29,14 +30,17 @@ func main() { if err != nil { die("Error unmarshalling: %v", err) } + macCodes := keys.PackKeyCodes(state.Data.Keys.Mac) + winCodes := keys.PackKeyCodes(state.Data.Keys.Win) + tpl := layout.GetKeystrokeTemplate(state.Model) switch cmd { case "keymap": - keymap.Print(state.Keys.Mac, layout.GetKeystrokeTemplate(state.Name), false) + keymap.Print(macCodes, tpl, false) case "annotation": fmt.Println("Mac:") - annotation.Print(state.Keys.Mac, layout.GetKeystrokeTemplate(state.Name)) + annotation.Print(macCodes, tpl) fmt.Println("Win:") - annotation.Print(state.Keys.Win, layout.GetKeystrokeTemplate(state.Name)) + annotation.Print(winCodes, tpl) } } diff --git a/features/features.go b/features/features.go index ee40958..94387cd 100644 --- a/features/features.go +++ b/features/features.go @@ -14,35 +14,34 @@ type Features struct { Keys KeysFeature } -// New creates Features instance with handle -func New(handle *hid.Device, model device.Model) *Features { - return &Features{ - Light: light.New(handle, &model), - Keys: keys.New(handle, &model), - } -} - // LightFeature represents keyboard light feature type LightFeature interface { GetEffects() (*light.Effects, error) - SetEffects(p *light.Effects) error + SetEffects(*light.Effects) error GetBacklightColors() (*light.BacklightColors, error) - SetBacklightColors(colors *light.BacklightColors) error - GetCustomEffectColors() (*light.CustomBacklightMap, error) - SetCustomEffectColors(colors *light.CustomBacklightMap) error + SetBacklightColors(*light.BacklightColors) error + GetCustomEffect() (*light.CustomEffectMap, error) + SetCustomEffect(*light.CustomEffectMap) error + GetStateData() (*light.StateData, error) + SetStateData(*light.StateData) error } // KeysFeature represents keyboard keys feature type KeysFeature interface { - GetWinCodes() ([]uint32, error) - GetMacCodes() ([]uint32, error) - SetWinCodes(keys []uint32) error - SetMacCodes(keys []uint32) error GetWin() (*keys.KeyMap, error) - GetMac() (*keys.KeyMap, error) SetWin(keyMap *keys.KeyMap) error + GetMac() (*keys.KeyMap, error) SetMac(keyMap *keys.KeyMap) error - Parse(keys []uint32) (*keys.KeyMap, error) GetMacros() (keys.Macros, error) SetMacros(macros keys.Macros) error + GetStateData() (*keys.StateData, error) + SetStateData(*keys.StateData) error +} + +// New creates Features instance with handle +func New(dev hid.Handler, model device.Model) *Features { + return &Features{ + Light: light.New(dev, model), + Keys: keys.New(dev, model), + } } diff --git a/features/keys/feature.go b/features/keys/feature.go index 5277cfe..fbba889 100644 --- a/features/keys/feature.go +++ b/features/keys/feature.go @@ -3,6 +3,7 @@ package keys import ( "github.com/mishamyrt/nuga-lib/device" "github.com/mishamyrt/nuga-lib/hid" + "github.com/mishamyrt/nuga-lib/internal/assert" "github.com/mishamyrt/nuga-lib/layout" ) @@ -13,14 +14,10 @@ type Feature struct { } // New creates keys feature instance. -func New(handle hid.Handler, model *device.Model) *Feature { - var template *layout.Template - if model != nil { - template = layout.GetKeystrokeTemplate(*model) - } +func New(handle hid.Handler, model device.Model) *Feature { return &Feature{ handle: handle, - template: template, + template: layout.GetKeystrokeTemplate(model), } } @@ -29,108 +26,126 @@ func (f *Feature) GetWin() (*KeyMap, error) { return f.getKeys(cmdGetWinKeys) } +// SetWin sets win keyboard keys +func (f *Feature) SetWin(keyMap *KeyMap) error { + return f.setKeys(cmdSetWinKeys, keyMap) +} + // GetMac returns mac keyboard keys func (f *Feature) GetMac() (*KeyMap, error) { return f.getKeys(cmdGetMacKeys) } -// SetWin sets win keyboard keys -func (f *Feature) SetWin(keyMap *KeyMap) error { - return f.setKeys(cmdGetWinKeys, cmdSetWinKeys, keyMap) -} - // SetMac sets mac keyboard keys func (f *Feature) SetMac(keyMap *KeyMap) error { - return f.setKeys(cmdGetMacKeys, cmdSetMacKeys, keyMap) -} - -// GetMacCodes returns mac keyboard key codes -func (f *Feature) GetMacCodes() ([]uint32, error) { - return f.getKeyCodes(cmdGetMacKeys) + return f.setKeys(cmdSetMacKeys, keyMap) } -// GetWinCodes returns win keyboard key codes -func (f *Feature) GetWinCodes() ([]uint32, error) { - return f.getKeyCodes(cmdGetWinKeys) -} - -// SetMacCodes sets mac keyboard key codes -func (f *Feature) SetMacCodes(keys []uint32) error { - return f.setKeyCodes(cmdSetMacKeys, keys) +// GetRawMacros returns raw keyboard macros +func (f *Feature) GetRawMacros() ([]byte, error) { + resp, err := f.handle.Request(cmdGetMacro, 1032) + if err != nil { + return nil, err + } + return resp[7:], nil } -// SetWinCodes sets win keyboard key codes -func (f *Feature) SetWinCodes(keys []uint32) error { - return f.setKeyCodes(cmdSetWinKeys, keys) +// SetRawMacros sets raw keyboard macros +func (f *Feature) SetRawMacros(data []byte) error { + request := make([]byte, 0, 1032) + request = append(request, cmdSetMacro...) + request = append(request, data...) + return f.handle.Send(request) } // GetMacros returns macros func (f *Feature) GetMacros() (Macros, error) { - resp, err := f.handle.Request(cmdGetMacro, 1032) + raw, err := f.GetRawMacros() if err != nil { return nil, err } - return ParseMacros(resp) + return ParseMacros(raw) } // SetMacros sets macros func (f *Feature) SetMacros(macros Macros) error { - request := make([]byte, 0, 1032) - request = append(request, cmdSetMacro...) data, err := macros.Bytes() if err != nil { return err } - request = append(request, data...) - return f.handle.Send(request) + return f.SetRawMacros(data) } -// Parse raw keys -func (f *Feature) Parse(keys []uint32) (*KeyMap, error) { - return ParseKeyMap(keys, f.template) -} - -func (f *Feature) getKeyCodes(cmd []byte) ([]uint32, error) { - response, err := f.handle.Request(cmd, 1032) +// GetStateData returns current raw keys state +func (f *Feature) GetStateData() (*StateData, error) { + mac, err := f.getRawKeys(cmdGetMacKeys) if err != nil { return nil, err } - return PackKeyCodes(response[7:]), nil + win, err := f.getRawKeys(cmdGetWinKeys) + if err != nil { + return nil, err + } + macros, err := f.GetRawMacros() + if err != nil { + return nil, err + } + return &StateData{ + Mac: mac, + Win: win, + Macros: macros, + }, nil } -func (f *Feature) getKeys(cmd []byte) (*KeyMap, error) { - if f.template == nil { - return nil, ErrNoTemplate +// SetStateData sets current raw keys state +func (f *Feature) SetStateData(data *StateData) error { + if err := f.setRawKeys(cmdSetMacKeys, data.Mac); err != nil { + return err + } + if err := f.setRawKeys(cmdSetWinKeys, data.Win); err != nil { + return err } - codes, err := f.getKeyCodes(cmd) + return f.SetRawMacros(data.Macros) +} + +func (f *Feature) getRawKeys(cmd []byte) ([]byte, error) { + response, err := f.handle.Request(cmd, 1032) if err != nil { return nil, err } - keys, err := ParseKeyMap(codes, f.template) + err = assert.SliceValue(response, 1, cmd[2]) if err != nil { return nil, err } - return keys, nil + return response[7:], nil } -func (f *Feature) setKeyCodes(cmd []byte, keys []uint32) error { +func (f *Feature) setRawKeys(cmd []byte, data []byte) error { request := make([]byte, 0, 1032) request = append(request, cmd...) - request = append(request, UnpackKeyCodes(keys)...) + request = append(request, data...) return f.handle.Send(request) } -func (f *Feature) setKeys(cmdGet []byte, cmdSet []byte, keys *KeyMap) error { +func (f *Feature) getKeys(cmd []byte) (*KeyMap, error) { if f.template == nil { - return ErrNoTemplate + return nil, ErrNoTemplate } - codes, err := f.getKeyCodes(cmdGet) + raw, err := f.getRawKeys(cmd) if err != nil { - return err + return nil, err } - err = keys.Apply(codes, f.template) + keys, err := ParseKeyMap(raw, f.template) if err != nil { - return err + return nil, err + } + return keys, nil +} + +func (f *Feature) setKeys(cmdSet []byte, keys *KeyMap) error { + if f.template == nil { + return ErrNoTemplate } - return f.setKeyCodes(cmdSet, codes) + raw := keys.Bytes(f.template) + return f.setRawKeys(cmdSet, raw) } diff --git a/features/keys/feature_simulation.go b/features/keys/feature_simulation.go new file mode 100644 index 0000000..418f1a4 --- /dev/null +++ b/features/keys/feature_simulation.go @@ -0,0 +1,102 @@ +package keys + +import ( + "github.com/mishamyrt/nuga-lib/device" + "github.com/mishamyrt/nuga-lib/layout" +) + +// FeatureSimulation represents simulated keys feature. +type FeatureSimulation struct { + state *State + template *layout.Template +} + +// NewSimulation creates simulated keys from template. +func NewSimulation(s *StateData, model device.Model) (*FeatureSimulation, error) { + var ( + f FeatureSimulation + err error + ) + f.template = layout.GetKeystrokeTemplate(model) + f.state, err = s.Parse(f.template) + if err != nil { + return nil, err + } + return &f, nil +} + +// GetMac returns mac keyboard keys +func (f *FeatureSimulation) GetMac() (*KeyMap, error) { + if f.template == nil { + return nil, ErrNoTemplate + } + return f.state.Mac, nil +} + +// SetMac sets mac keyboard keys +func (f *FeatureSimulation) SetMac(m *KeyMap) error { + if f.template == nil { + return ErrNoTemplate + } + f.state.Mac = m + return nil +} + +// GetWin returns win keyboard keys +func (f *FeatureSimulation) GetWin() (*KeyMap, error) { + if f.template == nil { + return nil, ErrNoTemplate + } + return f.state.Win, nil +} + +// SetWin sets win keyboard keys +func (f *FeatureSimulation) SetWin(m *KeyMap) error { + if f.template == nil { + return ErrNoTemplate + } + f.state.Win = m + return nil +} + +// GetMacros returns keyboard macros +func (f *FeatureSimulation) GetMacros() (Macros, error) { + if f.template == nil { + return nil, ErrNoTemplate + } + return f.state.Macros, nil +} + +// SetMacros sets keyboard macros +func (f *FeatureSimulation) SetMacros(m Macros) error { + if f.template == nil { + return ErrNoTemplate + } + f.state.Macros = m + return nil +} + +// GetStateData returns current simulated state. +func (f *FeatureSimulation) GetStateData() (*StateData, error) { + if f.template == nil { + return &StateData{ + Mac: make([]byte, 1024), + Win: make([]byte, 1024), + Macros: make([]byte, 1024), + }, nil + } + return f.state.Data(f.template) +} + +// SetStateData sets current simulated state. +func (f *FeatureSimulation) SetStateData(s *StateData) error { + if f.template == nil { + return ErrNoTemplate + } + state, err := s.Parse(f.template) + if err != nil { + return err + } + f.state = state + return nil +} diff --git a/features/keys/keycode.go b/features/keys/keycode.go index 7856ff9..9b1d651 100644 --- a/features/keys/keycode.go +++ b/features/keys/keycode.go @@ -2,9 +2,10 @@ package keys // PackKeyCodes packs key codes from raw keys payload func PackKeyCodes(p []byte) []uint32 { - values := make([]uint32, 256) + count := len(p) / 4 + values := make([]uint32, count) var offset int - for i := 0; i < 256; i++ { + for i := 0; i < count; i++ { for j := 0; j < 4; j++ { offset = (i * 4) + (3 - j) values[i] = (values[i] << 8) | uint32(p[offset]) @@ -15,9 +16,9 @@ func PackKeyCodes(p []byte) []uint32 { // UnpackKeyCodes unpacks key codes to raw keys payload func UnpackKeyCodes(v []uint32) []byte { - payload := make([]byte, 1024) + payload := make([]byte, len(v)*4) var offset int - for i := 0; i < 256; i++ { + for i := 0; i < len(v); i++ { offset = (i * 4) payload[offset] = byte(v[i] & 0xFF) payload[offset+1] = byte((v[i] >> 8) & 0xFF) diff --git a/features/keys/keymap.go b/features/keys/keymap.go index f744cbf..f80431d 100644 --- a/features/keys/keymap.go +++ b/features/keys/keymap.go @@ -30,31 +30,32 @@ type Key struct { // KeyMap represents keyboard layout type KeyMap map[layout.KeyName]Key -// Apply layout to key slice -func (k KeyMap) Apply(source []uint32, tpl *layout.Template) error { +// Bytes returns key map as bytes +func (k KeyMap) Bytes(tpl *layout.Template) []byte { + codes := make([]uint32, 256) for keyName, v := range k { position := tpl.GetPosition(keyName) switch v.Type { case ActionKeystroke: - source[position] = layout.FindKeyCode(v.Keystroke.Name) - if IsRegularKey(source[position]) && v.Keystroke.Modifiers != nil { - source[position] = ApplyModifiers(source[position], v.Keystroke.Modifiers) + codes[position] = layout.FindKeyCode(v.Keystroke.Name) + if IsRegularKey(codes[position]) && v.Keystroke.Modifiers != nil { + codes[position] = ApplyModifiers(codes[position], v.Keystroke.Modifiers) } case ActionMacro: - source[position] = IndexToMacro(*v.MacroIndex) + codes[position] = IndexToMacro(*v.MacroIndex) case ActionNone: - source[position] = 0 + codes[position] = 0 } - } - return nil + return UnpackKeyCodes(codes) } // ParseKeyMap key map from values -func ParseKeyMap(values []uint32, tpl *layout.Template) (*KeyMap, error) { +func ParseKeyMap(payload []byte, tpl *layout.Template) (*KeyMap, error) { + codes := PackKeyCodes(payload) keys := make(KeyMap) for key, position := range *tpl { - code := values[position] + code := codes[position] var actionType ActionType var keystroke *KeystrokeParams var macroIndex *uint8 diff --git a/features/keys/keymap_test.go b/features/keys/keymap_test.go index 3b412fc..54535f5 100644 --- a/features/keys/keymap_test.go +++ b/features/keys/keymap_test.go @@ -50,7 +50,7 @@ var testKeyMap = keys.KeyMap{ }, } -func TestApply(t *testing.T) { +func TestBytes(t *testing.T) { tests := []struct { expected layout.KeyName code uint32 @@ -64,29 +64,30 @@ func TestApply(t *testing.T) { {layout.KeyY, keys.IndexToMacro(1)}, {layout.KeyR, layout.Keys[layout.KeyNone].Code}, } - source := make([]uint32, len(tests)) - err := testKeyMap.Apply(source, &testTemplate) - if err != nil { - t.Fatalf("Apply returned an error: %v", err) + raw := testKeyMap.Bytes(&testTemplate) + codes := keys.PackKeyCodes(raw) + + if len(codes) < len(testTemplate) { + t.Errorf("Expected codes length to be more or equal %d, got %d", len(testTemplate), len(codes)) } for i, tt := range tests { t.Run(string(tt.expected), func(t *testing.T) { - if source[i] != tt.code { - t.Errorf("Expected source[%d] to be %#x, got %#x", i, tt.code, source[i]) + if codes[i] != tt.code { + t.Errorf("Expected source[%d] to be %#x, got %#x", i, tt.code, codes[i]) } }) } } func TestParseKeyMap(t *testing.T) { - values := []uint32{ + values := keys.UnpackKeyCodes([]uint32{ keys.ApplyModifiers(layout.Keys[layout.KeyM].Code, &keys.Modifiers{Ctrl: true}), layout.Keys[layout.KeyK].Code, layout.Keys[layout.KeyBrightnessUp].Code, keys.IndexToMacro(1), layout.Keys[layout.KeyNone].Code, - } + }) keyMap, err := keys.ParseKeyMap(values, &testTemplate) if err != nil { t.Fatalf("Parse returned an error: %v", err) diff --git a/features/keys/macro.go b/features/keys/macro.go index 7b3cb8d..cf2ef22 100644 --- a/features/keys/macro.go +++ b/features/keys/macro.go @@ -91,16 +91,8 @@ func (m Macros) Bytes() ([]byte, error) { return b, nil } -// ParseMacros parses macros payload with header +// ParseMacros parses macros payload func ParseMacros(payload []byte) (Macros, error) { - if payload[0] != codeMacroHeader { - return nil, ErrWrongMacroHeader - } - return ParseHeadlessMacros(payload[7:]) -} - -// ParseHeadlessMacros parses macros payload without header -func ParseHeadlessMacros(payload []byte) (Macros, error) { macros := make([]Macro, 0) for i := 0; i < len(payload); i += 128 { if i+128 > len(payload) { diff --git a/features/keys/simulation.go b/features/keys/simulation.go deleted file mode 100644 index dcdaeb2..0000000 --- a/features/keys/simulation.go +++ /dev/null @@ -1,104 +0,0 @@ -package keys - -import ( - "github.com/mishamyrt/nuga-lib/device" - "github.com/mishamyrt/nuga-lib/layout" -) - -// State represents simulation data. -type State struct { - Mac []uint32 `json:"mac"` - Win []uint32 `json:"win"` - Macros []uint8 `json:"macros,omitempty"` -} - -// FeatureSimulation represents simulated keys feature. -type FeatureSimulation struct { - defaultState State - data *State - template *layout.Template -} - -// NewSimulation creates simulated keys from template. -func NewSimulation(t *State, model *device.Model) *FeatureSimulation { - var template *layout.Template - if model != nil { - template = layout.GetKeystrokeTemplate(*model) - } - if t.Macros == nil { - t.Macros = make([]uint8, 0) - } - return &FeatureSimulation{ - data: t, - defaultState: *t, - template: template, - } -} - -// GetWinCodes returns win keyboard key codes -func (f *FeatureSimulation) GetWinCodes() ([]uint32, error) { - return f.data.Win, nil -} - -// GetMacCodes returns mac keyboard key codes -func (f *FeatureSimulation) GetMacCodes() ([]uint32, error) { - return f.data.Mac, nil -} - -// SetWinCodes sets win keyboard key codes -func (f *FeatureSimulation) SetWinCodes(keys []uint32) error { - f.data.Win = keys - return nil -} - -// SetMacCodes sets mac keyboard key codes -func (f *FeatureSimulation) SetMacCodes(keys []uint32) error { - f.data.Mac = keys - return nil -} - -// GetWin returns win keyboard keys -func (f *FeatureSimulation) GetWin() (*KeyMap, error) { - return ParseKeyMap(f.data.Win, f.template) -} - -// GetMac returns mac keyboard keys -func (f *FeatureSimulation) GetMac() (*KeyMap, error) { - return ParseKeyMap(f.data.Mac, f.template) -} - -// GetMacros returns keyboard macros -func (f *FeatureSimulation) GetMacros() (Macros, error) { - if len(f.data.Macros) > 0 { - res := make([]uint8, len(f.data.Macros), 1032) - res[0] = codeMacroHeader - copy(res[7:], f.data.Macros) - return ParseMacros(res) - } - return nil, nil -} - -// SetMacros sets keyboard macros -func (f *FeatureSimulation) SetMacros(m Macros) error { - res, err := m.Bytes() - if err != nil { - return err - } - f.data.Macros = res - return nil -} - -// SetWin sets win keyboard keys -func (f *FeatureSimulation) SetWin(keyMap *KeyMap) error { - return keyMap.Apply(f.data.Win, f.template) -} - -// SetMac sets mac keyboard keys -func (f *FeatureSimulation) SetMac(keyMap *KeyMap) error { - return keyMap.Apply(f.data.Mac, f.template) -} - -// Parse raw keys -func (f *FeatureSimulation) Parse(keys []uint32) (*KeyMap, error) { - return ParseKeyMap(keys, f.template) -} diff --git a/features/keys/state.go b/features/keys/state.go new file mode 100644 index 0000000..184216a --- /dev/null +++ b/features/keys/state.go @@ -0,0 +1,53 @@ +package keys + +import "github.com/mishamyrt/nuga-lib/layout" + +// State represents parsed keys state. +type State struct { + Mac *KeyMap `json:"mac"` + Win *KeyMap `json:"win"` + Macros Macros `json:"macros"` +} + +// Data returns raw keys state. +func (s *State) Data(tpl *layout.Template) (*StateData, error) { + macros, err := s.Macros.Bytes() + if err != nil { + return nil, err + } + mac := s.Mac.Bytes(tpl) + win := s.Win.Bytes(tpl) + return &StateData{ + Mac: mac, + Win: win, + Macros: macros, + }, nil +} + +// StateData represents raw keys state. +type StateData struct { + Mac []byte `json:"mac"` + Win []byte `json:"win"` + Macros []byte `json:"macros"` +} + +// Parse raw state data. +func (s *StateData) Parse(tpl *layout.Template) (*State, error) { + mac, err := ParseKeyMap(s.Mac, tpl) + if err != nil { + return nil, err + } + win, err := ParseKeyMap(s.Win, tpl) + if err != nil { + return nil, err + } + macros, err := ParseMacros(s.Macros) + if err != nil { + return nil, err + } + return &State{ + Mac: mac, + Win: win, + Macros: macros, + }, nil +} diff --git a/features/light/backlight_colors.go b/features/light/backlight_colors.go index 9667095..8db95aa 100644 --- a/features/light/backlight_colors.go +++ b/features/light/backlight_colors.go @@ -2,46 +2,47 @@ package light // BacklightColors represents keyboard color state. -type BacklightColors [ModesCount][ModeColorsCount]RGB +type BacklightColors [ModesCount][ModeColorsCount]Color // Set color to state. -func (b *BacklightColors) Set(modeIndex uint8, colorIndex uint8, color *RGB) { +func (b *BacklightColors) Set(modeIndex uint8, colorIndex uint8, color *Color) { b[modeIndex][colorIndex] = *color } // SetMac color to state. -func (b *BacklightColors) SetMac(modeIndex uint8, colorIndex uint8, color *RGB) { +func (b *BacklightColors) SetMac(modeIndex uint8, colorIndex uint8, color *Color) { b.Set(modeIndex+ModesCountPerOS, colorIndex, color) } // SetWin color to state. -func (b *BacklightColors) SetWin(modeIndex uint8, colorIndex uint8, color *RGB) { +func (b *BacklightColors) SetWin(modeIndex uint8, colorIndex uint8, color *Color) { b.Set(modeIndex, colorIndex, color) } // Get color from state. -func (b *BacklightColors) Get(modeIndex uint8, colorIndex uint8) *RGB { +func (b *BacklightColors) Get(modeIndex uint8, colorIndex uint8) *Color { return &b[modeIndex][colorIndex] } // GetMac returns colors for mac OS mode. -func (b *BacklightColors) GetMac() [][]RGB { +func (b *BacklightColors) GetMac() [][]Color { return b.toSlice(b[ModesCountPerOS:ModesCount]) } // GetWin returns colors for win OS mode. -func (b *BacklightColors) GetWin() [][]RGB { +func (b *BacklightColors) GetWin() [][]Color { return b.toSlice(b[0:ModesCountPerOS]) } // Bytes converts color state to raw byte slice. func (b *BacklightColors) Bytes() []byte { - var buf = make([]byte, 0, 1024) - var c RGB + payloadSize := ModesCount*ModeColorsCount*3 + len(ColorsSuffix) + var buf = make([]byte, 0, payloadSize) + var c Color for effect := range b { for i := range b[effect] { c = b[effect][i] - buf = append(buf, c.R, c.G, c.B) + buf = append(buf, c.Red(), c.Green(), c.Blue()) } } buf = append(buf, ColorsSuffix...) @@ -49,38 +50,39 @@ func (b *BacklightColors) Bytes() []byte { } // Slice returns colors as slice -func (b *BacklightColors) Slice() [][]RGB { +func (b *BacklightColors) Slice() [][]Color { return b.toSlice(b[:]) } -func (b *BacklightColors) toSlice(modes [][7]RGB) [][]RGB { - result := make([][]RGB, len(modes)) +func (b *BacklightColors) toSlice(modes [][7]Color) [][]Color { + result := make([][]Color, len(modes)) for i, colors := range modes { - result[i] = make([]RGB, 7) + result[i] = make([]Color, 7) copy(result[i], colors[:]) } return result } // ParseBacklightColors parses the raw byte slice into BacklightColors. -func ParseBacklightColors(data []byte) *BacklightColors { - var colors BacklightColors - var offset int +func ParseBacklightColors(data []byte) (*BacklightColors, error) { + if len(data) < (ModesCount * 21) { + return nil, ErrOutOfBounds + } + var ( + colors BacklightColors + offset int + ) for effect := 0; effect < ModesCount; effect++ { for i := 0; i < ModeColorsCount; i++ { - offset = (effect * 21) + (i * 3) - colors[effect][i] = RGB{ - R: data[offset], - G: data[offset+1], - B: data[offset+2], - } + offset = (effect * (ModeColorsCount * 3)) + (i * 3) + colors[effect][i] = FromRGB(data[offset], data[offset+1], data[offset+2]) } } - return &colors + return &colors, nil } // BacklightColorsFromSlice loads color state from colors slice -func BacklightColorsFromSlice(modes [][]RGB) *BacklightColors { +func BacklightColorsFromSlice(modes [][]Color) *BacklightColors { var colors BacklightColors for effect := 0; effect < ModesCount; effect++ { for i := 0; i < ModeColorsCount; i++ { diff --git a/features/light/color.go b/features/light/color.go index 571522d..3f2a8ae 100644 --- a/features/light/color.go +++ b/features/light/color.go @@ -1,54 +1,76 @@ package light import ( - "errors" "fmt" + + "github.com/mishamyrt/nuga-lib/internal/hex" ) -// RGB represents light color -type RGB struct { - R, G, B uint8 +// Color represents light color +type Color uint32 + +// String returns the hex string representation of the color +func (c Color) String() string { + return hex.FromUInt32Color(uint32(c)) +} + +// Red returns the red component of the color. +func (c Color) Red() uint8 { + return uint8(c >> 16 & 0xFF) +} + +// Green returns the green component of the color. +func (c Color) Green() uint8 { + return uint8(c >> 8 & 0xFF) +} + +// Blue returns the blue component of the color. +func (c Color) Blue() uint8 { + return uint8(c & 0xFF) +} + +// FromRGB converts RGB color to uint32 +func FromRGB(r, g, b uint8) Color { + return Color(r)<<16 | Color(g)<<8 | Color(b) } // Color presets -var ( +const ( // ColorRed represents the RGB value for the red color. - ColorRed = RGB{0xFF, 0x00, 0x00} + ColorRed Color = 0xFF0000 // ColorGreen represents the RGB value for the green color. - ColorGreen = RGB{0x00, 0xFF, 0x00} + ColorGreen Color = 0x00FF00 // ColorBlue represents the RGB value for the blue color. - ColorBlue = RGB{0x00, 0x00, 0xFF} + ColorBlue Color = 0x0000FF // ColorYellow represents the RGB value for the yellow color. - ColorYellow = RGB{0xFF, 0xFF, 0x00} + ColorYellow Color = 0xFFFF00 // ColorPurple represents the RGB value for the purple color. - ColorPurple = RGB{0xFF, 0x00, 0xFF} + ColorPurple Color = 0xFF00FF // ColorCyan represents the RGB value for the cyan color. - ColorCyan = RGB{0x00, 0xFF, 0xFF} + ColorCyan Color = 0x00FFFF // ColorWhite represents the RGB value for the white color. - ColorWhite = RGB{0xFF, 0xFF, 0xFF} + ColorWhite Color = 0xFFFFFF ) -// ErrInvalidColorFormat is returned when HEX color string format is unexpected -var ErrInvalidColorFormat = errors.New("invalid HEX color string format") - // ParseHexColor converts a string with HEX color into an RGB structure -func ParseHexColor(s string) (*RGB, error) { - c := RGB{} - var err error +func ParseHexColor(s string) (Color, error) { + var ( + err error + r, g, b uint8 + ) switch len(s) { case 7: - _, err = fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B) + _, err = fmt.Sscanf(s, "#%02x%02x%02x", &r, &g, &b) case 4: - _, err = fmt.Sscanf(s, "#%1x%1x%1x", &c.R, &c.G, &c.B) - // Double the hex digits - c.R *= 0x11 - c.G *= 0x11 - c.B *= 0x11 + _, err = fmt.Sscanf(s, "#%1x%1x%1x", &r, &g, &b) + r *= 0x11 + g *= 0x11 + b *= 0x11 default: err = ErrInvalidColorFormat } if err != nil { - return nil, ErrInvalidColorFormat + return 0, ErrInvalidColorFormat } - return &c, err + return FromRGB(r, g, b), err } diff --git a/features/light/color_test.go b/features/light/color_test.go index 207d5a5..f68221b 100644 --- a/features/light/color_test.go +++ b/features/light/color_test.go @@ -2,33 +2,34 @@ package light_test import ( "errors" - "github.com/mishamyrt/nuga-lib/features/light" "testing" + + "github.com/mishamyrt/nuga-lib/features/light" ) func TestParseHexValidLong(t *testing.T) { t.Parallel() - hexString := "#1a2b3c" - expectedRGB := light.RGB{R: 0x1a, G: 0x2b, B: 0x3c} + hexString := "#1A2B3C" + var expected light.Color = 0x1A2B3C result, err := light.ParseHexColor(hexString) if err != nil { t.Errorf("Unexpected error: %v", err) } - if *result != expectedRGB { - t.Errorf("Expected: %v, Got: %v", expectedRGB, result) + if result != expected { + t.Errorf("Expected: %v, Got: %v", expected, result) } } func TestParseHexValidShort(t *testing.T) { t.Parallel() hexString := "#f00" - expectedRGB := light.RGB{R: 0xFF, G: 0, B: 0} + var expected light.Color = 0xFF0000 result, err := light.ParseHexColor(hexString) if err != nil { t.Errorf("Unexpected error: %v", err) } - if *result != expectedRGB { - t.Errorf("Expected: %v, Got: %v", expectedRGB, result) + if result != expected { + t.Errorf("Expected: %v, Got: %v", expected, result) } } @@ -39,8 +40,8 @@ func TestParseHexInvalid(t *testing.T) { if err == nil { t.Error("Expected error, but got none") } - if value != nil { - t.Errorf("Expected nil value, but got %v", value) + if value != 0 { + t.Errorf("Expected 0 value, but got %v", value) } if !errors.Is(err, expectedErr) { t.Errorf("Expected: %v, Got: %v", expectedErr, err) diff --git a/features/light/constants.go b/features/light/constants.go index b8f60f4..1723b73 100644 --- a/features/light/constants.go +++ b/features/light/constants.go @@ -10,17 +10,13 @@ const ( ) const ( - // ParamsLength represents Halo75 params count. - ParamsLength = 123 - // EffectsOffset represents effect start offset. - EffectsOffset = 15 + effectsLength = 123 + effectsOffset = 15 - // ColorParamsOffset represents Halo75 color start offset. - ColorParamsOffset = 22 - // ColorsStartOffset represents start offset of colors response. - ColorsStartOffset = 7 - // ColorEndOffset represents end offset of colors response. - ColorEndOffset = 18 + lightsStartOffset = 7 + lightsEndOffset = 18 + + colorParamsOffset = 22 ) // Commands @@ -62,5 +58,4 @@ var ( 0x90, 0x0E, 0xBF, 0xEF, 0xF0, 0xC2, 0x05, 0xE4, 0xA3, 0xF0, 0xE4, 0x90, 0x0E, 0xC1, 0xF0, 0x90, } - codeCustomEffectHeader byte = 0x89 ) diff --git a/features/light/custom.go b/features/light/custom.go index 66ed1d1..97b4d7a 100644 --- a/features/light/custom.go +++ b/features/light/custom.go @@ -2,39 +2,37 @@ package light import "github.com/mishamyrt/nuga-lib/layout" -// CustomBacklightMap represents custom backlight colors -type CustomBacklightMap map[layout.KeyName]RGB +// CustomEffectMap represents custom backlight colors +type CustomEffectMap map[layout.KeyName]Color // Bytes returns custom colors bytes -func (c CustomBacklightMap) Bytes(tpl *layout.Template) []byte { - payload := make([]byte, 1024) +func (c CustomEffectMap) Bytes(tpl *layout.Template) ([]byte, error) { + const payloadSize = 1024 + payload := make([]byte, payloadSize) for keyName, color := range c { position := tpl.GetPosition(keyName) - payload[position] = color.R - payload[position+126] = color.G - payload[position+252] = color.B + if position+252 >= payloadSize { + return nil, ErrOutOfBounds + } + payload[position] = color.Red() + payload[position+126] = color.Green() + payload[position+252] = color.Blue() } - return payload + return payload, nil } -// ParseCustomBacklight parses custom colors -func ParseCustomBacklight(payload []byte, tpl *layout.Template) (*CustomBacklightMap, error) { - if payload[0] != byte(codeCustomEffectHeader) { - return nil, ErrWrongCustomColorsHeader - } - raw := payload[7:] - backlightMap := make(CustomBacklightMap) - var color RGB +// ParseCustomEffect parses custom effect color map +func ParseCustomEffect(payload []byte, tpl *layout.Template) (*CustomEffectMap, error) { + backlightMap := make(CustomEffectMap) for keyName, position := range *tpl { - color = RGB{ - R: raw[position], - G: raw[position+126], - B: raw[position+252], - } - if color.R == 0 && color.G == 0 && color.B == 0 { - continue + if int(position+252) >= len(payload) { + return nil, ErrOutOfBounds } - backlightMap[keyName] = color + backlightMap[keyName] = FromRGB( + payload[position], + payload[position+126], + payload[position+252], + ) } return &backlightMap, nil } diff --git a/features/light/custom_test.go b/features/light/custom_test.go index 1004117..e378b3f 100644 --- a/features/light/custom_test.go +++ b/features/light/custom_test.go @@ -14,25 +14,24 @@ var tpl = layout.Template{ layout.KeyT: 3, } -var colorMap = light.CustomBacklightMap{ - layout.KeyM: {R: 0xFF, G: 0x00, B: 0x00}, - layout.KeyY: {R: 0x00, G: 0xFF, B: 0x00}, - layout.KeyR: {R: 0x00, G: 0x00, B: 0xFF}, - layout.KeyT: {R: 0xFF, G: 0xFF, B: 0xFF}, +var colorMap = light.CustomEffectMap{ + layout.KeyM: 0xFF0000, + layout.KeyY: 0x00FF00, + layout.KeyR: 0x0000FF, + layout.KeyT: 0xFFFFFF, } func TestParseCustomBacklight(t *testing.T) { t.Parallel() - payload := make([]byte, 1050) - payload[0] = 0x89 + payload := make([]byte, 1043) for keyName, position := range tpl { - payload[7+position] = colorMap[keyName].R - payload[7+position+126] = colorMap[keyName].G - payload[7+position+252] = colorMap[keyName].B + payload[position] = colorMap[keyName].Red() + payload[position+126] = colorMap[keyName].Green() + payload[position+252] = colorMap[keyName].Blue() } - customMap, err := light.ParseCustomBacklight(payload, &tpl) + customMap, err := light.ParseCustomEffect(payload, &tpl) if err != nil { - t.Fatal("unexpected error:", err) + t.Fatalf("unexpected error on custom effect parsing: %v", err) } if len(*customMap) != 4 { t.Fatalf("expected 4 colors, got %d", len(*customMap)) @@ -46,7 +45,10 @@ func TestParseCustomBacklight(t *testing.T) { func TestCustomBacklightToBytes(t *testing.T) { t.Parallel() - payload := colorMap.Bytes(&tpl) + payload, err := colorMap.Bytes(&tpl) + if err != nil { + t.Fatalf("unexpected error on color map serialization: %v", err) + } if len(payload) != 1024 { t.Fatalf("expected 1024 bytes, got %d", len(payload)) } @@ -54,14 +56,14 @@ func TestCustomBacklightToBytes(t *testing.T) { r := payload[position] g := payload[position+126] b := payload[position+252] - if r != colorMap[keyName].R { - t.Fatalf("expected R value %v for key %v, got %v", colorMap[keyName].R, keyName, r) + if r != colorMap[keyName].Red() { + t.Fatalf("expected R value %v for key %v, got %v", colorMap[keyName].Red(), keyName, r) } - if g != colorMap[keyName].G { - t.Fatalf("expected G value %v for key %v, got %v", colorMap[keyName].G, keyName, g) + if g != colorMap[keyName].Green() { + t.Fatalf("expected G value %v for key %v, got %v", colorMap[keyName].Green(), keyName, g) } - if b != colorMap[keyName].B { - t.Fatalf("expected B value %v for key %v, got %v", colorMap[keyName].B, keyName, b) + if b != colorMap[keyName].Blue() { + t.Fatalf("expected B value %v for key %v, got %v", colorMap[keyName].Blue(), keyName, b) } } } diff --git a/features/light/effect.go b/features/light/effect.go index b86506b..88662e9 100644 --- a/features/light/effect.go +++ b/features/light/effect.go @@ -85,7 +85,7 @@ func ParseEffects(data []byte) *Effects { result.Backlight.Mode = BacklightDomain.Find(data[5]) result.Backlight.Params = make([]EffectParams, 29) for i := range result.Backlight.Params { - offset := ColorParamsOffset + (i * 2) + offset := colorParamsOffset + (i * 2) value := data[offset+1] result.Backlight.Params[i] = EffectParams{ Color: data[offset], diff --git a/features/light/errors.go b/features/light/errors.go index 0347ecf..9b5f41f 100644 --- a/features/light/errors.go +++ b/features/light/errors.go @@ -11,8 +11,11 @@ var ErrNotSupported = errors.New("current light mode does not support changing t // ErrOutOfRange is returned when the value transmitted by the user is outside the permitted limit. var ErrOutOfRange = errors.New("passed value is outside the permitted limits") -// ErrWrongCustomColorsHeader is returned when custom colors header is wrong -var ErrWrongCustomColorsHeader = errors.New("wrong custom colors header. Must be 0x89") +// ErrNoTemplate is returned when template is not ready +var ErrNoTemplate = errors.New("keyboard is not supports custom colors yet") -// ErrNoCustomColorsTemplate is returned when template is not ready -var ErrNoCustomColorsTemplate = errors.New("keyboard is not supports custom colors yet") +// ErrInvalidColorFormat is returned when hex color string format is unexpected +var ErrInvalidColorFormat = errors.New("invalid hex color string format") + +// ErrOutOfBounds is returned when position is out of bounds +var ErrOutOfBounds = errors.New("position out of bounds") diff --git a/features/light/feature.go b/features/light/feature.go index 1859947..036caec 100644 --- a/features/light/feature.go +++ b/features/light/feature.go @@ -13,124 +13,151 @@ type Feature struct { } // New creates light feature instance. -func New(handle hid.Handler, model *device.Model) *Feature { - var template *layout.Template - if model != nil { - template = layout.GetBacklightTemplate(*model) - } +func New(handle hid.Handler, model device.Model) *Feature { return &Feature{ handle: handle, - template: template, + template: layout.GetBacklightTemplate(model), } } // GetRawEffects returns raw effects data. func (f *Feature) GetRawEffects() ([]byte, error) { - response, err := f.handle.Request(CmdGetParams, 270) + raw, err := f.handle.Request(CmdGetParams, 270) if err != nil { return nil, err } - return response, nil + return raw[effectsOffset : effectsOffset+effectsLength], nil } -// GetEffectsSlice returns trimmed effects slice. -func (f *Feature) GetEffectsSlice() ([]byte, error) { - raw, err := f.GetRawEffects() +// SetRawEffects sets raw effects data. +func (f *Feature) SetRawEffects(data []byte) error { + previous, err := f.GetRawEffects() if err != nil { - return nil, err + return err } - return raw[EffectsOffset : EffectsOffset+ParamsLength], nil + paramsRequest := make([]byte, 0, 1032) + paramsRequest = append(paramsRequest, CmdSetParams...) + paramsRequest = append(paramsRequest, data...) + paramsRequest = append(paramsRequest, previous...) + paramsRequest = append(paramsRequest, make([]byte, 770)...) + return f.handle.Send(paramsRequest) } // GetEffects returns keyboard effects. func (f *Feature) GetEffects() (*Effects, error) { - params, err := f.GetEffectsSlice() + raw, err := f.GetRawEffects() if err != nil { return nil, err } - effects := ParseEffects(params) - return effects, err + return ParseEffects(raw), err } -// GetRawColors returns raw keyboard colors. -func (f *Feature) GetRawColors() ([]byte, error) { - var colors []byte - colors, err := f.handle.Request(CmdGetColors, 1050) +// SetEffects sets keyboard effects. +func (f *Feature) SetEffects(e *Effects) error { + return f.SetRawEffects(e.Bytes()) +} + +// GetRawBacklightColors returns raw keyboard colors. +func (f *Feature) GetRawBacklightColors() ([]byte, error) { + resp, err := f.handle.Request(CmdGetColors, 1050) if err != nil { - return colors, err + return nil, err } - return colors, err + return resp[lightsStartOffset : len(resp)-lightsEndOffset], nil +} + +// SetRawBacklightColors sets raw keyboard colors. +func (f *Feature) SetRawBacklightColors(data []byte) error { + cmdLength := len(CmdSetColors) + len(data) + request := make([]byte, 0, cmdLength) + request = append(request, CmdSetColors...) + request = append(request, data...) + return f.handle.Send(request) } // GetBacklightColors returns keyboard backlight colors state. func (f *Feature) GetBacklightColors() (*BacklightColors, error) { - raw, err := f.GetRawColors() + raw, err := f.GetRawBacklightColors() if err != nil { return nil, err } - colorSubset := raw[ColorsStartOffset : len(raw)-ColorEndOffset] - return ParseBacklightColors(colorSubset), err + return ParseBacklightColors(raw) } // SetBacklightColors sets keyboard backlight color state. func (f *Feature) SetBacklightColors(colors *BacklightColors) error { - colorsContent := colors.Bytes() - cmdLength := len(CmdSetColors) + len(colorsContent) - request := make([]byte, 0, cmdLength) - request = append(request, CmdSetColors...) - request = append(request, colorsContent...) - return f.handle.Send(request) + return f.SetRawBacklightColors(colors.Bytes()) } -// ResetColors resets colors to defaults. -func (f *Feature) ResetColors() error { - var colors BacklightColors - for i := range colors { - colors[i][0] = ColorRed - colors[i][1] = ColorGreen - colors[i][2] = ColorBlue - colors[i][3] = ColorYellow - colors[i][4] = ColorPurple - colors[i][5] = ColorCyan - colors[i][6] = ColorWhite +// GetRawCustomEffect returns current custom effect raw colors. +func (f *Feature) GetRawCustomEffect() ([]byte, error) { + resp, err := f.handle.Request(CmdGetCustomFirstPage, 1050) + if err != nil { + return nil, err } - return f.SetBacklightColors(&colors) + return resp[lightsStartOffset : len(resp)-lightsEndOffset], nil } -// SetEffects sets keyboard effects. -func (f *Feature) SetEffects(p *Effects) error { - currentParams, err := f.GetEffectsSlice() +// SetRawCustomEffect applies raw custom effect. +func (f *Feature) SetRawCustomEffect(p []byte) error { + req := make([]byte, 0, len(CmdSetCustomFirstPage)+len(p)) + req = append(req, CmdSetCustomFirstPage...) + req = append(req, p...) + return f.handle.Send(req) +} + +// GetCustomEffect returns current custom effect colors. +func (f *Feature) GetCustomEffect() (*CustomEffectMap, error) { + if f.template == nil { + return nil, ErrNoTemplate + } + raw, err := f.GetRawCustomEffect() if err != nil { - return err + return nil, err } - paramsRequest := make([]byte, 0, 1032) - paramsRequest = append(paramsRequest, CmdSetParams...) - paramsRequest = append(paramsRequest, p.Bytes()...) - paramsRequest = append(paramsRequest, currentParams...) - paramsRequest = append(paramsRequest, make([]byte, 770)...) - return f.handle.Send(paramsRequest) + return ParseCustomEffect(raw, f.template) } -// GetCustomEffectColors returns current custom effect colors. -func (f *Feature) GetCustomEffectColors() (*CustomBacklightMap, error) { +// SetCustomEffect sets current custom effect colors. +func (f *Feature) SetCustomEffect(colors *CustomEffectMap) error { if f.template == nil { - return nil, ErrNoCustomColorsTemplate + return ErrNoTemplate + } + payload, err := colors.Bytes(f.template) + if err != nil { + return err + } + return f.SetRawCustomEffect(payload) +} + +// GetStateData returns current keyboard light state. +func (f *Feature) GetStateData() (*StateData, error) { + colors, err := f.GetRawBacklightColors() + if err != nil { + return nil, err + } + params, err := f.GetRawEffects() + if err != nil { + return nil, err } - raw, err := f.handle.Request(CmdGetCustomFirstPage, 1050) + customEffect, err := f.GetRawCustomEffect() if err != nil { return nil, err } - return ParseCustomBacklight(raw, f.template) + return &StateData{ + Colors: colors, + Params: params, + CustomEffect: customEffect, + }, nil } -// SetCustomEffectColors sets current custom effect colors. -func (f *Feature) SetCustomEffectColors(colors *CustomBacklightMap) error { - if f.template == nil { - return ErrNoCustomColorsTemplate +// SetStateData sets keyboard light state. +func (f *Feature) SetStateData(data *StateData) error { + if err := f.SetRawBacklightColors(data.Colors); err != nil { + return err } - payload := colors.Bytes(f.template) - req := make([]byte, 0, len(CmdSetCustomFirstPage)+len(payload)) - req = append(req, CmdSetCustomFirstPage...) - req = append(req, payload...) - return f.handle.Send(req) + if err := f.SetRawEffects(data.Params); err != nil { + return err + } + return f.SetRawCustomEffect(data.CustomEffect) } diff --git a/features/light/feature_simulation.go b/features/light/feature_simulation.go new file mode 100644 index 0000000..70ab42d --- /dev/null +++ b/features/light/feature_simulation.go @@ -0,0 +1,79 @@ +package light + +import ( + "github.com/mishamyrt/nuga-lib/device" + "github.com/mishamyrt/nuga-lib/layout" +) + +// FeatureSimulation represents simulated light feature. +type FeatureSimulation struct { + state *State + template *layout.Template +} + +// NewSimulation creates simulated light from template. +func NewSimulation(s *StateData, model device.Model) (*FeatureSimulation, error) { + template := layout.GetBacklightTemplate(model) + state, err := s.Parse(template) + if err != nil { + return nil, err + } + return &FeatureSimulation{ + state: state, + template: template, + }, nil +} + +// GetEffects returns current simulated effect. +func (f *FeatureSimulation) GetEffects() (*Effects, error) { + return f.state.Effects, nil +} + +// SetEffects sets current simulated effect. +func (f *FeatureSimulation) SetEffects(e *Effects) error { + f.state.Effects = e + return nil +} + +// GetBacklightColors returns current simulated colors. +func (f *FeatureSimulation) GetBacklightColors() (*BacklightColors, error) { + return f.state.Colors, nil +} + +// SetBacklightColors sets current simulated colors. +func (f *FeatureSimulation) SetBacklightColors(c *BacklightColors) error { + f.state.Colors = c + return nil +} + +// GetCustomEffect returns current simulated custom effect colors. +func (f *FeatureSimulation) GetCustomEffect() (*CustomEffectMap, error) { + if f.template == nil { + return nil, ErrNoTemplate + } + return f.state.CustomEffect, nil +} + +// SetCustomEffect sets current simulated custom effect colors. +func (f *FeatureSimulation) SetCustomEffect(c *CustomEffectMap) error { + if f.template == nil { + return ErrNoTemplate + } + f.state.CustomEffect = c + return nil +} + +// GetStateData returns current simulated state. +func (f *FeatureSimulation) GetStateData() (*StateData, error) { + return f.state.Data(f.template), nil +} + +// SetStateData sets current simulated state. +func (f *FeatureSimulation) SetStateData(s *StateData) error { + state, err := s.Parse(f.template) + if err != nil { + return err + } + f.state = state + return nil +} diff --git a/features/light/light-simulation.go b/features/light/light-simulation.go deleted file mode 100644 index dee4cba..0000000 --- a/features/light/light-simulation.go +++ /dev/null @@ -1,86 +0,0 @@ -package light - -import ( - "github.com/mishamyrt/nuga-lib/device" - "github.com/mishamyrt/nuga-lib/internal/slices" - "github.com/mishamyrt/nuga-lib/layout" -) - -// State represents raw lights state. -type State struct { - Colors []int `json:"colors"` - Params []int `json:"params"` - CustomEffect []int `json:"custom_effect"` -} - -// FeatureSimulation represents simulated light feature. -type FeatureSimulation struct { - effects *Effects - colors *BacklightColors - template *layout.Template - customEffect []byte -} - -// ParseColorsState parses raw colors state. -func ParseColorsState(s []int) *BacklightColors { - colorsSubset := s[ColorsStartOffset : len(s)-ColorEndOffset] - return ParseBacklightColors(slices.Cast[int, byte](colorsSubset)) -} - -// ParseParamsState parses raw params state. -func ParseParamsState(s []int) *Effects { - paramsSubset := s[EffectsOffset : EffectsOffset+ParamsLength] - return ParseEffects(slices.Cast[int, byte](paramsSubset)) -} - -// NewSimulation creates simulated light from template. -func NewSimulation(t *State, model *device.Model) *FeatureSimulation { - var template *layout.Template - if model != nil { - template = layout.GetBacklightTemplate(*model) - } - return &FeatureSimulation{ - effects: ParseParamsState(t.Params), - colors: ParseColorsState(t.Colors), - template: template, - } -} - -// GetEffects returns current simulated effect. -func (f *FeatureSimulation) GetEffects() (*Effects, error) { - return f.effects, nil -} - -// SetEffects sets current simulated effect. -func (f *FeatureSimulation) SetEffects(effects *Effects) error { - f.effects = effects - return nil -} - -// GetBacklightColors returns current simulated colors. -func (f *FeatureSimulation) GetBacklightColors() (*BacklightColors, error) { - return f.colors, nil -} - -// SetBacklightColors sets current simulated colors. -func (f *FeatureSimulation) SetBacklightColors(c *BacklightColors) error { - f.colors = c - return nil -} - -// GetCustomEffectColors returns current simulated custom effect colors. -func (f *FeatureSimulation) GetCustomEffectColors() (*CustomBacklightMap, error) { - if f.template == nil { - return nil, ErrNoCustomColorsTemplate - } - return ParseCustomBacklight(f.customEffect, f.template) -} - -// SetCustomEffectColors sets current simulated custom effect colors. -func (f *FeatureSimulation) SetCustomEffectColors(colors *CustomBacklightMap) error { - if f.template == nil { - return ErrNoCustomColorsTemplate - } - f.customEffect = colors.Bytes(f.template) - return nil -} diff --git a/features/light/state.go b/features/light/state.go new file mode 100644 index 0000000..e169428 --- /dev/null +++ b/features/light/state.go @@ -0,0 +1,58 @@ +package light + +import ( + "github.com/mishamyrt/nuga-lib/layout" + "github.com/pkg/errors" +) + +// State represents parsed lights state. +type State struct { + Colors *BacklightColors `json:"colors"` + Effects *Effects `json:"effects"` + CustomEffect *CustomEffectMap `json:"custom_effect"` +} + +// Data returns raw state data. +func (s *State) Data(tpl *layout.Template) *StateData { + var customEffect []byte + if tpl != nil { + custom, err := s.CustomEffect.Bytes(tpl) + if err == nil { + customEffect = custom + } + } + return &StateData{ + Colors: s.Colors.Bytes(), + Params: s.Effects.Bytes(), + CustomEffect: customEffect, + } +} + +// StateData represents raw lights state. +type StateData struct { + Colors []byte `json:"colors"` + Params []byte `json:"effects"` + CustomEffect []byte `json:"custom_effect"` +} + +// Parse raw state data. +func (s *StateData) Parse(tpl *layout.Template) (*State, error) { + colors, err := ParseBacklightColors(s.Colors) + if err != nil { + return nil, errors.Wrap(err, "colors") + } + effects := ParseEffects(s.Params) + var customEffect *CustomEffectMap + if tpl != nil { + custom, err := ParseCustomEffect(s.CustomEffect, tpl) + if err != nil { + return nil, errors.Wrap(err, "custom effect") + } + customEffect = custom + } + return &State{ + Colors: colors, + Effects: effects, + CustomEffect: customEffect, + }, nil +} diff --git a/features/simulation.go b/features/simulation.go new file mode 100644 index 0000000..d9c50eb --- /dev/null +++ b/features/simulation.go @@ -0,0 +1,30 @@ +package features + +import ( + "github.com/mishamyrt/nuga-lib/device" + "github.com/mishamyrt/nuga-lib/features/keys" + "github.com/mishamyrt/nuga-lib/features/light" + "github.com/pkg/errors" +) + +// StateData represents raw features state. It contains data of all supported features +type StateData struct { + Lights *light.StateData `json:"lights"` + Keys *keys.StateData `json:"keys"` +} + +// NewSimulation creates simulated features from state +func NewSimulation(s *StateData, model device.Model) (*Features, error) { + l, err := light.NewSimulation(s.Lights, model) + if err != nil { + return nil, errors.Wrap(err, "lights") + } + k, err := keys.NewSimulation(s.Keys, model) + if err != nil { + return nil, errors.Wrap(err, "keys") + } + return &Features{ + Light: l, + Keys: k, + }, nil +} diff --git a/go.mod b/go.mod index 009bf43..cbf1f06 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,9 @@ module github.com/mishamyrt/nuga-lib go 1.20 -require github.com/sstallion/go-hid v0.14.1 +require ( + github.com/pkg/errors v0.9.1 + github.com/sstallion/go-hid v0.14.1 +) require golang.org/x/sys v0.9.0 // indirect diff --git a/go.sum b/go.sum index 89acf8f..f22fbad 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/sstallion/go-hid v0.14.1 h1:shbZlKqv5fr1KnxwqtLEPGkOoA6OSUWTx9TblegATvc= github.com/sstallion/go-hid v0.14.1/go.mod h1:fPKp4rqx0xuoTV94gwKojsPG++KNKhxuU88goGuGM7I= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= diff --git a/hid/device.go b/hid/device.go index e31eb35..28b6b11 100644 --- a/hid/device.go +++ b/hid/device.go @@ -5,6 +5,7 @@ import ( "sync" "time" + "github.com/mishamyrt/nuga-lib/internal/assert" "github.com/sstallion/go-hid" ) @@ -76,7 +77,6 @@ func (d *Device) Read(count int) ([]byte, error) { if err != nil { return nil, err } - packet := buf[1:] if d.debug { if length > 0 { log.Printf("Read %v", buf) @@ -85,20 +85,26 @@ func (d *Device) Read(count int) ([]byte, error) { } } d.waitSync() - return packet, nil + return buf[1:], nil } // Request sends a request to the device. -func (d *Device) Request(payload []byte, count int) ([]byte, error) { - var resp []byte - var err error +func (d *Device) Request(request []byte, count int) ([]byte, error) { + var ( + resp []byte + err error + ) for i := 0; i < requestRetries; i++ { if d.debug { log.Println("Read attempt", i+1) } - resp, err = d.tryRequest(payload, count) - if len(resp) > 0 && resp[0] != 0 { - return resp, nil + resp, err = d.tryRequest(request, count) + if len(resp) > 0 { + err = assert.SliceValue(resp, 0, request[1]) + if err != nil { + return nil, err + } + return resp, err } } if err != nil { @@ -130,7 +136,15 @@ func (d *Device) tryRequest(payload []byte, count int) ([]byte, error) { if err != nil { return nil, err } - return d.Read(count) + resp, err := d.Read(count) + if err != nil { + return nil, err + } + // The first element is feature report id, it is truncated + if len(resp) != count-1 { + return nil, NewErrCountMismatch(count-1, len(resp)) + } + return resp, nil } func (d *Device) waitSync() { diff --git a/hid/errors.go b/hid/errors.go index 3315d85..cafd788 100644 --- a/hid/errors.go +++ b/hid/errors.go @@ -9,8 +9,8 @@ import ( // ErrNotFound is returned when the device wasn't found var ErrNotFound = errors.New("keyboard is not found") -// ErrCountMismatch is returned when transmitted number of bytes is not expected -var ErrCountMismatch = errors.New("transmitted number of bytes is not expected") +// ErrCountMismatch is returned when number of bytes is not expected +var ErrCountMismatch = errors.New("unexpected bytes count") // NewErrCountMismatch creates a byte count mismatch error. func NewErrCountMismatch(expected, actual int) error { diff --git a/internal/assert/errors.go b/internal/assert/errors.go new file mode 100644 index 0000000..eaf41f5 --- /dev/null +++ b/internal/assert/errors.go @@ -0,0 +1,9 @@ +package assert + +import "errors" + +// ErrUnexpectedValue is returned when an unexpected value is found at a position +var ErrUnexpectedValue = errors.New("unexpected value") + +// ErrNotFound is returned when an expected value is not found +var ErrNotFound = errors.New("not found") diff --git a/internal/assert/slice.go b/internal/assert/slice.go new file mode 100644 index 0000000..d34f103 --- /dev/null +++ b/internal/assert/slice.go @@ -0,0 +1,26 @@ +// Package assert contains assertion functions +package assert + +import ( + "fmt" + + "github.com/pkg/errors" +) + +// SliceValue returns error if value at position is not equal to target value +func SliceValue[T comparable](haystack []T, position int, needle T) error { + if haystack[position] == needle { + return nil + } + return errors.Wrap(ErrUnexpectedValue, fmt.Sprintf("%v at %v", needle, position)) +} + +// SliceIncludes returns error if target value is not found in slice +func SliceIncludes[T comparable](haystack []T, needle T) error { + for _, v := range haystack { + if v == needle { + return nil + } + } + return errors.Wrap(ErrNotFound, fmt.Sprintf("%v", needle)) +} diff --git a/internal/assert/slice_test.go b/internal/assert/slice_test.go new file mode 100644 index 0000000..d0f6c5e --- /dev/null +++ b/internal/assert/slice_test.go @@ -0,0 +1,33 @@ +package assert_test + +import ( + "testing" + + "github.com/mishamyrt/nuga-lib/internal/assert" +) + +func TestSliceValue(t *testing.T) { + t.Parallel() + err := assert.SliceValue([]int{1, 2, 3}, 2, 3) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + err = assert.SliceValue([]int{1, 2, 3}, 2, 4) + if err == nil { + t.Error("Expected error, but got none") + } +} + +func TestSliceIncludes(t *testing.T) { + t.Parallel() + err := assert.SliceIncludes([]int{1, 2, 3}, 3) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + err = assert.SliceIncludes([]int{1, 2, 3}, 4) + if err == nil { + t.Error("Expected error, but got none") + } +} diff --git a/internal/hex/format.go b/internal/hex/format.go index c7440d7..633c6bf 100644 --- a/internal/hex/format.go +++ b/internal/hex/format.go @@ -1,14 +1,22 @@ // Package hex provides functions for formatting hex strings package hex -import "fmt" +import ( + "fmt" + "strings" +) // FromByte converts byte to hex string func FromByte(v byte) string { - return fmt.Sprintf("%02x", v) + return strings.ToUpper(fmt.Sprintf("%02x", v)) } // FromUInt32 converts uint32 to hex string func FromUInt32(v uint32) string { - return fmt.Sprintf("0x%08x", v) + return "0x" + strings.ToUpper(fmt.Sprintf("%08x", v)) +} + +// FromUInt32Color converts uint32 color to hex +func FromUInt32Color(v uint32) string { + return strings.ToUpper(fmt.Sprintf("#%06x", v)) } diff --git a/internal/hex/format_test.go b/internal/hex/format_test.go index e3e5417..3d9ca32 100644 --- a/internal/hex/format_test.go +++ b/internal/hex/format_test.go @@ -13,7 +13,7 @@ func TestFromByte(t *testing.T) { }{ {0x00, "00"}, {0x01, "01"}, - {0xFF, "ff"}, + {0xFF, "FF"}, } for _, tt := range tests { @@ -32,7 +32,7 @@ func TestFromUInt32(t *testing.T) { }{ {0x00000000, "0x00000000"}, {0x00000001, "0x00000001"}, - {0xffffffff, "0xffffffff"}, + {0xFFFFFFFF, "0xFFFFFFFF"}, } for _, tt := range tests { @@ -43,3 +43,23 @@ func TestFromUInt32(t *testing.T) { }) } } + +func TestFromUInt32Color(t *testing.T) { + tests := []struct { + input uint32 + output string + }{ + {0xFF0000, "#FF0000"}, + {0xFF00FF, "#FF00FF"}, + {0xFFFFFF, "#FFFFFF"}, + {0x000000, "#000000"}, + } + + for _, tt := range tests { + t.Run(tt.output, func(t *testing.T) { + if result := hex.FromUInt32Color(tt.input); result != tt.output { + t.Errorf("Expected FromUInt32Color(%#x) to be %v, got %v", tt.input, tt.output, result) + } + }) + } +} diff --git a/internal/hex/print_test.go b/internal/hex/print_test.go index cbbe05d..14b237d 100644 --- a/internal/hex/print_test.go +++ b/internal/hex/print_test.go @@ -15,7 +15,7 @@ func TestPrintBytes(t *testing.T) { }{ {"empty", []byte{}, "[]\n"}, {"single", []byte{0x00}, "[00]\n"}, - {"multiple", []byte{0x00, 0x01, 0xFF}, "[00, 01, ff]\n"}, + {"multiple", []byte{0x00, 0x01, 0xFF}, "[00, 01, FF]\n"}, } out := bytes.NewBuffer([]byte{}) diff --git a/simulation.go b/simulation.go new file mode 100644 index 0000000..a269f81 --- /dev/null +++ b/simulation.go @@ -0,0 +1,23 @@ +package nuga + +import ( + "github.com/mishamyrt/nuga-lib/dump" + "github.com/mishamyrt/nuga-lib/features" +) + +const fakePath = "/simulated/device/path" + +// FromTemplate creates simulated keyboard +func FromTemplate(t *dump.State) (*Device, error) { + f, err := features.NewSimulation(&t.Data, t.Model) + if err != nil { + return nil, err + } + return &Device{ + Name: t.Model, + Path: fakePath, + Firmware: t.Firmware, + Features: f, + Capabilities: GetCapabilities(t.Model), + }, nil +} diff --git a/dump/simulation_test.go b/simulation_test.go similarity index 84% rename from dump/simulation_test.go rename to simulation_test.go index 0a78393..be76e59 100644 --- a/dump/simulation_test.go +++ b/simulation_test.go @@ -1,4 +1,4 @@ -package dump_test +package nuga_test import ( "encoding/json" @@ -6,11 +6,12 @@ import ( "os" "testing" + "github.com/mishamyrt/nuga-lib" "github.com/mishamyrt/nuga-lib/dump" ) func readTemplate(model string) (*dump.State, error) { - path := fmt.Sprintf("defaults/%v.json", model) + path := fmt.Sprintf("dump/defaults/%v.nugafile", model) content, err := os.ReadFile(path) if err != nil { return nil, err @@ -30,7 +31,7 @@ func TestOpenSimulation(t *testing.T) { if err != nil { t.Errorf("Error while reading template: %v", err) } - device, err := dump.OpenSimulation(template) + device, err := nuga.FromTemplate(template) if err != nil { t.Errorf("Expected error on opening simulation: %v", err) }