diff --git a/go.mod b/go.mod index 768706e..8288e1c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( github.com/gdamore/tcell/v2 v2.3.11 github.com/google/go-cmp v0.3.0 + github.com/jinzhu/copier v0.3.2 github.com/mattn/go-runewidth v0.0.13 // indirect github.com/rivo/tview v0.0.0-20210312174852-ae9464cc3598 github.com/spf13/cobra v1.1.3 diff --git a/go.sum b/go.sum index ad546dc..c17528c 100644 --- a/go.sum +++ b/go.sum @@ -98,6 +98,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jinzhu/copier v0.3.2 h1:QdBOCbaouLDYaIPFfi1bKv5F5tPpeTwXe4sD0jqtz5w= +github.com/jinzhu/copier v0.3.2/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= diff --git a/ui/actions.go b/ui/actions.go index 113d6c0..1d2999e 100644 --- a/ui/actions.go +++ b/ui/actions.go @@ -4,27 +4,16 @@ import ( "context" "fmt" + "github.com/jinzhu/copier" "github.com/tenntenn/natureremo" ) type ( // process action, must return new state - ActionFunc func(state *State, action Action, ctx interface{}) error + Action func(state *State, cli *natureremo.Client, ctx interface{}) error ) -// Action type -type Action string - -var ( - GetAppliances Action = "get appliances" - GetDevices Action = "get devices" - PowerON Action = "power on" - PowerOFF Action = "power off" - OpenUpdateApplianceView Action = "open update appliance view" - UpdateAirConSettings Action = "update aircon settings" -) - -func ActionGetAppliances(state *State, action Action, ctx interface{}) error { +func ActionGetAppliances(state *State, cli *natureremo.Client, ctx interface{}) error { apps, err := Client.ApplianceService.GetAll(context.Background()) if err != nil { return err @@ -33,7 +22,7 @@ func ActionGetAppliances(state *State, action Action, ctx interface{}) error { return nil } -func ActionGetDevices(state *State, action Action, ctx interface{}) error { +func ActionGetDevices(state *State, cli *natureremo.Client, ctx interface{}) error { devices, err := Client.DeviceService.GetAll(context.Background()) if err != nil { return err @@ -51,33 +40,42 @@ func ActionGetDevices(state *State, action Action, ctx interface{}) error { return nil } -func ActionAppliancesPower(state *State, action Action, ctx interface{}) error { - app, err := getAppliance(state, ctx) +func ActionAppliancesPower(state *State, cli *natureremo.Client, ctx interface{}) error { + data, ok := ctx.(AppliancePowerOnOff) + if !ok { + return fmt.Errorf(`ctx is not "AppliancePowerOnOff": %T`, ctx) + } + + var row interface{} = data.Row + app, err := getAppliance(state, row) if err != nil { return err } - on := action == PowerON + on := data.Power == natureremo.ButtonPowerOn switch app.Type { case natureremo.ApplianceTypeAirCon: - app.AirConSettings.Button = natureremo.ButtonPowerOn - + btn := natureremo.ButtonPowerOn if !on { - app.AirConSettings.Button = natureremo.ButtonPowerOff + btn = natureremo.ButtonPowerOff } - err = Client.ApplianceService. - UpdateAirConSettings(context.Background(), app, app.AirConSettings) + settings := &natureremo.AirConSettings{ + Button: btn, + } + app.AirConSettings.Button = btn + err = cli.ApplianceService. + UpdateAirConSettings(context.Background(), app, settings) case natureremo.ApplianceTypeLight: power := "on" if !on { power = "off" } app.Light.State.Power = power - _, err = Client.ApplianceService.SendLightSignal(context.Background(), app, power) + _, err = cli.ApplianceService.SendLightSignal(context.Background(), app, power) case natureremo.ApplianceTypeTV: btn := "power" - _, err = Client.ApplianceService.SendTVSignal(context.Background(), app, btn) + _, err = cli.ApplianceService.SendTVSignal(context.Background(), app, btn) default: return fmt.Errorf("unsupported appliance: %v", app.Type) } @@ -85,7 +83,7 @@ func ActionAppliancesPower(state *State, action Action, ctx interface{}) error { return err } -func ActionOpenUpdateApplianceView(state *State, action Action, ctx interface{}) error { +func ActionOpenUpdateApplianceView(state *State, cli *natureremo.Client, ctx interface{}) error { app, err := getAppliance(state, ctx) if err != nil { return err @@ -101,22 +99,24 @@ func ActionOpenUpdateApplianceView(state *State, action Action, ctx interface{}) return nil } -func ActionOpenUpdateAirConSettings(state *State, action Action, ctx interface{}) error { - data, ok := ctx.(map[string]UpdateAirConFormData) +func ActionUpdateAirConSettings(state *State, cli *natureremo.Client, ctx interface{}) error { + data, ok := ctx.(map[int]UpdateAirConFormData) if !ok { - return fmt.Errorf(`ctx type is not valid type: %T`, ctx) + return fmt.Errorf(`ctx is invalid type: %T`, ctx) } var ( - id string + idx int form UpdateAirConFormData ) - for id, form = range data { + for idx, form = range data { break } - app := &natureremo.Appliance{ID: id} + oldapp := state.Appliances[idx] + + app := &natureremo.Appliance{ID: oldapp.ID} settings := &natureremo.AirConSettings{} if form.Power.Value() == "ON" { @@ -142,11 +142,14 @@ func ActionOpenUpdateAirConSettings(state *State, action Action, ctx interface{} return err } - newApps, err := Client.ApplianceService.GetAll(context.Background()) + err := copier.CopyWithOption(oldapp.AirConSettings, settings, + copier.Option{IgnoreEmpty: true, DeepCopy: true}) if err != nil { return err } - state.Appliances = newApps + // NOTE copier option is ignore empty, but when power is on, the value is empty + // so copier doesn't copy button + oldapp.AirConSettings.Button = settings.Button return nil } diff --git a/ui/appliances.go b/ui/appliances.go index 29e6801..ac788b9 100644 --- a/ui/appliances.go +++ b/ui/appliances.go @@ -40,6 +40,11 @@ func (a *Appliances) GetSelect() int { return row } +type AppliancePowerOnOff struct { + Power natureremo.Button + Row int +} + func NewAppliances() *Appliances { a := &Appliances{ Table: tview.NewTable().SetSelectable(true, false), @@ -61,11 +66,19 @@ func NewAppliances() *Appliances { row := a.GetSelect() switch event.Rune() { case 'u': - Dispatcher.Dispatch(PowerON, row) + ctx := AppliancePowerOnOff{ + Power: natureremo.ButtonPowerOn, + Row: row, + } + Dispatcher.Dispatch(ActionAppliancesPower, ctx) case 'd': - Dispatcher.Dispatch(PowerOFF, row) + ctx := AppliancePowerOnOff{ + Power: natureremo.ButtonPowerOff, + Row: row, + } + Dispatcher.Dispatch(ActionAppliancesPower, ctx) case 'o': - Dispatcher.Dispatch(OpenUpdateApplianceView, row) + Dispatcher.Dispatch(ActionOpenUpdateApplianceView, row) } return event }) @@ -80,7 +93,8 @@ func (a *Appliances) OpenUpdateAirConView(app *natureremo.Appliance) { viewData := ToUpdateAirConViewData(app) - dispatcher := make(chan map[string]UpdateAirConFormData) + row := a.GetSelect() + dispatcher := make(chan map[int]UpdateAirConFormData) addTemp := func() { form.AddDropDown("Temperature", viewData.Temp.Values, viewData.Temp.Current, @@ -89,7 +103,7 @@ func (a *Appliances) OpenUpdateAirConView(app *natureremo.Appliance) { return } viewData.Temp.Current = idx - updateData := map[string]UpdateAirConFormData{app.ID: viewData} + updateData := map[int]UpdateAirConFormData{row: viewData} dispatcher <- updateData }) } @@ -101,7 +115,7 @@ func (a *Appliances) OpenUpdateAirConView(app *natureremo.Appliance) { return } viewData.Volume.Current = idx - updateData := map[string]UpdateAirConFormData{app.ID: viewData} + updateData := map[int]UpdateAirConFormData{row: viewData} dispatcher <- updateData }) } @@ -135,7 +149,7 @@ func (a *Appliances) OpenUpdateAirConView(app *natureremo.Appliance) { return } viewData.Power.Current = idx - updateData := map[string]UpdateAirConFormData{app.ID: viewData} + updateData := map[int]UpdateAirConFormData{row: viewData} dispatcher <- updateData }) @@ -145,7 +159,7 @@ func (a *Appliances) OpenUpdateAirConView(app *natureremo.Appliance) { return } viewData.Mode.Current = idx - updateData := map[string]UpdateAirConFormData{app.ID: viewData} + updateData := map[int]UpdateAirConFormData{row: viewData} dispatcher <- updateData toggleItems() }) @@ -158,13 +172,13 @@ func (a *Appliances) OpenUpdateAirConView(app *natureremo.Appliance) { return } viewData.Direction.Current = idx - updateData := map[string]UpdateAirConFormData{app.ID: viewData} + updateData := map[int]UpdateAirConFormData{row: viewData} dispatcher <- updateData }) // update appliance with view data go func() { for data := range dispatcher { - Dispatcher.Dispatch(UpdateAirConSettings, data) + Dispatcher.Dispatch(ActionUpdateAirConSettings, data) } log.Println("aircon settings dispatcher goroutine is closed") }() diff --git a/ui/dispatcher.go b/ui/dispatcher.go index ef7367b..0c2a30a 100644 --- a/ui/dispatcher.go +++ b/ui/dispatcher.go @@ -3,15 +3,13 @@ package ui import ( "bytes" "encoding/json" - "fmt" "log" "github.com/google/go-cmp/cmp" ) type dispatcher struct { - state *State - actions map[Action]ActionFunc + state *State } func copyState(state *State) *State { @@ -32,15 +30,8 @@ func copyState(state *State) *State { } func (d *dispatcher) Dispatch(action Action, ctx interface{}) { - f, ok := d.actions[action] - if !ok { - msg := fmt.Sprintf("doesn't register action: %v\n", action) - UI.Message(msg) - return - } - old := copyState(d.state) - err := f(d.state, action, ctx) + err := action(d.state, Client, ctx) if err != nil { UI.Message(err.Error()) return @@ -69,13 +60,5 @@ var Dispatcher *dispatcher func init() { Dispatcher = &dispatcher{ state: &State{}, - actions: map[Action]ActionFunc{ - GetAppliances: ActionGetAppliances, - GetDevices: ActionGetDevices, - PowerON: ActionAppliancesPower, - PowerOFF: ActionAppliancesPower, - OpenUpdateApplianceView: ActionOpenUpdateApplianceView, - UpdateAirConSettings: ActionOpenUpdateAirConSettings, - }, } } diff --git a/ui/ui.go b/ui/ui.go index 2b7b671..bcb899c 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -151,13 +151,13 @@ func Start() { UI.app.SetFocus(apps) - Dispatcher.Dispatch(GetAppliances, nil) - Dispatcher.Dispatch(GetDevices, nil) + Dispatcher.Dispatch(ActionGetAppliances, nil) + Dispatcher.Dispatch(ActionGetDevices, nil) go func() { t := time.NewTicker(INTERVAL * time.Hour) for range t.C { - Dispatcher.Dispatch(GetDevices, nil) + Dispatcher.Dispatch(ActionGetDevices, nil) } }()