Skip to content

Commit fb0cf17

Browse files
committed
Remove redundant mcs status subcommands
Removed battery, fuel, location, tires, and doors subcommands from 'mcs status'. The main command shows all info, and --json can be used for scripting. This eliminates redundancy and inconsistency (some sections had subcommands, others didn't). Fixes: mcs-278q
1 parent 01a4884 commit fb0cf17

File tree

3 files changed

+13
-420
lines changed

3 files changed

+13
-420
lines changed

.beads/issues.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
{"id":"mcs-26","title":"Refactor CLI tests to use table-driven pattern","description":"Tests for status subcommands (battery, fuel, location, tires, doors) repeat same pattern:\n cmd := NewStatusCmd()\n for _, subCmd := range cmd.Commands() {\n if subCmd.Use == \"battery\" { ... }\n }\n\nConvert to table-driven test:\n subcommands := []string{\"battery\", \"fuel\", \"location\", \"tires\", \"doors\"}\n for _, name := range subcommands {\n t.Run(name, func(t *testing.T) { ... })\n }\n\nAlso deduplicate test client setup using shared fixtures.\n\nFiles: internal/cli/status_test.go, internal/api/vehicle_test.go","acceptance_criteria":"- [ ] Subcommand tests use table-driven pattern\n- [ ] Test client setup uses shared helper\n- [ ] Tests clearly document expected behavior\n- [ ] No duplicate test setup code\n- [ ] All tests pass","status":"closed","priority":1,"issue_type":"chore","created_at":"2025-12-18T03:19:24.991759-08:00","updated_at":"2025-12-18T22:36:26.035364-08:00","closed_at":"2025-12-18T11:19:43.041731-08:00","dependencies":[{"issue_id":"mcs-26","depends_on_id":"mcs-25","type":"related","created_at":"2025-12-18T03:20:22.636349-08:00","created_by":"carlosvillela","metadata":"{}"}]}
3232
{"id":"mcs-2683","title":"Improve confirmation polling: 20s HVAC delay, 5s poll interval, in-place progress","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T03:21:15.478627-08:00","updated_at":"2025-12-21T03:21:22.183812-08:00","closed_at":"2025-12-21T03:21:22.183812-08:00","close_reason":"Implemented: 20s initial delay for HVAC commands, 5s poll interval, in-place progress updates with carriage return"}
3333
{"id":"mcs-27","title":"Add context.Context support to API client","description":"The API client doesn't support Go's context.Context for cancellation and timeouts. This is important for CLI applications where users might want to cancel long-running operations.\n\nAdd context parameter to API methods:\n func (c *Client) APIRequest(ctx context.Context, method, uri string, ...) error\n func (c *Client) DoorLock(ctx context.Context, internalVIN string) error\n\nFiles: internal/api/client.go, control.go, vehicle.go","design":"## Migration:\n1. Add ctx param to APIRequest, use http.NewRequestWithContext\n2. Update control.go and vehicle.go functions\n3. CLI: use cmd.Context() from cobra or context.WithTimeout\n\nStart with context.TODO() during migration, replace incrementally","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-18T03:19:25.041813-08:00","updated_at":"2025-12-18T22:36:26.035167-08:00","closed_at":"2025-12-18T11:34:23.679134-08:00"}
34+
{"id":"mcs-278q","title":"Remove redundant mcs status subcommands","description":"The `mcs status` command has subcommands (battery, fuel, location, tires, doors) that are redundant with the main status output.\n\n## Current state\n- `mcs status` shows all info: battery, fuel, HVAC, doors, windows, hazards, tires, location, odometer\n- Subcommands show individual sections: battery, fuel, location, tires, doors\n- Inconsistent: some sections have subcommands, some don't (windows, hvac, odometer)\n\n## Proposal\nRemove all subcommands. The main `mcs status` command provides all the info, and `mcs status --json` can be used for scripting.\n\n## Subcommands to remove\n- `mcs status battery`\n- `mcs status fuel`\n- `mcs status location`\n- `mcs status tires`\n- `mcs status doors`","status":"in_progress","priority":2,"issue_type":"task","created_at":"2025-12-29T10:12:51.254642-08:00","updated_at":"2025-12-29T10:13:02.980464-08:00"}
3435
{"id":"mcs-28","title":"Replace custom base64Encode with stdlib","description":"A custom base64 encoding function (30 lines) is implemented when Go's standard library provides this.\n\nLocation: internal/api/auth.go:379-410\n\nReplace with:\n encoding/base64.StdEncoding.EncodeToString(data)\n\nwhich is already used elsewhere in the codebase (crypto.go:32).","status":"closed","priority":1,"issue_type":"chore","created_at":"2025-12-18T03:19:47.250193-08:00","updated_at":"2025-12-18T22:36:26.034948-08:00","closed_at":"2025-12-18T11:09:39.669959-08:00"}
3536
{"id":"mcs-29","title":"Remove hardcoded vehicle model from status output","description":"The vehicle model is hardcoded in status.go:223:\n output := \"\\nCX-90 GT PHEV (2025)\\n\"\n\nThis should be extracted from vecBaseInfos or made configurable to support other vehicle models.\n\nFiles: internal/cli/status.go","design":"## Options:\n1. Extract from vecBaseInfos response (preferred) - look for CarName/ModelName/ModelYear fields\n2. Add to config.toml as fallback\n\nRun `mcs raw status` to see actual JSON structure for field names","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-18T03:19:47.315867-08:00","updated_at":"2025-12-18T22:36:26.034754-08:00","closed_at":"2025-12-18T11:08:33.964914-08:00"}
3637
{"id":"mcs-29on","title":"Nil pointer dereference when TokenCache.Load returns nil","description":"TokenCache.Load returns nil when the cache file does not exist, but callers may not check for nil before dereferencing, risking runtime panics","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T23:14:33.414852-08:00","updated_at":"2025-12-20T16:20:04.064735-08:00","closed_at":"2025-12-20T16:20:04.064735-08:00","close_reason":"Not a bug. Load() returning nil is documented behavior. TestLoad_NoCache verifies this. Callers are expected to check for nil."}

internal/cli/status_cmd.go

Lines changed: 11 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,6 @@ import (
99
"github.com/spf13/cobra"
1010
)
1111

12-
// StatusType represents the type of status information to display.
13-
type StatusType string
14-
15-
const (
16-
StatusAll StatusType = "all"
17-
StatusBattery StatusType = "battery"
18-
StatusFuel StatusType = "fuel"
19-
StatusLocation StatusType = "location"
20-
StatusTires StatusType = "tires"
21-
StatusDoors StatusType = "doors"
22-
StatusWindows StatusType = "windows"
23-
StatusOdometer StatusType = "odometer"
24-
StatusHVAC StatusType = "hvac"
25-
)
26-
2712
// NewStatusCmd creates the status command.
2813
func NewStatusCmd() *cobra.Command {
2914
var jsonOutput bool
@@ -54,176 +39,23 @@ func NewStatusCmd() *cobra.Command {
5439
mcs status --json
5540
5641
# Request fresh status from vehicle (PHEV/EV only, waits up to 90 seconds)
57-
mcs status --refresh
58-
59-
# Show specific status category
60-
mcs status battery
61-
mcs status fuel
62-
mcs status location`,
42+
mcs status --refresh`,
6343
RunE: func(cmd *cobra.Command, args []string) error {
64-
return runStatus(cmd, jsonOutput, StatusAll, refresh, refreshWait)
44+
return runStatus(cmd, jsonOutput, refresh, refreshWait)
6545
},
6646
SilenceUsage: true,
6747
}
6848

69-
// Add persistent JSON flag
70-
statusCmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "output in JSON format")
71-
statusCmd.PersistentFlags().BoolVarP(&refresh, "refresh", "r", false, "request fresh status from vehicle (PHEV/EV only)")
72-
statusCmd.PersistentFlags().IntVar(&refreshWait, "refresh-wait", 90, "max seconds to wait for vehicle response")
73-
74-
// Add battery subcommand
75-
statusCmd.AddCommand(&cobra.Command{
76-
Use: "battery",
77-
Short: "Show battery status",
78-
Example: ` # Show battery status
79-
mcs status battery
80-
81-
# Example output:
82-
# BATTERY: 85% (42.5 km range) [plugged in, not charging]
83-
84-
# Show battery charging with time estimates
85-
# BATTERY: 65% (32.5 km range) [charging, ~2h 30m quick / ~4h AC]
86-
87-
# Show battery status in JSON format
88-
mcs status battery --json
89-
90-
# Example JSON output:
91-
# {
92-
# "battery_level": 85,
93-
# "range_km": 42.5,
94-
# "charge_time_ac_min": 0,
95-
# "charge_time_qbc_min": 0,
96-
# "plugged_in": true,
97-
# "charging": false,
98-
# "heater_on": false,
99-
# "heater_auto": true
100-
# }`,
101-
RunE: func(cmd *cobra.Command, args []string) error {
102-
return runStatus(cmd, jsonOutput, StatusBattery, refresh, refreshWait)
103-
},
104-
SilenceUsage: true,
105-
})
106-
107-
// Add fuel subcommand
108-
statusCmd.AddCommand(&cobra.Command{
109-
Use: "fuel",
110-
Short: "Show fuel status",
111-
Example: ` # Show fuel status
112-
mcs status fuel
113-
114-
# Example output:
115-
# FUEL: 75% (450.5 km range)
116-
117-
# Show fuel status in JSON format
118-
mcs status fuel --json
119-
120-
# Example JSON output:
121-
# {
122-
# "fuel_level": 75,
123-
# "range_km": 450.5
124-
# }`,
125-
RunE: func(cmd *cobra.Command, args []string) error {
126-
return runStatus(cmd, jsonOutput, StatusFuel, refresh, refreshWait)
127-
},
128-
SilenceUsage: true,
129-
})
130-
131-
// Add location subcommand
132-
statusCmd.AddCommand(&cobra.Command{
133-
Use: "location",
134-
Short: "Show vehicle location",
135-
Example: ` # Show vehicle location
136-
mcs status location
137-
138-
# Example output:
139-
# LOCATION: 37.774929, -122.419418
140-
# https://maps.google.com/?q=37.774929,-122.419418
141-
142-
# Show location in JSON format
143-
mcs status location --json
144-
145-
# Example JSON output:
146-
# {
147-
# "latitude": 37.774929,
148-
# "longitude": -122.419418,
149-
# "timestamp": "20240315143045"
150-
# }`,
151-
RunE: func(cmd *cobra.Command, args []string) error {
152-
return runStatus(cmd, jsonOutput, StatusLocation, refresh, refreshWait)
153-
},
154-
SilenceUsage: true,
155-
})
156-
157-
// Add tires subcommand
158-
statusCmd.AddCommand(&cobra.Command{
159-
Use: "tires",
160-
Short: "Show tire pressure",
161-
Example: ` # Show tire pressure (color-coded based on deviation from 36 PSI target)
162-
mcs status tires
163-
164-
# Example output:
165-
# TIRES: FL:35.0 FR:35.0 RL:33.0 RR:33.0 PSI
166-
#
167-
# Color coding: Green (±3 PSI), Yellow (4-6 PSI off), Red (>6 PSI off)
168-
169-
# Show tire pressure in JSON format
170-
mcs status tires --json
171-
172-
# Example JSON output:
173-
# {
174-
# "front_left_psi": 35.0,
175-
# "front_right_psi": 35.0,
176-
# "rear_left_psi": 33.0,
177-
# "rear_right_psi": 33.0
178-
# }`,
179-
RunE: func(cmd *cobra.Command, args []string) error {
180-
return runStatus(cmd, jsonOutput, StatusTires, refresh, refreshWait)
181-
},
182-
SilenceUsage: true,
183-
})
184-
185-
// Add doors subcommand
186-
statusCmd.AddCommand(&cobra.Command{
187-
Use: "doors",
188-
Short: "Show door lock status",
189-
Example: ` # Show door lock status
190-
mcs status doors
191-
192-
# Example output when all locked:
193-
# DOORS: All locked
194-
195-
# Example output with issues:
196-
# DOORS: Driver unlocked, Trunk open
197-
198-
# Show door status in JSON format
199-
mcs status doors --json
200-
201-
# Example JSON output:
202-
# {
203-
# "driver_open": false,
204-
# "driver_locked": true,
205-
# "passenger_open": false,
206-
# "passenger_locked": true,
207-
# "rear_left_open": false,
208-
# "rear_left_locked": true,
209-
# "rear_right_open": false,
210-
# "rear_right_locked": true,
211-
# "trunk_open": false,
212-
# "hood_open": false,
213-
# "fuel_lid_open": false,
214-
# "all_locked": true
215-
# }`,
216-
RunE: func(cmd *cobra.Command, args []string) error {
217-
return runStatus(cmd, jsonOutput, StatusDoors, refresh, refreshWait)
218-
},
219-
SilenceUsage: true,
220-
})
49+
// Add flags
50+
statusCmd.Flags().BoolVar(&jsonOutput, "json", false, "output in JSON format")
51+
statusCmd.Flags().BoolVarP(&refresh, "refresh", "r", false, "request fresh status from vehicle (PHEV/EV only)")
52+
statusCmd.Flags().IntVar(&refreshWait, "refresh-wait", 90, "max seconds to wait for vehicle response")
22153

22254
return statusCmd
22355
}
22456

22557
// runStatus executes the status command.
226-
func runStatus(cmd *cobra.Command, jsonOutput bool, statusType StatusType, refresh bool, refreshWait int) error {
58+
func runStatus(cmd *cobra.Command, jsonOutput bool, refresh bool, refreshWait int) error {
22759
return withVehicleClientEx(cmd.Context(), func(ctx context.Context, client *api.Client, vehicleInfo VehicleInfo) error {
22860
// Get initial EV status (needed for refresh comparison and final display)
22961
evStatus, err := client.GetEVVehicleStatus(ctx, string(vehicleInfo.InternalVIN))
@@ -245,10 +77,12 @@ func runStatus(cmd *cobra.Command, jsonOutput bool, statusType StatusType, refre
24577
return fmt.Errorf("failed to get vehicle status: %w", err)
24678
}
24779

248-
// Display status based on type
249-
if err := displayStatusWithVehicle(cmd, statusType, vehicleStatus, evStatus, vehicleInfo, jsonOutput); err != nil {
80+
// Display status
81+
output, err := displayAllStatus(vehicleStatus, evStatus, vehicleInfo, jsonOutput)
82+
if err != nil {
25083
return err
25184
}
85+
_, _ = fmt.Fprintln(cmd.OutOrStdout(), output)
25286

25387
return nil
25488
})
@@ -312,71 +146,3 @@ func refreshAndWaitForStatus(ctx context.Context, cmd *cobra.Command, client *ap
312146
}
313147
}
314148
}
315-
316-
// statusFormatter is a function that formats a specific status type.
317-
type statusFormatter func() (string, error)
318-
319-
// getStatusFormatter returns a formatter function for the given status type.
320-
func getStatusFormatter(statusType StatusType, vehicleStatus *api.VehicleStatusResponse, evStatus *api.EVVehicleStatusResponse, vehicleInfo VehicleInfo, jsonOutput bool) statusFormatter {
321-
formatters := map[StatusType]statusFormatter{
322-
StatusBattery: func() (string, error) {
323-
batteryInfo, _ := evStatus.GetBatteryInfo()
324-
325-
return formatBatteryStatus(batteryInfo, jsonOutput)
326-
},
327-
StatusFuel: func() (string, error) {
328-
fuelInfo, _ := vehicleStatus.GetFuelInfo()
329-
330-
return formatFuelStatus(fuelInfo, jsonOutput)
331-
},
332-
StatusLocation: func() (string, error) {
333-
locationInfo, _ := vehicleStatus.GetLocationInfo()
334-
335-
return formatLocationStatus(locationInfo, jsonOutput)
336-
},
337-
StatusTires: func() (string, error) {
338-
tireInfo, _ := vehicleStatus.GetTiresInfo()
339-
340-
return formatTiresStatus(tireInfo, jsonOutput)
341-
},
342-
StatusDoors: func() (string, error) {
343-
doorStatus, _ := vehicleStatus.GetDoorsInfo()
344-
345-
return formatDoorsStatus(doorStatus, jsonOutput)
346-
},
347-
StatusWindows: func() (string, error) {
348-
windowsInfo, _ := vehicleStatus.GetWindowsInfo()
349-
350-
return formatWindowsStatus(windowsInfo, jsonOutput)
351-
},
352-
StatusOdometer: func() (string, error) {
353-
odometerInfo, _ := vehicleStatus.GetOdometerInfo()
354-
355-
return formatOdometerStatus(odometerInfo, jsonOutput)
356-
},
357-
StatusHVAC: func() (string, error) {
358-
hvacInfo, _ := evStatus.GetHvacInfo()
359-
360-
return formatHvacStatus(hvacInfo, jsonOutput)
361-
},
362-
StatusAll: func() (string, error) {
363-
return displayAllStatus(vehicleStatus, evStatus, vehicleInfo, jsonOutput)
364-
},
365-
}
366-
367-
return formatters[statusType]
368-
}
369-
370-
// displayStatusWithVehicle outputs the status based on type, including vehicle info for "all".
371-
func displayStatusWithVehicle(cmd *cobra.Command, statusType StatusType, vehicleStatus *api.VehicleStatusResponse, evStatus *api.EVVehicleStatusResponse, vehicleInfo VehicleInfo, jsonOutput bool) error {
372-
formatter := getStatusFormatter(statusType, vehicleStatus, evStatus, vehicleInfo, jsonOutput)
373-
output, err := formatter()
374-
if err != nil {
375-
return err
376-
}
377-
378-
// Writing to stdout rarely fails, so we ignore the error here
379-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), output)
380-
381-
return nil
382-
}

0 commit comments

Comments
 (0)