mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-22 15:53:43 +08:00
fix(vowifi): enable IMS SMS submission on OpenStick 410 (#83)
Co-authored-by: MengMengCode <[email protected]>
This commit is contained in:
@@ -35,6 +35,10 @@ func (mapper nativeQMIControllerMapper) ReadSIMMetadata(ctx context.Context, id
|
||||
return mapper.Mapper.ReadSIMMetadata(ctx, id)
|
||||
}
|
||||
|
||||
func (mapper nativeQMIControllerMapper) ReadSMSCenter(ctx context.Context, id string) (string, error) {
|
||||
return mapper.Mapper.ReadSMSCenter(ctx, id)
|
||||
}
|
||||
|
||||
func (mapper nativeQMIControllerMapper) ProbeNativeQMIApplication(ctx context.Context, id, preference string) ([]byte, string, error) {
|
||||
physical, err := mapper.physical(id)
|
||||
if err != nil {
|
||||
|
||||
@@ -242,10 +242,6 @@ func (s *Server) handleSMSSend(w http.ResponseWriter, r *http.Request) {
|
||||
s.writeStoreError(w, err)
|
||||
return
|
||||
}
|
||||
if store.NormalizeDeviceType(config.DeviceType) == store.DeviceTypeWiFi410 {
|
||||
writeError(w, http.StatusNotImplemented, "device_feature_unsupported", "SMS is not supported by the native OpenStick 410 backend")
|
||||
return
|
||||
}
|
||||
entry, physicalID, present := s.physicalForConfig(config)
|
||||
if !s.requirePhysicalDevice(w, present) {
|
||||
return
|
||||
@@ -291,6 +287,13 @@ func (s *Server) handleSMSSend(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Native OpenStick 410 supports SMS only through an established VoWiFi IMS
|
||||
// session. Keep cellular AT+CMGS disabled until that modem path is separately
|
||||
// implemented and validated; never silently fall back from IMS to cellular.
|
||||
if store.NormalizeDeviceType(config.DeviceType) == store.DeviceTypeWiFi410 {
|
||||
writeError(w, http.StatusConflict, "ims_sms_not_ready", "OpenStick 410 requires a ready VoWiFi IMS SMS session")
|
||||
return
|
||||
}
|
||||
result, sendErr := s.devices.SendSMS(
|
||||
r.Context(),
|
||||
physicalID,
|
||||
|
||||
@@ -3,6 +3,7 @@ package integration
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"vocat/internal/device"
|
||||
@@ -74,6 +75,43 @@ func (mapper ATMapper) ExecuteSensitiveAT(
|
||||
return mapper.Devices.ExecuteSensitiveAT(ctx, physicalID, command)
|
||||
}
|
||||
|
||||
// ReadSMSCenter reads the SIM-provisioned service-centre address through the
|
||||
// modem's read-only AT interface. Native QMI devices still expose a companion
|
||||
// AT port, and SMS-over-IMS needs this value to address RP-DATA submissions.
|
||||
func (mapper ATMapper) ReadSMSCenter(ctx context.Context, configuredID string) (string, error) {
|
||||
response, err := mapper.ExecuteAT(ctx, configuredID, "AT+CSCA?")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read SMS service centre: %w", err)
|
||||
}
|
||||
for _, line := range response.Lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if !strings.HasPrefix(line, "+CSCA:") {
|
||||
continue
|
||||
}
|
||||
value := strings.TrimSpace(strings.TrimPrefix(line, "+CSCA:"))
|
||||
if comma := strings.IndexByte(value, ','); comma >= 0 {
|
||||
value = value[:comma]
|
||||
}
|
||||
value = strings.Trim(strings.TrimSpace(value), `"`)
|
||||
digits := strings.TrimPrefix(value, "+")
|
||||
if len(digits) < 3 || len(digits) > 20 {
|
||||
break
|
||||
}
|
||||
valid := true
|
||||
for _, digit := range digits {
|
||||
if digit < '0' || digit > '9' {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if valid {
|
||||
return value, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
return "", errors.New("modem returned no valid SMS service-centre address")
|
||||
}
|
||||
|
||||
// ReadSIMMetadata reuses the device manager's per-ICCID EF cache. VoWiFi
|
||||
// identity discovery therefore gains Android-style SPN/GID MVNO selectors
|
||||
// without issuing duplicate APDUs on every reconnect.
|
||||
|
||||
@@ -37,6 +37,7 @@ type nativeQMIBinding struct {
|
||||
|
||||
var _ SIMIdentityReader = (*NativeQMIAdapter)(nil)
|
||||
var _ PreferredAKAProvider = (*NativeQMIAdapter)(nil)
|
||||
var _ SMSCenterReader = (*NativeQMIAdapter)(nil)
|
||||
var _ RadioController = (*NativeQMIAdapter)(nil)
|
||||
|
||||
func NewNativeQMIAdapter(controller NativeQMIController, purePolicy func(string) bool) (*NativeQMIAdapter, error) {
|
||||
@@ -73,6 +74,14 @@ func (adapter *NativeQMIAdapter) ReadIdentity(ctx context.Context, deviceID stri
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
func (adapter *NativeQMIAdapter) ReadSMSCenter(ctx context.Context, deviceID string) (string, error) {
|
||||
reader, ok := adapter.controller.(SMSCenterReader)
|
||||
if !ok {
|
||||
return "", errors.New("vocat: native QMI controller does not expose an SMS service-centre reader")
|
||||
}
|
||||
return reader.ReadSMSCenter(ctx, deviceID)
|
||||
}
|
||||
|
||||
func (adapter *NativeQMIAdapter) binding(identity SIMIdentity) (nativeQMIBinding, error) {
|
||||
adapter.mu.Lock()
|
||||
binding, ok := adapter.bindings[strings.TrimSpace(identity.ICCID)]
|
||||
|
||||
@@ -60,12 +60,10 @@ function showSmsSendOutcome(result: SmsSendResult) {
|
||||
message.success("短信已确认送达");
|
||||
return;
|
||||
case "accepted_unconfirmed":
|
||||
message.info(
|
||||
result.transport === "ims"
|
||||
? "IMS 已接受短信提交,但尚未收到收件人送达确认"
|
||||
: "模块已接受短信提交,但尚未收到运营商送达确认",
|
||||
5000,
|
||||
);
|
||||
// Keep the user-facing result identical across cellular AT (EC20) and
|
||||
// VoWiFi IMS (410). The transport remains available in the API response
|
||||
// for diagnostics, but it must not change the meaning shown to users.
|
||||
message.info("模块已接受短信提交,但尚未收到运营商送达确认", 5000);
|
||||
return;
|
||||
case "partial":
|
||||
message.warning(`短信仅有 ${accepted}/${total} 段被接受,不能判定为发送成功`, 5000);
|
||||
|
||||
Reference in New Issue
Block a user