Modem.psm1

$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
   Write-Host "Module Modem.psm1 removed on $(Get-Date)"
}
$MyInvocation.MyCommand.ScriptBlock.Module.AccessMode = "readonly"
[String]$result = '0'
[String]$name = 'minutes'
function Get-Modem {
<#
.SYNOPSIS
Display GPRS online activity from selected date.
.DESCRIPTION
The Get-Modem cmdlet scans the Internet Explorer Event log for events relating to GPRS online activity, specifically
events 5003 (Connect) and 5004 (Disconnect), generated via Huawei USBs; a Balloon Tip appearing in the Navigation
area when the SIM card has 5 days remaining before expiry.
 
It is necessary to install the Network Monitor Service (from the website below) as this writes an event 300 to the
Internet Explorer Event Log whenever offline status is detected with details of the data transmitted and Adapter in
use.
.EXAMPLE
    Get-Modem -After 23/10/2017 -Verbose
    Display GPRS online activity from selected date. Shows all online times since 23/10/2017
    with individual entries displayed. In the rare cases of Connect and Disconnect events not
    being logged in the IE Event Log, the former will be suffixed with '#' and the latter with
    '*' in any -Verbose display; these times therefore not being 100% accurate.
 
    PS D:\Scripts> Get-Modem -After 23/10/2017 -Verbose
    VERBOSE: Command activation - User [Peter] Computer [SEA3-W12GOLNKFB]
    VERBOSE: Network Monitor - Running
    Calculating total connect time of all GPRS modem devices...
    VERBOSE: Disconnect at: 25/10/2009 18:55:50. Online - 00:45:56 minutes
    VERBOSE: Connect at: 25/10/2009 18:09:54.
    VERBOSE: Disconnect at: 25/10/2009 13:52:55. Online - 00:30:32 minutes*
    VERBOSE: Connect at: 25/10/2009 13:22:23.
    VERBOSE: Disconnect at: 24/10/2009 19:19:56. Online - 00:33:53 minutes
    VERBOSE: Connect at: 24/10/2009 18:46:03.
    VERBOSE: Disconnect at: 24/10/2009 13:48:03. Online - 00:11:12 minutes
    VERBOSE: Connect at: 24/10/2009 13:36:51.
    VERBOSE: Disconnect at: 24/10/2009 13:30:56. Online - 00:20:08 minutes#
    VERBOSE: Connect at: 24/10/2009 13:10:48.
    Total online usage since Monday 23 October 2017 is 02:21:41 hours (7 Mb).
    VERBOSE: Data values: Download = 5.716 Mb, Upload = 1.175 Mb (Sessions = 5)
    VERBOSE: SIM card will expire in 28 days on Wednesday, 29 November 2017
.EXAMPLE
    Get-Modem -Today
    Lists entries for today only and also invokes the Verbose switch for more detail
    (NOTE: should both the -Today and -After parameters be used together, the former will
    always take preference).
 
    PS D:\Scripts> Get-Modem -Today
    VERBOSE: Command activation - User [Peter] Computer [SEA3-W12GOLNKFB]
    VERBOSE: Network Monitor - Running
    Calculating total connect time of all GPRS modem devices...
    VERBOSE: Disconnect at: 06/11/2009 20:43:22. Online - 00:25:47 minutes
    VERBOSE: Connect at: 06/11/2009 20:17:35.
    Total online usage for today Friday 06 November 2009 is 00:25:47 minutes (0 Mb).
    VERBOSE: Data values: Download = 0.000 Mb, Upload = 0.000 Mb (Sessions = 1)
    VERBOSE: SIM card will expire in 25 days on Wednesday, 29 November 2017
.EXAMPLE
    Get-Modem -Hours 2:20:00
    Add or subtract an extra amount of time to or from the total. For example, if the
    same SIM card has had time deducted when used on another computer. To subtract an
    amount of time just use a leading '-': Get-Modem -Hours -01:10:00 in this case.
 
    PS D:\Scripts> Get-Modem -Hours 2:20:00
    Calculating total connect time of all GPRS modem devices...
    Total online usage since Tuesday 03 November 2009 is 10:20:54 hours (7 Mb).
    (including 02:20:00 hours adjustment time)
 
.NOTES
Twenty five days after the date set with the -Month switch, a reminder message will appear in the
Notification Area and will reappear each day until the 30 days has expired. Additionally, whenever
the -Verbose switch is in use, this will also indicate the day when the SIM will expire and the
number of days until then (NOTE: these messages can always be suppressed by resetting the default
'-Limit' from 30 to a larger number).
 
If an -Adjust value is to be permanently added, an Alias function can be included to accomplish this:
function GPRS {
   Invoke-Expression "Get-Modem -Adjust 01:01:01 $args"
}
This must also be included in the Manifest FunctionsToExport = 'GPRS'
 
The -Device parameter may be used to select download/upload data from a different device; the default
being 'Adapter: Remote NDIS*'. Note the final '*' as a wildcard character.
.LINK
 Http://www.SeaStar.co.nf
#>

   [CmdletBinding()]
   param (
      [String]$eap = $errorActionPreference,
      [String]$next = '5004',   #We need a DISCONNECT event to start with. Otherwise ONLINE.
      [String]$_tag, 
      [String]$disconn,
      [String]$connect,
      [ValidateNotNullOrEmpty()]  
      [ValidateRange(30,100)]
      [Int]$limit = 30,
      [Alias("From")] 
      [ValidateNotNullOrEmpty()] 
      [String]$after = $env:dtac,
      [ValidateNotNullOrEmpty()]       
      [Parameter(position=0)]
      [String]$month = '00/00/0000',              #Format will be checked before use.
      [ValidateNotNullOrEmpty()]
      [Alias("Hours")]
      [String]$adjust ='00:00:00',                #Format will be checked before use.
      [Switch]$today,
      [Int]$count = 0,
      [Double]$download = 0.0,
      [Double]$upload   = 0.0,
      [ValidateNotNullOrEmpty()] 
      [String]$device = "Adapter: Remote NDIS*",         #Input must end with * here.
      [String]$pattern = "^.*(?<adapter>Adapter: .*\)).*= -?(?<down>(\d+\W\d+)).*= -?(?<up>(\d+\W\d+)).*$"
   )
   [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
   $GLOBAL:notifyIcon = New-Object System.Windows.Forms.NotifyIcon
   $GLOBAL:notifyIcon.Icon = [System.Drawing.SystemIcons]::Information 
   $GLOBAL:notifyIcon.BalloonTipIcon  = "Info"                 #Symbol for message.
   $GLOBAL:notifyIcon.BalloonTipTitle = "Get-Modem SIM information"

   function convert ([String]$dateUK) {
     $temp = $dateUK.Split("/ ")
     [DateTime]$new = $temp[1] + '/' + $temp[0] + '/' + $temp[2]
     [String]$weekday = "{0:dddd, }" -f [DateTime]$new
     [String]$after = $weekday + $new.ToLongDateString()
   }

   function AddTime ([String]$hhmmss) {
      $PRIVATE:PrivateData = $MyInvocation.MyCommand.Module.PrivateData
      [Int]$h = $MyInvocation.MyCommand.Module.PrivateData['h']
      [Int]$m = $MyInvocation.MyCommand.Module.PrivateData['m']
      [Int]$s = $MyInvocation.MyCommand.Module.PrivateData['s']
      if ($hhmmss -match '^\d+:[0-5][0-9]:[0-5][0-9]$') {
         $plus = $hhmmss.Split(':')
         $h+= $plus[0]
         $m+= $plus[1]
         $s+= $plus[2]
         if ($s -gt 59) {
            [Int]$min = $s/60
            [Int]$sec = $s%60
            $m+= $min
            $s = $sec
         }
         if ($m -gt 59) {
            [Int]$hour = $m/60
            [Int]$min  = $m%60
            $h+= $hour
            $m = $min
         }
         $MyInvocation.MyCommand.Module.PrivateData['h'] = $h
         $MyInvocation.MyCommand.Module.PrivateData['m'] = $m
         $MyInvocation.MyCommand.Module.PrivateData['s'] = $s
         $SCRIPT:result = "{0:D3}:{1:D2}:{2:D2}" -f $h, $m, $s
         $content = '^(-?)(0)(\d+:\d\d:\d\d)$'
         $SCRIPT:result = $result -replace $content, '$1$3'
      }
   }

   function SubtractTime ([String]$hhmmss) {
      $PRIVATE:PrivateData = $MyInvocation.MyCommand.Module.PrivateData
      [Int]$h = $MyInvocation.MyCommand.Module.PrivateData['h']
      [Int]$m = $MyInvocation.MyCommand.Module.PrivateData['m']
      [Int]$s = $MyInvocation.MyCommand.Module.PrivateData['s']
      if ($hhmmss -match '^\d+:[0-5][0-9]:[0-5][0-9]$') {
         $minus = $hhmmss.Split(':')
         if (($s - [Int]$minus[2]) -lt 0) {   
            $s+= (60 - [Int]$minus[2])
            [Int]$minus[1]+= 1
         } else {
            $s = $s - [Int]$minus[2]
         }
         if (($m - [Int]$minus[1]) -lt 0) { 
            $m+= (60 - [Int]$minus[1])
            [Int]$minus[0]+= 1
         } else {
            $m = $m - [Int]$minus[1]
         }
         $h = $h - $minus[0]
         $MyInvocation.MyCommand.Module.PrivateData['h'] = $h
         $MyInvocation.MyCommand.Module.PrivateData['m'] = $m
         $MyInvocation.MyCommand.Module.PrivateData['s'] = $s
         $SCRIPT:result = "{0:D3}:{1:D2}:{2:D2}" -f $h, $m, $s
         $content = '^(-?)(0)(\d+:\d\d:\d\d)$'
         $SCRIPT:result = $SCRIPT:result -replace $content, '$1$3'
       }
   }
   if ($month -ne '00/00/0000') {
      if ($month -match '^(0?[1-9]|1[0-9]|2[0-9]|3[01])/(0?[1-9]|1[0-2])/(201[78])$') {
         $now     = Get-Date                              #Will produce today's date.
         $runDate = (Get-Date $month).Date
         if ($runDate -le $now) {
            Write-Warning "[Get-Usb] Creating new default GPRS (Monthly) search date'"
            Write-Host "New default GPRS (Monthly) search date created for: $month"
            [Environment]::SetEnvironmentVariable("DTAC",$month,"User")
            #Everything valid so change the date and quit now.
         } else {
            Write-Warning "Monthly default date must be today or earlier."
         }
      } else {
         Write-Warning "Format '$month' is invalid. Use 'dd/mm/2017' only."
      }
      return
   }
   if ($today) {
      [String]$SCRIPT:result = 0
      $verbosePreference = 'Continue'
      $after = ([DateTime]::Now).ToShortDateString()
   }
   if ($after -notmatch '^(0?[1-9]|1[0-9]|2[0-9]|3[01])/(0?[1-9]|1[0-2])/(201[78])$') {
      if ($env:dtac -eq $null) {           #We will get this message on a first time run only.
         Write-Warning "Use the -Month option to create a valid start date."
      } else {
         Write-Warning "Format '$after' is invalid. Use 'dd/M/2017' only. Terminating..."
      }
      return
   }
   $ErrorActionPreference = 'Stop'
   try {
      $_network = (Get-Service -Name NetMonitor).status   #Will fail if service not installed.
   } catch {
      $_network = "Not installed"
   }
   $ErrorActionPreference = $eap  
   Write-Verbose "Command activation - User [$($env:USERNAME)] Computer [$($env:COMPUTERNAME)]"
   Write-Verbose "Network Monitor - $_network"
   Write-Host "Calculating total connect time of all GPRS modem devices..."
   $MyInvocation.MyCommand.Module.PrivateData['h'] = 0                        #Clear totals otherwise they are added again!
   $MyInvocation.MyCommand.Module.PrivateData['m'] = 0
   $MyInvocation.MyCommand.Module.PrivateData['s'] = 0
   $log = Get-EventLog -LogName 'Internet Explorer' -After $after 
   $log | Where-Object {($_.eventID -eq '5004') -or ($_.eventID -eq '5003') -or ($_.eventID -eq '300') } |
      ForEach-Object {
         $MyObject = $_
         switch ($_.EventID) {  
            '5004' {   #Check DISCONNECT events. We need earliest so ignore rest.
                if ($next -ne '5004') {       #Remove next line for latest event.
                   $disconn = $MyObject.TimeWritten.ToString()
                   $_tag = '*'                  
                   break
                }  
                $disconn = $MyObject.TimeWritten.ToString()
                $next = '5003'
            }
            '5003' {     #Check CONNECT events. We need the earliest one here.
                if ($next -ne '5003') {
                   $connect  = $myObject.TimeWritten.ToString()
                   $_tag = '#'                  
                   break  
                }
                $connect  = $myObject.TimeWritten.ToString()
                $subtract = New-TimeSpan $connect $disconn
                if ($subtract.TotalSeconds -gt 3599) {
                   $show = 'hours'
                } else {
                   $show = 'minutes'
                }
                AddTime $subtract
                $next = '5004'
                Write-Verbose "Disconnect at $disconn. Online - $subtract $show$_tag"
                Write-Verbose " Connect at $connect."
                $_tag = ''
                $count+= 1
            }
            '300'  {
                if (!$MyObject.Message.Contains(' WiFi ')) {
                   if (($MyObject.Message -match $pattern) -and ($matches.adapter -like $device)) {
                      $download+= $matches.down
                      $upload+=   $matches.up
                   } 
               }
            }
         }
      }
   if ((Get-UIculture).Name -eq 'en-US' -and (Get-Culture).Name -eq 'en-GB') {
      convert $after
   }
   [String]$sum = "{0:N0}" -f ($upload + $download)              #Format for output.
   if ($adjust -ne '00:00:00') {                     #An adjust value has been added.
      if ($adjust -match '^([-+]?[0-9]?[0-9]):([0-5][0-9]):00$') {
         $_tag = '+'
         if ($adjust.StartsWith('-')) {
            if ($adjust.replace('-','') -gt $result) {
               Write-Warning "Adjustment exceeds total usage. Please enter a lower value."
               $_tag = ''    
            }  else {
               SubtractTime $adjust.replace('-','')
            }
         } else {
             AddTime $adjust
         }
      } else {
         Write-Warning "Format '$adjust' is invalid. Use 'hh:mm:00' only. Terminating..."
         return
      }
   }
   [Int]$since = $MyInvocation.MyCommand.Module.PrivateData['h']  #Retrieve the total hours.
   if ($since -gt 0) {
      $name = 'hours'
   }
   Write-Host "Total online usage since $after is $result $name ($sum Mb)"
   if (($_tag -eq '+') -and ($today -eq $false)) {            #No need to show for -Today.
      Write-Host "(including $adjust hours adjustment time)"
   }
   Write-Verbose "Data values: Download = $download Mb, Upload = $upload Mb (Sessions = $count)"
   $output = New-Timespan (Get-Date $env:dtac).Date ([DateTime]::Today).Date
   $unit = ($limit-$output.Days)
   $expires = (Get-Date $env:dtac).AddDays($limit)
   $invalid = "$($expires.DayOfWeek), $($expires.ToLongDateString())"
   switch ($unit) {        
      {($_ -lt 30) -and ($_ -gt 5)} {
              Write-Verbose "SIM card will expire in $unit days on $invalid"
              break
      }
      {($_ -lt 6) -and ($_ -gt 1)} {   
              $GLOBAL:notifyIcon.Visible = $true 
              $GLOBAL:notifyIcon.BalloonTipText = "SIM card expires in $unit days on $($expires.ToLongDateString())."
              $GLOBAL:notifyIcon.ShowBalloonTip(500)
              Write-Verbose "SIM card will expire in $unit days on $invalid"
              break
      }
      {($_ -eq 1)} {
              $GLOBAL:notifyIcon.Visible = $true
              $GlOBAL:notifyIcon.Icon = [System.Drawing.SystemIcons]::Warning
              $GLOBAL:notifyIcon.BalloonTipIcon  = "Warning"   
              $GLOBAL:notifyIcon.BalloonTipText = "SIM card will expire tomorrow."
              $GLOBAL:notifyIcon.ShowBalloonTip(500)
              Write-Verbose "SIM card will expire tomorrow"
              break
      }
      {($_ -eq 0)} {
              $GLOBAL:notifyIcon.Visible = $true 
              $GLOBAL:notifyIcon.BalloonTipText = "SIM card will expire today."
              $GLOBAL:notifyIcon.ShowBalloonTip(500)
              Write-Verbose "SIM card will expire today"
              break
      }
      {$_ -lt 0} {
              $GLOBAL:notifyIcon.Visible = $true 
              $GLOBAL:notifyIcon.BalloonTipText = "SIM card expired on $invalid."
              $GLOBAL:notifyIcon.ShowBalloonTip(500)
              Write-Verbose "SIM card expired on $invalid."
              break
      } 
   }
   #Write-Verbose "SIM card will expire in $unit days on $invalid"
   if ($GLOBAL:notifyIcon.Visible) {
      Start-Sleep -Seconds 5
      $GLOBAL:notifyIcon.Visible = $false
      $GLOBAL:notifyIcon.Dispose()
   }
} #End Get-Modem

function gprs {       #Show all adapters. Use 'Adapter: dtac*' for dtac only.
   Invoke-Expression "Get-Modem -Adjust '01:06:00' -Device 'Adapter:*' $args"                     
}         #NOTE: Any extra $args need to be in quotes if they contain spaces.

New-Alias usb Get-Modem
Export-ModuleMember -Function Get-Modem, gprs -Alias usb