// Init invokes a set of methods that will make the initial setup of the modem. func (p *DefaultProfile) Init(d *Device) (err error) { p.dev = d p.dev.Send(NoopCmd) // kinda flush if err = p.COPS(true, true); err != nil { return errors.New("at init: unable to adjust the format of operator's name") } var info *SystemInfoReport if info, err = p.SYSINFO(); err != nil { return errors.New("at init: unable to read system info") } p.dev.State = &DeviceState{ ServiceState: info.ServiceState, ServiceDomain: info.ServiceDomain, RoamingState: info.RoamingState, SystemMode: info.SystemMode, SystemSubmode: info.SystemSubmode, SimState: info.SimState, } if p.dev.State.OperatorName, err = p.OperatorName(); err != nil { return errors.New("at init: unable to read operator's name") } if p.dev.State.ModelName, err = p.ModelName(); err != nil { return errors.New("at init: unable to read modem's model name") } if p.dev.State.IMEI, err = p.IMEI(); err != nil { return errors.New("at init: unable to read modem's IMEI code") } if err = p.CMGF(false); err != nil { return errors.New("at init: unable to switch message format to PDU") } if err = p.CPMS(MemoryTypes.NvRAM, MemoryTypes.NvRAM, MemoryTypes.NvRAM); err != nil { return errors.New("at init: unable to set messages storage") } if err = p.CNMI(1, 1, 0, 0, 0); err != nil { return errors.New("at init: unable to turn on message notifications") } var octets map[uint64][]byte if octets, err = p.CMGL(MessageFlags.Any); err != nil { return errors.New("at init: unable to check message inbox") } for n, oct := range octets { var msg sms.Message if _, err := msg.ReadFrom(oct); err != nil { return errors.New("at init: error while parsing message inbox") } if err := p.CMGD(n, DeleteOptions.Index); err != nil { return errors.New("at init: error while cleaning message inbox") } d.messages <- &msg } return nil }
// SendLongSMS sends an SMS message with given text to the given address, // the encoding and other parameters are default. // If the message is very long, it will be sent in parts. func (d *Device) SendLongSMS(text string, address sms.PhoneNumber) (err error) { defer func() { multipartReferenceNumber = uint16((uint32(multipartReferenceNumber) + 1) & 0xFFFF) }() msg := sms.Message{ Text: text, Type: sms.MessageTypes.Submit, Encoding: sms.Encodings.Gsm7Bit, Address: address, VPFormat: sms.ValidityPeriodFormats.Relative, VP: sms.ValidityPeriod(24 * time.Hour * 4), } maxSize := 130 for _, w := range text { // detected a double-width char if w > 1 { msg.Encoding = sms.Encodings.UCS2 maxSize = 60 break } } msgParts := util.SplitStringBySize(text, maxSize) numberOfParts := byte(255) if len(msgParts) <= 255 { numberOfParts = byte(len(msgParts)) } msg.UserDataStartsWithHeader = numberOfParts > 1 for index, part := range msgParts { msg.MessageReference = messageReferenceCounter msg.Text = part if msg.Encoding == sms.Encodings.UCS2 { } if numberOfParts > 1 { msg.UserDataHeader.ConcatenationIE = sms.ConcatenationInformationElement{NumberOfParts: numberOfParts, PartNumber: byte(index + 1), ReferenceNumber: multipartReferenceNumber} } messageReferenceCounter = byte((uint16(messageReferenceCounter) + 1) & 0xFF) if index >= int(numberOfParts) { break } n, octets, err := msg.PDU() if err != nil { return err } if err = d.Commands.CMGS(n, octets); err != nil { return err } } return }
// SendSMS sends an SMS message with given text to the given address, // the encoding and other parameters are default. func (d *Device) SendSMS(text string, address sms.PhoneNumber) (err error) { msg := sms.Message{ Text: text, Type: sms.MessageTypes.Submit, Encoding: sms.Encodings.Gsm7Bit, Address: address, VPFormat: sms.ValidityPeriodFormats.Relative, VP: sms.ValidityPeriod(24 * time.Hour * 4), } for _, w := range text { // detected a double-width char if w > 1 { msg.Encoding = sms.Encodings.UCS2 break } } n, octets, err := msg.PDU() if err != nil { return err } err = d.Commands.CMGS(n, octets) return }
// handleReport detects and parses a report from the notification port represented // as a string. The parsed values may change the inner state or be sent over out channels. func (d *Device) handleReport(str string) (err error) { report := Reports.Resolve(str) str = strings.TrimSpace(strings.TrimPrefix(str, report.ID)) switch report { case Reports.Message: var report messageReport if err = report.Parse(str); err != nil { return } var octets []byte octets, err = d.Commands.CMGR(report.Index) if err != nil { return } if err = d.Commands.CMGD(report.Index, DeleteOptions.Index); err != nil { return } var msg sms.Message if _, err = msg.ReadFrom(octets); err != nil { return } d.messages <- &msg case Reports.Ussd: var ussd ussdReport if err = ussd.Parse(str); err != nil { return } var text string if ussd.Enc == Encodings.UCS2 { text, err = pdu.DecodeUcs2(ussd.Octets) if err != nil { return } } else if ussd.Enc == Encodings.Gsm7Bit { text, err = pdu.Decode7Bit(ussd.Octets) if err != nil { return } } else { return ErrUnknownEncoding } d.ussd <- Ussd(text) case Reports.SignalStrength: var rssi signalStrengthReport if err = rssi.Parse(str); err != nil { return } if d.State.SignalStrength != int(rssi) { d.State.SignalStrength = int(rssi) d.updated <- struct{}{} } case Reports.Mode: var report modeReport if err = report.Parse(str); err != nil { return } var updated bool if d.State.SystemMode != report.Mode { d.State.SystemMode = report.Mode updated = true } if d.State.SystemSubmode != report.Submode { d.State.SystemSubmode = report.Submode updated = true } if updated { d.updated <- struct{}{} } case Reports.ServiceState: var report serviceStateReport if err = report.Parse(str); err != nil { return } if d.State.ServiceState != Opt(report) { d.State.ServiceState = Opt(report) d.updated <- struct{}{} } case Reports.SimState: var report simStateReport if err = report.Parse(str); err != nil { return } if d.State.SimState != Opt(report) { d.State.SimState = Opt(report) d.updated <- struct{}{} } case Reports.BootHandshake: var token bootHandshakeReport if err = token.Parse(str); err != nil { return } if err = d.Commands.BOOT(uint64(token)); err != nil { return } case Reports.Stin: // ignore. what is this btw? default: switch FinalResults.Resolve(str) { case FinalResults.Noop, FinalResults.NotSupported, FinalResults.Timeout: // ignore default: return errors.New("at: unknown report: " + str) } } return }