Files
powerpods/goTool/client_api.go
T
skrueckenandCursor 7f28a48a10 Add runtime UV protection toggle and treat near-zero sense as external power.
Per-pod NVS enable (default off) via UART/ESP-NOW/dashboard, and skip UV latch when LiPo ADC reads ~0 mV so USB/external supply still boots.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-01 22:51:17 +01:00

784 lines
23 KiB
Go

package main
import (
"fmt"
"time"
"google.golang.org/protobuf/proto"
"powerpod/gotool/pb"
)
func (m *managedSerial) getVersion() (*pb.VersionResponse, error) {
payload, err := m.exchange(byte(pb.MessageType_VERSION), "VERSION")
if err != nil {
return nil, err
}
return decodeVersionPayload(payload)
}
func (m *managedSerial) getVersionPoll() (*pb.VersionResponse, error) {
payload, err := m.exchangePoll(byte(pb.MessageType_VERSION), "VERSION")
if err != nil {
return nil, err
}
return decodeVersionPayload(payload)
}
func (m *managedSerial) listClients() ([]*pb.ClientInfo, error) {
payload, err := m.exchange(byte(pb.MessageType_CLIENT_INFO), "CLIENT_INFO")
if err != nil {
return nil, err
}
return decodeClientsPayload(payload)
}
func (m *managedSerial) listClientsPoll() ([]*pb.ClientInfo, error) {
payload, err := m.exchangePoll(byte(pb.MessageType_CLIENT_INFO), "CLIENT_INFO")
if err != nil {
return nil, err
}
return decodeClientsPayload(payload)
}
func decodeBatteryStatusPayload(payload []byte) (*pb.BatteryStatusResponse, error) {
if len(payload) < 2 {
return nil, fmt.Errorf("short battery response")
}
if payload[0] != byte(pb.MessageType_BATTERY_STATUS) {
return nil, fmt.Errorf("unexpected command id 0x%02x (want 0x%02x)",
payload[0], byte(pb.MessageType_BATTERY_STATUS))
}
var msg pb.UartMessage
if err := proto.Unmarshal(payload[1:], &msg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
if msg.GetType() != pb.MessageType_BATTERY_STATUS {
return nil, fmt.Errorf("unexpected type %v", msg.GetType())
}
r := msg.GetBatteryStatusResponse()
if r == nil {
return nil, fmt.Errorf("missing battery_status_response")
}
return r, nil
}
func (m *managedSerial) BatteryStatus(req *pb.BatteryStatusRequest) (*pb.BatteryStatusResponse, error) {
return m.batteryStatusExchange(false, req)
}
func (m *managedSerial) BatteryStatusPoll(req *pb.BatteryStatusRequest) (*pb.BatteryStatusResponse, error) {
return m.batteryStatusExchange(true, req)
}
func (m *managedSerial) batteryStatusExchange(poll bool, req *pb.BatteryStatusRequest) (*pb.BatteryStatusResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_BATTERY_STATUS,
Payload: &pb.UartMessage_BatteryStatusRequest{
BatteryStatusRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_BATTERY_STATUS)}, body...)
var respPayload []byte
if poll {
respPayload, err = m.exchangePayloadMode(true, payload, "BATTERY_STATUS", batteryReadTimeout)
} else {
respPayload, err = m.exchangePayloadMode(false, payload, "BATTERY_STATUS", batteryReadTimeout)
}
if err != nil {
return nil, err
}
return decodeBatteryStatusPayload(respPayload)
}
func (s *serialPort) batteryStatus(req *pb.BatteryStatusRequest) (*pb.BatteryStatusResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_BATTERY_STATUS,
Payload: &pb.UartMessage_BatteryStatusRequest{
BatteryStatusRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_BATTERY_STATUS)}, body...)
respPayload, err := s.exchangePayloadForBattery(payload, "BATTERY_STATUS")
if err != nil {
return nil, err
}
return decodeBatteryStatusPayload(respPayload)
}
func (m *managedSerial) AccelStream(req *pb.AccelStreamRequest) (*pb.AccelStreamResponse, error) {
return m.accelStreamExchange(false, req)
}
func (m *managedSerial) AccelStreamPoll(req *pb.AccelStreamRequest) (*pb.AccelStreamResponse, error) {
return m.accelStreamExchange(true, req)
}
// SetAccelStream enables or disables the ESP-NOW accel stream for one slave (master UART).
func (m *managedSerial) SetAccelStream(clientID uint32, enable bool) (*pb.AccelStreamResponse, error) {
return m.AccelStream(&pb.AccelStreamRequest{
Write: true,
Enable: enable,
ClientId: clientID,
})
}
// GetAccelStream returns whether the accel stream is enabled for a slave on the master.
func (m *managedSerial) GetAccelStream(clientID uint32) (bool, error) {
resp, err := m.AccelStreamPoll(&pb.AccelStreamRequest{
Write: false,
ClientId: clientID,
})
if err != nil {
return false, err
}
if !resp.GetSuccess() {
return false, fmt.Errorf("accel stream read failed for client %d", clientID)
}
return resp.GetEnabled(), nil
}
func (m *managedSerial) TapNotify(req *pb.TapNotifyRequest) (*pb.TapNotifyResponse, error) {
return m.tapNotifyExchange(false, req)
}
func (m *managedSerial) TapNotifyPoll(req *pb.TapNotifyRequest) (*pb.TapNotifyResponse, error) {
return m.tapNotifyExchange(true, req)
}
func (m *managedSerial) tapNotifyExchange(poll bool, req *pb.TapNotifyRequest) (*pb.TapNotifyResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_TAP_NOTIFY,
Payload: &pb.UartMessage_TapNotifyRequest{TapNotifyRequest: req},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_TAP_NOTIFY)}, body...)
var respPayload []byte
if poll {
respPayload, err = m.exchangePayloadPoll(payload, "TAP_NOTIFY")
} else {
respPayload, err = m.exchangePayload(payload, "TAP_NOTIFY")
}
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetTapNotifyResponse()
if r == nil {
return nil, fmt.Errorf("missing tap_notify_response")
}
return r, nil
}
func (m *managedSerial) readCacheStatusPoll() (*pb.CacheStatusResponse, error) {
payload, err := m.exchangePoll(byte(pb.MessageType_CACHE_STATUS), "CACHE_STATUS")
if err != nil {
return nil, err
}
return decodeCacheStatusPayload(payload)
}
func decodeCacheStatusPayload(payload []byte) (*pb.CacheStatusResponse, error) {
if len(payload) < 1 {
return nil, fmt.Errorf("empty response payload")
}
if payload[0] != byte(pb.MessageType_CACHE_STATUS) {
return nil, fmt.Errorf("unexpected command id 0x%02x (want 0x%02x)",
payload[0], byte(pb.MessageType_CACHE_STATUS))
}
var msg pb.UartMessage
if err := proto.Unmarshal(payload[1:], &msg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
if msg.GetType() != pb.MessageType_CACHE_STATUS {
return nil, fmt.Errorf("unexpected type %v", msg.GetType())
}
r := msg.GetCacheStatusResponse()
if r == nil {
return nil, fmt.Errorf("missing cache_status_response")
}
return r, nil
}
func (m *managedSerial) accelStreamExchange(poll bool, req *pb.AccelStreamRequest) (*pb.AccelStreamResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_ACCEL_STREAM,
Payload: &pb.UartMessage_AccelStreamRequest{AccelStreamRequest: req},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_ACCEL_STREAM)}, body...)
var respPayload []byte
if poll {
respPayload, err = m.exchangePayloadPoll(payload, "ACCEL_STREAM")
} else {
respPayload, err = m.exchangePayload(payload, "ACCEL_STREAM")
}
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetAccelStreamResponse()
if r == nil {
return nil, fmt.Errorf("missing accel_stream_response")
}
return r, nil
}
func (m *managedSerial) AccelDeadzone(req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error) {
return m.accelDeadzoneExchange(false, req)
}
func (m *managedSerial) AccelDeadzonePoll(req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error) {
return m.accelDeadzoneExchange(true, req)
}
func (m *managedSerial) TapConfig(req *pb.TapConfigRequest) (*pb.TapConfigResponse, error) {
return m.tapConfigExchange(false, req)
}
func (m *managedSerial) TapConfigPoll(req *pb.TapConfigRequest) (*pb.TapConfigResponse, error) {
return m.tapConfigExchange(true, req)
}
func (m *managedSerial) tapConfigExchange(poll bool, req *pb.TapConfigRequest) (*pb.TapConfigResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_TAP_CONFIG,
Payload: &pb.UartMessage_TapConfigRequest{TapConfigRequest: req},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_TAP_CONFIG)}, body...)
var respPayload []byte
if poll {
respPayload, err = m.exchangePayloadPoll(payload, "TAP_CONFIG")
} else {
respPayload, err = m.exchangePayload(payload, "TAP_CONFIG")
}
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetTapConfigResponse()
if r == nil {
return nil, fmt.Errorf("missing tap_config_response")
}
return r, nil
}
func (m *managedSerial) BatteryUv(req *pb.BatteryUvRequest) (*pb.BatteryUvResponse, error) {
return m.batteryUvExchange(false, req)
}
func (m *managedSerial) BatteryUvPoll(req *pb.BatteryUvRequest) (*pb.BatteryUvResponse, error) {
return m.batteryUvExchange(true, req)
}
func (m *managedSerial) batteryUvExchange(poll bool, req *pb.BatteryUvRequest) (*pb.BatteryUvResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_BATTERY_UV,
Payload: &pb.UartMessage_BatteryUvRequest{BatteryUvRequest: req},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_BATTERY_UV)}, body...)
var respPayload []byte
if poll {
respPayload, err = m.exchangePayloadPoll(payload, "BATTERY_UV")
} else {
respPayload, err = m.exchangePayload(payload, "BATTERY_UV")
}
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetBatteryUvResponse()
if r == nil {
return nil, fmt.Errorf("missing battery_uv_response")
}
return r, nil
}
func (m *managedSerial) accelDeadzoneExchange(poll bool, req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_ACCEL_DEADZONE,
Payload: &pb.UartMessage_AccelDeadzoneRequest{AccelDeadzoneRequest: req},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_ACCEL_DEADZONE)}, body...)
var respPayload []byte
if poll {
respPayload, err = m.exchangePayloadPoll(payload, "ACCEL_DEADZONE")
} else {
respPayload, err = m.exchangePayload(payload, "ACCEL_DEADZONE")
}
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetAccelDeadzoneResponse()
if r == nil {
return nil, fmt.Errorf("missing accel_deadzone_response")
}
return r, nil
}
func (m *managedSerial) EspnowUnicastTest(clientID, seq uint32) (*pb.EspNowUnicastTestResponse, error) {
req := &pb.EspNowUnicastTestRequest{ClientId: clientID, Seq: seq}
msg := &pb.UartMessage{
Type: pb.MessageType_ESPNOW_UNICAST_TEST,
Payload: &pb.UartMessage_EspnowUnicastTestRequest{
EspnowUnicastTestRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_ESPNOW_UNICAST_TEST)}, body...)
respPayload, err := m.exchangePayload(payload, "ESPNOW_UNICAST_TEST")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetEspnowUnicastTestResponse()
if r == nil {
return nil, fmt.Errorf("missing espnow_unicast_test_response")
}
return r, nil
}
func decodeVersionPayload(payload []byte) (*pb.VersionResponse, error) {
var msg pb.UartMessage
if err := proto.Unmarshal(payload[1:], &msg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
if msg.GetType() != pb.MessageType_VERSION {
return nil, fmt.Errorf("unexpected type %v", msg.GetType())
}
ver := msg.GetVersionResponse()
if ver == nil {
return nil, fmt.Errorf("missing version_response")
}
return ver, nil
}
func decodeClientsPayload(payload []byte) ([]*pb.ClientInfo, error) {
var msg pb.UartMessage
if err := proto.Unmarshal(payload[1:], &msg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
if msg.GetType() != pb.MessageType_CLIENT_INFO {
return nil, fmt.Errorf("unexpected type %v", msg.GetType())
}
info := msg.GetClientInfoResponse()
if info == nil {
return nil, fmt.Errorf("missing client_info_response")
}
return info.GetClients(), nil
}
func (s *serialPort) getVersion() (*pb.VersionResponse, error) {
payload, err := s.exchange(byte(pb.MessageType_VERSION), "VERSION")
if err != nil {
return nil, err
}
return decodeVersionPayload(payload)
}
func (s *serialPort) listClients() ([]*pb.ClientInfo, error) {
payload, err := s.exchange(byte(pb.MessageType_CLIENT_INFO), "CLIENT_INFO")
if err != nil {
return nil, err
}
return decodeClientsPayload(payload)
}
func (s *serialPort) AccelStream(req *pb.AccelStreamRequest) (*pb.AccelStreamResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_ACCEL_STREAM,
Payload: &pb.UartMessage_AccelStreamRequest{
AccelStreamRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_ACCEL_STREAM)}, body...)
respPayload, err := s.exchangePayload(payload, "ACCEL_STREAM")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetAccelStreamResponse()
if r == nil {
return nil, fmt.Errorf("missing accel_stream_response")
}
return r, nil
}
func (s *serialPort) TapNotify(req *pb.TapNotifyRequest) (*pb.TapNotifyResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_TAP_NOTIFY,
Payload: &pb.UartMessage_TapNotifyRequest{
TapNotifyRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_TAP_NOTIFY)}, body...)
respPayload, err := s.exchangePayload(payload, "TAP_NOTIFY")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetTapNotifyResponse()
if r == nil {
return nil, fmt.Errorf("missing tap_notify_response")
}
return r, nil
}
func (s *serialPort) readCacheStatus() (*pb.CacheStatusResponse, error) {
payload, err := s.exchange(byte(pb.MessageType_CACHE_STATUS), "CACHE_STATUS")
if err != nil {
return nil, err
}
return decodeCacheStatusPayload(payload)
}
func (s *serialPort) accelDeadzone(req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_ACCEL_DEADZONE,
Payload: &pb.UartMessage_AccelDeadzoneRequest{
AccelDeadzoneRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_ACCEL_DEADZONE)}, body...)
respPayload, err := s.exchangePayload(payload, "ACCEL_DEADZONE")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetAccelDeadzoneResponse()
if r == nil {
return nil, fmt.Errorf("missing accel_deadzone_response")
}
return r, nil
}
func (s *serialPort) tapConfig(req *pb.TapConfigRequest) (*pb.TapConfigResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_TAP_CONFIG,
Payload: &pb.UartMessage_TapConfigRequest{
TapConfigRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_TAP_CONFIG)}, body...)
respPayload, err := s.exchangePayload(payload, "TAP_CONFIG")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetTapConfigResponse()
if r == nil {
return nil, fmt.Errorf("missing tap_config_response")
}
return r, nil
}
func (s *serialPort) batteryUv(req *pb.BatteryUvRequest) (*pb.BatteryUvResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_BATTERY_UV,
Payload: &pb.UartMessage_BatteryUvRequest{
BatteryUvRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_BATTERY_UV)}, body...)
respPayload, err := s.exchangePayload(payload, "BATTERY_UV")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetBatteryUvResponse()
if r == nil {
return nil, fmt.Errorf("missing battery_uv_response")
}
return r, nil
}
func (s *serialPort) espnowUnicastTest(clientID, seq uint32) (*pb.EspNowUnicastTestResponse, error) {
req := &pb.EspNowUnicastTestRequest{ClientId: clientID, Seq: seq}
msg := &pb.UartMessage{
Type: pb.MessageType_ESPNOW_UNICAST_TEST,
Payload: &pb.UartMessage_EspnowUnicastTestRequest{
EspnowUnicastTestRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_ESPNOW_UNICAST_TEST)}, body...)
respPayload, err := s.exchangePayload(payload, "ESPNOW_UNICAST_TEST")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetEspnowUnicastTestResponse()
if r == nil {
return nil, fmt.Errorf("missing espnow_unicast_test_response")
}
return r, nil
}
// EchoPingResult is the host-side round-trip for ESP-NOW echo ping.
type EchoPingResult struct {
Success bool `json:"success"`
ClientID uint32 `json:"client_id"`
TimestampUs uint64 `json:"timestamp_us"`
RttMs float64 `json:"rtt_ms"` // goTool: full UART round-trip
EspRttUs uint32 `json:"esp_rtt_us"` // master: µs delta ping send → pong recv
}
func (s *serialPort) echoPing(clientID uint32) (*EchoPingResult, error) {
t0 := time.Now()
timestampUs := uint64(t0.UnixMicro())
req := &pb.EspNowEchoPingRequest{
ClientId: clientID,
TimestampUs: timestampUs,
}
msg := &pb.UartMessage{
Type: pb.MessageType_ESPNOW_ECHO_PING,
Payload: &pb.UartMessage_EspnowEchoPingRequest{
EspnowEchoPingRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_ESPNOW_ECHO_PING)}, body...)
respPayload, err := s.exchangePayload(payload, "ESPNOW_ECHO_PING")
if err != nil {
return nil, err
}
rttMs := float64(time.Since(t0).Microseconds()) / 1000.0
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetEspnowEchoPingResponse()
if r == nil {
return nil, fmt.Errorf("missing espnow_echo_ping_response")
}
if !r.GetSuccess() {
return &EchoPingResult{
Success: false,
ClientID: r.GetClientId(),
RttMs: rttMs,
}, nil
}
if r.GetTimestampUs() != timestampUs {
return nil, fmt.Errorf("timestamp mismatch: sent %d got %d", timestampUs, r.GetTimestampUs())
}
return &EchoPingResult{
Success: true,
ClientID: r.GetClientId(),
TimestampUs: r.GetTimestampUs(),
RttMs: rttMs,
EspRttUs: r.GetEspRttUs(),
}, nil
}
func (m *managedSerial) FindMe(clientID uint32) error {
return hostFindMe(m, clientID)
}
func (m *managedSerial) SetLogLevel(write bool, level uint32) (*pb.SetLogLevelResponse, error) {
return hostSetLogLevel(m, write, level)
}
func (m *managedSerial) InputSendMode(req *pb.InputSendModeRequest) (*pb.InputSendModeResponse, error) {
return hostInputSendMode(m, req)
}
func (m *managedSerial) Restart(clientID uint32) error {
if err := hostRestart(m, clientID); err != nil {
return err
}
if clientID == 0 {
m.recoverAfterMasterRestart()
}
return nil
}
func (s *serialPort) ledRingProgress(req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_LED_RING,
Payload: &pb.UartMessage_LedRingProgressRequest{
LedRingProgressRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_LED_RING)}, body...)
respPayload, err := s.exchangePayload(payload, "LED_RING")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetLedRingProgressResponse()
if r == nil {
return nil, fmt.Errorf("missing led_ring_progress_response")
}
return r, nil
}
func (s *serialPort) GetVersion() (*pb.VersionResponse, error) { return s.getVersion() }
func (s *serialPort) ListClients() ([]*pb.ClientInfo, error) { return s.listClients() }
func (s *serialPort) SetAccelStream(clientID uint32, enable bool) (*pb.AccelStreamResponse, error) {
return s.AccelStream(&pb.AccelStreamRequest{
Write: true,
Enable: enable,
ClientId: clientID,
})
}
func (s *serialPort) GetAccelStream(clientID uint32) (bool, error) {
resp, err := s.AccelStream(&pb.AccelStreamRequest{
Write: false,
ClientId: clientID,
})
if err != nil {
return false, err
}
if !resp.GetSuccess() {
return false, fmt.Errorf("accel stream read failed for client %d", clientID)
}
return resp.GetEnabled(), nil
}
func (s *serialPort) AccelDeadzone(req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error) {
return s.accelDeadzone(req)
}
func (s *serialPort) TapConfig(req *pb.TapConfigRequest) (*pb.TapConfigResponse, error) {
return s.tapConfig(req)
}
func (s *serialPort) BatteryUv(req *pb.BatteryUvRequest) (*pb.BatteryUvResponse, error) {
return s.batteryUv(req)
}
func (s *serialPort) EspnowUnicastTest(clientID, seq uint32) (*pb.EspNowUnicastTestResponse, error) {
return s.espnowUnicastTest(clientID, seq)
}
func (s *serialPort) EchoPing(clientID uint32) (*EchoPingResult, error) {
return s.echoPing(clientID)
}
func (m *managedSerial) EchoPing(clientID uint32) (*EchoPingResult, error) {
return hostEchoPing(m, clientID)
}
func (s *serialPort) LedRing(req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error) {
return s.ledRingProgress(req)
}
func (m *managedSerial) LedRing(req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error) {
return hostLedRing(m, req)
}
func (s *serialPort) FindMe(clientID uint32) (*pb.EspNowFindMeResponse, error) {
return s.espnowFindMe(clientID)
}
func (s *serialPort) Restart(clientID uint32) (*pb.RestartResponse, error) {
return s.restart(clientID)
}
func (s *serialPort) OtaSlaveProgress(clientID uint32) (*pb.OtaSlaveProgressResponse, error) {
return QueryOtaSlaveProgress(s, clientID)
}