Ended up with a little tray application example that grabs and displays Caller ID information
in a timed popup window that can be closed by clicking a button or pressing ESC.
Optionally it plays a sound for each RING on the line.
Cancel it by rightclicking on the icon in the tray and selecting Quit.
Demonstrates:
- Creating/Updating a preference file
- Making a tray application
- Using some of the SerialPort commands
Improve, expand, modify, and do with it what you wish but contribute back here please.
Code: Select all
; ------------------------------------------------------------
;
; PureBasic - SerialPort - Caller ID example
;
; a tray application by terryhough
;
; requires a Caller ID capable modem (many are not)
; ------------------------------------------------------------
Enumeration
#CallerID_Wdw
#CallerID_List
#CallerID_Close_Btn
#Font_Name
#Font_Numbr
#Font_Other
EndEnumeration
#SerialPort = 0
Global Msg$
Global Text$
Global *Buffer = AllocateMemory(1024)
Global DefaultSound.s : DefaultSound.s = "Y"
Global DefaultShow.s : DefaultShow.s = "Y"
Global DefaultVerb.s : DefaultVerb.s = "N"
Global Sound$
Global Verb$
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
Port$ = "COM4" ; Substitute the COM port of your CallerID compatible modem
CompilerEndIf
Procedure Show_CallerID(CID_Date.s, CID_Time.s, CID_Name.s, CID_Number.s)
ExamineDesktops()
PosX = DesktopWidth(0) - 320 - 10
PosY = DesktopHeight(0) - 140 - 34
hParent = OpenWindow(#CallerID_Wdw, PosX, PosY, 320, 140, "Caller ID",#PB_Window_TitleBar)
ButtonGadget(#CallerID_Close_Btn, 110, 110, 100, 20,"Close")
AddKeyboardShortcut(#CallerID_Wdw,#PB_Shortcut_Escape,18) ; add an ESC capture event
AddWindowTimer(#CallerID_Wdw, 123, 250)
LoadFont(#Font_Name, "Arial", 15 , #PB_Font_Bold)
LoadFont(#Font_Numbr, "Arial", 12 , #PB_Font_Bold)
LoadFont(#Font_Other, "Arial", 10 , #PB_Font_Bold)
If CreateImage(0, 300, 90)
If StartDrawing(ImageOutput(0))
DrawingMode(#PB_2DDrawing_Transparent)
RoundBox(1, 1, 298, 88, 18, 18, RGB(255,255,255))
DrawingFont(FontID(#Font_Name))
Width = TextWidth(CID_Name)
Left = (WindowWidth(#CallerID_Wdw) - Width) / 2
DrawText(Left,10,CID_Name, RGB(255,0,0))
DrawingFont(FontID(#Font_Numbr))
Width = TextWidth(CID_Number)
Left = (WindowWidth(#CallerID_Wdw) - Width) / 2
DrawText(Left, 45,CID_Number, RGB(0,0,255))
DrawingFont(FontID(#Font_Other))
Width = TextWidth(CID_Date) + TextWidth(" " ) + TextWidth(CID_Time)
Left = (WindowWidth(#CallerID_Wdw) - Width) / 2
DrawText(Left, 67,CID_Date + " " + CID_Time, RGB(0,0,0))
StopDrawing()
EndIf
; Create a gadget to display the image
ImageGadget(0, 10, 10, 300, 90, ImageID(0),#PB_Image_Border)
EndIf
While WindowEvent():Wend ; Give the window a chance to display
EndProcedure
Procedure RetrieveResponse()
Repeat
Result = AvailableSerialPortInput(#SerialPort) ; Check for available data in input buffer
If Result
Delay(50) ; Give modem a chance to respond
While Result
ReadSerialPortData(#SerialPort, *Buffer, Result) ; Read the data in the input buffer into memory
Text$ = PeekS(*Buffer, Result)
Msg$ + Text$
Result = AvailableSerialPortInput(#SerialPort)
Wend
Break
EndIf
Delay(500) ; Let system do other things
ForEver
EndProcedure
Procedure SetupModem()
Msg$ = "SerialPort opened succesfully" + #LF$
Msg$ + "Send AT command to turn on reset the modem" + #LF$
Result = WriteSerialPortString(#SerialPort, "ATZ" + Chr(13))
Msg$ + "Waiting for modem response" + #LF$
While AvailableSerialPortOutput(#SerialPort):Wend ; Wait for send buffer to empty
RetrieveResponse() ; Should get an OK response
;MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
Msg$ + "Send AT command to turn on Caller ID" + #LF$
Result = WriteSerialPortString(#SerialPort, "AT+VCID=1" + Chr(13))
; Usual AT command to activate CallerID sensing is AT+VCID=1
; Some modems require different AT commands; AT#CID=1 , AT#CC1, AT%CCID=1 or AT*ID1
While AvailableSerialPortOutput(#SerialPort):Wend ; Wait for send buffer to empty
RetrieveResponse()
If FindString(Text$,"OK",1)
Msg$ + "Modem ready to accept Caller ID information" + #LF$
SysTrayIconToolTip(1, "Modem ready to accept Caller ID information")
;MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
Show_CallerID("Modem ready. ", "Accepting Caller ID", "CallerID", "Example Program")
ProcedureReturn #True
Else
Msg$ + "Modem did not accept Caller ID turn on command" + #LF$
MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
ProcedureReturn #False
EndIf
EndProcedure
Procedure WaitForCallerID()
Msg$ = ""
Repeat
Result = AvailableSerialPortInput(#SerialPort) ; Check for available data in input buffer
If Result
While Result
ReadSerialPortData(#SerialPort, *Buffer, Result) ; Read the data in the input buffer into memory
Msg$ + PeekS(*Buffer, Result)
If FindString(Msg$,"RING", 1)
If Sound$ = "Y"
PlaySound(0,0)
EndIf
Msg$ = RemoveString(Msg$,"RING")
EndIf
;MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
If FindString(Msg$,"DATE",1)
Msg$ = Mid(Msg$,FindString(Msg$,"DATE",1))
Msg$ = RemoveString(Msg$, Chr(13), #PB_String_NoCase, FindString(Msg$,"NMBR",1))
Msg$ = RemoveString(Msg$, Chr(10), #PB_String_NoCase, FindString(Msg$,"NMBR",1))
;CID_ShowDate.s = InsertString(Trim(StringField(StringField(Msg$, 2, Chr(13)),2,"=")),"/",3) ; or just grab date/time from system
;CID_ShowTime.s = Trim(StringField(StringField(Msg$, 3, Chr(13)),2,"=")) ; or just grab date/time from system
DOW = DayOfWeek(Date)
Select DOW
Case 0
CID_ShowDOW.s = "SUN"
Case 1
CID_ShowDOW.s = "MON"
Case 2
CID_ShowDOW.s = "TUE"
Case 3
CID_ShowDOW.s = "WED"
Case 4
CID_ShowDOW.s = "THU"
Case 5
CID_ShowDOW.s = "FRI"
Case 6
CID_ShowDOW.s = "SAT"
EndSelect
CID_ShowDate.s = CID_ShowDOW.s + " " + FormatDate("%mm/%dd", Date())
Hour = Hour(Date())
If Hour < 12
AMPM.s = " AM"
ElseIf Hour = 12
AMPM.s = " PM"
Else
Hour - 12
AMPM.s = " PM"
EndIf
CID_ShowTime.s = RSet(Str(Hour),2) + ":" + FormatDate("%ii", Date()) + AMPM.s
CID_ShowName.s = Trim(StringField(StringField(Msg$, 3, Chr(13)),2,"="))
CID_ShowNumber.s = Trim(StringField(StringField(Msg$, 4, Chr(13)),2,"="))
CID_ShowNumber.s = InsertString(CID_ShowNumber.s,"(",1)
CID_ShowNumber.s = InsertString(CID_ShowNumber.s,") ",5)
CID_ShowNumber.s = InsertString(CID_ShowNumber.s," ",10)
; MessageRequester("DB",CID_ShowDate + #LF$ + CID_ShowTime + #LF$ + CID_ShowName + #LF$ + CID_ShowNumber,#MB_ICONINFORMATION)
Show_CallerID(CID_ShowDate.s, CID_ShowTime.s, CID_ShowName.s, CID_ShowNumber.s)
; Do other processing with the Caller ID here. Look up info in a database, store call history, etc.
Msg$ = ""
Break 2
EndIf
Result = AvailableSerialPortInput(#SerialPort)
Wend
Break
EndIf
ForEver
EndProcedure
Procedure ReCreateTheINIFile()
If FileSize("CallerID.INI") = -1
If CreatePreferences("CallerID.INI")
PreferenceGroup("CallerID")
WritePreferenceString("Sound" , DefaultSound)
PreferenceComment(" Sound is defaulted to Y for Yes, put N for no sound")
WritePreferenceString("Show" , DefaultShow)
PreferenceComment(" Display message is defaulted to Y for Yes, put N for no display")
WritePreferenceString("Verbose" , DefaultVerb)
PreferenceComment(" Display verbose replys is defaulted to N for No, put Y for verbose response")
PreferenceComment(" ")
PreferenceComment(" This is a default INI file created by CallerID.")
ClosePreferences()
EndIf
EndIf
EndProcedure
ReCreateTheINIFile() ; Make sure INI file exists.
ReReadPreferences:
If OpenPreferences("CallerID.INI")
PreferenceGroup("CallerID")
Sound$ = ReadPreferenceString("Sound" , "Y")
Show$ = ReadPreferenceString("Show" , "Y")
Verb$ = ReadPreferenceString("Verbose" , "N")
ClosePreferences()
Else
MessageRequester("Error","Unable to find or read the INI file.",#MB_ICONERROR)
EndIf
RereadFlag.w = 0
If Sound$ = ""
DefaultSound = InputRequester("Caller ID Ring Sound","Enable Sound? (Y/N)", "Y")
RereadFlag = 1
Else
DefaultSound.s = Sound$
EndIf
If Show$ = ""
DefaultShow = InputRequester("Caller ID Show Display","Enable Display? (Y/N)", "Y")
RereadFlag = 1
Else
DefaultShow.s = Show$
EndIf
If Verb$ = ""
DefaultShow = InputRequester("Caller ID Verbose","Enable Verbose? (Y/N)", "N")
RereadFlag = 1
Else
DefaultVerb.s = Verb$
EndIf
If RereadFlag = 1
DeleteFile("SERVER.INI") ; Delete the INI file
ReCreateTheINIFile() ; and go recreate it,
Goto ReReadPreferences ; then reread them.
EndIf
CatchImage(3, ?EmbeddedImage) ; catch the embedded image from memory
If Sound$ = "Y"
If InitSound()
CatchSound(0, ?EmbeddedSound) ; Catch the embedded sound from memory
EndIf
EndIf
If OpenWindow(1, 0, 0, 80, 80, "Caller ID Display", #PB_Window_Invisible | #PB_Window_ScreenCentered)
AddSysTrayIcon(1, WindowID(1), ImageID(3)) ; Add an icon to the System Tray --
SysTrayIconToolTip(1, "Display Caller ID information for incoming calls.")
If CreatePopupMenu(0) ; Creating the pop-up menu
OpenSubMenu("&Config")
MenuItem(5, "Toggle &Sound") ; Sound toggle on or off
MenuItem(6, "Toggle &Display") ; Display toggle on or off
MenuItem(7, "Toggle &Verbose") ; Verbose toggle on or off
CloseSubMenu()
MenuItem(9, "&About")
MenuBar()
MenuItem(4, "&Quit")
EndIf
SetMenuItemState(0, 5, 0) ; Show Sound status
If Sound$ = "Y"
SetMenuItemState(0, 5, 1)
EndIf
SetMenuItemState(0, 6, 0) ; Show Display status
If Show$ = "Y"
SetMenuItemState(0, 6, 1)
EndIf
SetMenuItemState(0, 7, 0) ; Show Verbose status
If Verb$ = "Y"
SetMenuItemState(0, 7, 1)
EndIf
If OpenSerialPort(#SerialPort, Port$, 14400, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
ttimer = 10
If SetupModem()
Repeat
Event = WaitWindowEvent(1000)
Select Event
Case #PB_Event_SysTray
If EventType() = #PB_EventType_RightClick
DisplayPopupMenu(0, WindowID(1))
EndIf
Case #PB_Event_Timer
If EventTimer() = 123
ttimer + 1
If ttimer = 20 ; Set to desired milliSeconds Delay/250
If IsWindow(#CallerID_Wdw)
CloseWindow(#CallerID_Wdw)
EndIf
ttimer = 0
EndIf
EndIf
Case #PB_Event_Menu
Select EventMenu() ; See which menu has been selected
Case 4 ; Quit
Quit = 1
Case 5 ; Toggle sound
If Sound$ = "Y"
SetMenuItemState(0, 5, 0)
Sound$ = "N"
Else
SetMenuItemState(0, 5, 1)
Sound$ = "Y"
If Sound$ = "Y"
If InitSound()
CatchSound(0, ?EmbeddedSound) ; Catch the embedded sound from memory
EndIf
EndIf
EndIf
DefaultSound = Sound$
DeleteFile("SERVER.INI")
ReCreateTheINIFile()
Case 6 ; Toggle Display
If Show$ = "Y"
SetMenuItemState(0, 6, 0)
Show$ = "N"
Else
SetMenuItemState(0, 6, 1)
Show$ = "Y"
EndIf
DefaultShow = Show$
DeleteFile("SERVER.INI")
ReCreateTheINIFile()
Case 7 ; Toggle Verbose
If Verb$ = "Y"
SetMenuItemState(0, 7, 0)
Verb$ = "N"
Else
SetMenuItemState(0, 7, 1)
Verb$ = "Y"
EndIf
DefaultVerb = Verb$
DeleteFile("SERVER.INI")
ReCreateTheINIFile()
Case 9 ; About
About$="Y"
Show_CallerID("", "", "CallerID", "Example Program")
Case 18 ; ESC key pressed
If IsWindow(#CallerID_Wdw)
CloseWindow(#CallerID_Wdw)
EndIf
ttimer = 0
EndSelect
Case #PB_Event_Gadget
If EventGadget() = #CallerID_Close_btn
If IsWindow(#CallerID_Wdw)
CloseWindow(#CallerID_Wdw)
EndIf
ttimer = 0
EndIf
Case #PB_Event_CloseWindow
If EventWindow() = #CallerID_Wdw
CloseWindow(#CallerID_Wdw)
EndIf
ttimer = 0
EndSelect
If AvailableSerialPortInput(#SerialPort) ; Check for available data in input buffer
WaitforCallerID()
EndIf
Until Quit = 1
EndIf ; End of Setup Modem
CloseSerialPort(#SerialPort)
Else
MessageRequester("Error", "Can't open the serial port: " + Port$)
EndIf
Else
MessageRequester("Error","The window cannot be opened!",#MB_ICONERROR)
EndIf
End
EmbeddedSound: IncludeBinary "ringout.wav" ; Embed the sound to play - substitute your sound file
EmbeddedImage: IncludeBinary "lighton.ico" ; Embed the image to show - substitute your image icon