Imports System.Net, System.Web, System.IO, System.Text, System.Data.SQLite
Imports System.Threading, System.Convert
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Configuration
Imports tenGlobalMethods
Imports System.Net.WebRequestMethods
Imports OpenPop.Pop3
Imports System.Net.Mail
Imports System.Diagnostics
Imports System.Runtime.CompilerServices
Imports HomeSeer.PluginSdk.Devices


Class GetCallerId
    Public Sub Main(ByVal Parms As Object)
        ''
        '' Script to test OpenCNAM processing
        ''
        ''  Specify the 10-digit phone number as the parameter 
        ''
        Dim ParmArray() As String
        '' Get test script parameter  (Phone Number)
        ParmArray = Parms.ToString.Split(ToChar("|"))
        '' Get Name from OpenCNAM and write to HS Log
        hs.WriteLog("CallerId Test", "P=" & ParmArray(0).ToString & "  N=" & GetFromOpenCNAM(ParmArray(0)))
    End Sub
    Public Function GetFromOpenCNAM(PhoneNumber As String) As String
        ''
        '' GetFromOpenCNAM     
        ''
        ''   Input:  10 Digit Phone Number (string)
        ''   Returns: Associated Name  (or blank, if no data or request fails)
        ''
        ''      First, a request is made to OpenCNAM without authorization codes   (no charge)
        ''      If no data retrieved, a second request is made using authorization codes   ($0.004 per request)
        ''
        Dim Result As String
        '' Validate parm is 10 digits (more error checking needed here)
        If PhoneNumber.Length <> 10 Then
            hs.WriteLog("GetFromOpenCNAM", "Supplied parameter not 10 digits exactly: " & PhoneNumber.ToString)
            Return ""
        End If
        '' Try without authorization codes
        Result = SendOpenCNAM(PhoneNumber, False)
        '' If failed, try with authorization codes
        If Result = "" Then Result = SendOpenCNAM(PhoneNumber, True)
        Return Result
    End Function
    Function SendOpenCNAM(ByVal sPhoneNumber As String, UseAuthentication As Boolean) As String
        ''
        '' SendOpenCNAM
        ''
        ''      Input:  Phone Number
        ''              Boolean to specify if authorization is to be used
        ''
        ''      Returns:  Associated Name (or blank if not fournd)
        ''
        Dim strData As String = ""
        Dim tlRequest As System.Net.HttpWebRequest
        Dim tlResponse As System.Net.HttpWebResponse
        Dim tlStatusCode As Integer
        Dim BaseUrl As String = " https://api.opencnam.com/v2/phone/"
        Dim AuthParms As String = "?account_sid=ACe9dcc6d238284f828bb0c38ec31d4c4a&auth_token=AUd2273d282897411db7b6e84e5e62b422"
        Dim sUrl As String
        sUrl = BaseUrl & sPhoneNumber
        If UseAuthentication Then
            sUrl = sUrl & AuthParms
        End If
        Try
            tlRequest = CType(System.Net.WebRequest.Create(sUrl), System.Net.HttpWebRequest)
            tlRequest.ContentType = "text/html"
            tlRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"
            tlRequest.Method = "GET"
            tlRequest.Timeout = 1000
            tlRequest.AllowAutoRedirect = True
            tlResponse = CType(tlRequest.GetResponse(), System.Net.HttpWebResponse)
            tlStatusCode = tlResponse.StatusCode
            If tlStatusCode = System.Net.HttpStatusCode.OK Then
                strData = New System.IO.StreamReader(tlResponse.GetResponseStream()).ReadToEnd()
            Else
                Return ""
                hs.WriteLog("SendOpenCNAM", "HttpWebRequest Returned: " & tlStatusCode.ToString)
            End If
            tlResponse.Close()
        Catch ex As System.Net.WebException
            If ex.Message.Contains("(404)") Then
                '' 404   No Data for Phone Number, return blank
                Return ""
            End If
            If ex.Message.Contains("(401)") Then
                '' 401   Not Authorized, account codes invalid
                hs.WriteLog("SendOpenCNAM", "Authorization Failed, URL: " & sUrl)
                Return ""
            End If
            If ex.Message.Contains("(400)") Then
                '' 400   Phone Number Provided is Invalid
                hs.WriteLog("SendOpenCNAM", "Invalid Phone Number, URL: " & sUrl)
                Return ""
            End If
            If ex.Message.Contains("(403)") Then
                '' 403   Exceeded Daily Request Count
                hs.WriteLog("SendOpenCNAM", "Exceeded Request Limit, URL: " & sUrl)
                Return ""
            End If
            If ex.Message.Contains("(402)") Then
                '' 402  Deliquent Account
                hs.WriteLog("SendOpenCNAM", "Account is Deliquent, URL: " & sUrl)
                Return ""
            End If
            '' ELSE
            hs.WriteLog("SendOpenCNAM", "HTTP Request Failed: " & ex.ToString)
            Return ""
        End Try
        Return strData
    End Function
End Class

Public Class PbxRecordReceived
    Dim PhoneNumberType As String
    Dim DatabaseFile As String = "d:\files\maillist\MailList.db3"
    Dim LogDatabaseFile As String = "d:\files\Tenholder D Web Sites\PhoneCalls.db3"
    Public Sub Main(ByVal Parms As Object)
        ''
        '' This script is run whenever a record is received from the PBX
        ''
        ''   Pbx Record:
        ''      0  Type     CID,IN,OUT
        ''      1  Number
        ''      2  CO
        ''      3  Ext
        ''      4  Duration
        ''
        Dim PbxDeviceAddress As String = "tenHsPanPbx01"
        Dim DevRef As Integer, PbxRecord As String, PbxParts() As String
        Dim CallerName As String, n As String
        '' Get the PBX Device
        DevRef = hs.GetDeviceRef(PbxDeviceAddress)
        If DevRef < 1 Then
            hs.WriteLog("PbxRecordReceived", "Device " & PbxDeviceAddress & " Not Found")
            Exit Sub
        End If
        '' Get and parse the PBX record
        PbxRecord = hs.DeviceString(DevRef).Trim      'get the PBX data from the Device String
        If PbxRecord.Length < 5 Then
            hs.WriteLog("PbxRecordReceived", "Invalid PBX Record: " & PbxRecord)
            Exit Sub
        End If
        PbxParts = PbxRecord.Split(ToChar(","))
        If PbxParts.Length <> 5 Then
            hs.WriteLog("PbxRecordReceived", "Invalid PBX Record: " & PbxRecord)
            Exit Sub
        End If
        '' Process the PBX Record
        ''   Get Name from Database
        CallerName = ""
        If PbxParts(0).Trim.ToUpper = "CID" Or PbxParts(0).Trim.ToUpper = "IN" Then
            '' Resolve Name, if possible
            n = PbxParts(1).Trim      'get phone number
            If n.Length = 7 Then n = "314" & n
            If n.Length = 11 Then n = n.Substring(1, 10)
            CallerName = GetFromLocalDb(n)
            If CallerName.Length < 3 Then
                ''   Get Name from Webpbxparts(1)
                CallerName = GetFromOpenCNAM(n)
                PhoneNumberType = "From Web"
            Else
                '' Type comes from Local DB
                ' PhoneNumberType = "From dB"
            End If
        End If
        Select Case PbxParts(0).Trim.ToUpper
            Case "CID"
                '' Caller ID at start of call
                ''
                '' Process Number for sending to tablet
                ''
                ''   Announce Phone Call on deck
                hs.SpeakEx(38, "House Phone is Ringing", False)
                ''   Turn OFF Kitchen TV
                ' hs.PluginFunction("IRTrans", "", "SendIR", {"kitchen_tv", "power_off"})
                ''   Speak Name of Caller
                If CallerName.Length > 1 Then
                    ''   If we retrieved good Name, Speak it in Kitchen
                    hs.RunScriptFunc("SendToTablet.vb", "Main", "Speak" & "Call From " & CallerName, True, True)
                End If
                ''   Display Phone Call in Kitchen
                hs.RunScriptFunc("SendToTablet.vb", "Main", "PbxRecord" & FormatPhoneNumber(PbxParts(1)) & "  " & CallerName, True, True)
                AddToLogDatabase(FormatPhoneNumber(PbxParts(1)), CallerName, "Call")
            Case "IN"
                '' Incoming Call Complete
                ''   Log this call to database  (for web page)
                AddToLogDatabase(FormatPhoneNumber(PbxParts(1)), CallerName, PhoneNumberType)
                ''   Send Text to Mobile Phones
                'hs.RunScriptFunc("SendAnEmail.vb", "Main", "Call~3143228520@txt.att.net~" & FormatPhoneNumber(PbxParts(1)) & "  " & CallerName & "  " & PbxParts(4) & " seconds.  #6*100", True, True)
                'hs.RunScriptFunc("SendAnEmail.vb", "Main", "Call~3143231467@txt.att.net~" & FormatPhoneNumber(PbxParts(1)) & "  " & CallerName & "  " & PbxParts(4) & " seconds.  #6*100", True, True)
                hs.RunScriptFunc("PushIt.vb", "Main", "Joe|Home Phone Call|" & FormatPhoneNumber(PbxParts(1)) & "  " & CallerName & "  " & PbxParts(4) & " seconds.  #6*100", True, True)
        End Select
    End Sub
    Private Sub AddToLogDatabase(pPhoneNumber As String, pCallerName As String, pCallerType As String)
        '' Record phone call to Log database
        Dim cmd As SQLite.SQLiteCommand, conn As SQLiteConnection
        Dim s As New StringBuilder
        '' Build Connection String from File Path
        conn = New SQLite.SQLiteConnection
        conn.ConnectionString = "data source=""" & LogDatabaseFile & """"
        conn.Open()
        '' Set default file format for new databases
        cmd = New SQLite.SQLiteCommand("PRAGMA legacy_file_format = OFF", conn)
        cmd.ExecuteNonQuery()
        '' Create the Table  (if it does not already exist)
        s.Clear()
        s = s.Append("CREATE TABLE IF NOT EXISTS PhoneLog (")
        s = s.Append("DateTime Text,Number Text,Name Text,Type Text)")
        cmd = New SQLite.SQLiteCommand(s.ToString, conn)
        cmd.ExecuteNonQuery()
        '' Add Index (if not there)
        s.Clear()
        s = s.Append("CREATE INDEX IF NOT EXISTS NumberIndex ON PhoneLog (DateTime DESC)")
        cmd = New SQLite.SQLiteCommand(s.ToString, conn)
        cmd.ExecuteNonQuery()
        '' Insert the record
        s.Clear()
        s = s.Append("INSERT INTO PhoneLog (DateTime,Number,Name,Type) VALUES (")
        s = s.Append("'" & Format(Now, "yyyyMMddHHmmss") & "'")
        s = s.Append(",'" & pPhoneNumber & "'")
        s = s.Append(",'" & pCallerName & "'")
        s = s.Append(",'" & pCallerType & "')")
        cmd = New SQLite.SQLiteCommand(s.ToString, conn)
        cmd.ExecuteNonQuery()
    End Sub
    Public Function GetFromLocalDb(pPhoneNumber As String) As String
        ''
        '' GetFromLocalDb    
        ''
        ''   Input:  10 Digit Phone Number (string)
        ''   Returns: Associated Name  (or blank, if no data or request fails)
        ''
        ''  The local phone number database (created from Outlook entries offline) is used
        ''

        Dim PhoneNumber As String
        Dim cmd As SQLite.SQLiteCommand, conn As SQLiteConnection, rdr As SQLiteDataReader
        Dim s As String, ret As String
        PhoneNumber = pPhoneNumber
        '' Open Database
        conn = New SQLite.SQLiteConnection
        conn.ConnectionString = "data source=""" & DatabaseFile & """"
        conn.Open()
        '' Retrieve MailList entry for requested Phone Number
        s = "SELECT * FROM PhoneNumbers WHERE Number=" & PhoneNumber
        cmd = New SQLite.SQLiteCommand(s, conn)
        rdr = cmd.ExecuteReader
        If rdr.Read Then
            '' Found a record
            ret = rdr("Name").ToString
            PhoneNumberType = rdr("Type").ToString
            s = s
        Else
            '' No Record
            ret = ""
            PhoneNumberType = "   "
            s = s
        End If
        conn.Close()
        hs.WriteLog("PbxRecordRcvd", "Result from DB: " & ret)
        Return ret
    End Function
    Public Function GetFromOpenCNAM(PhoneNumber As String) As String
        ''
        '' GetFromOpenCNAM     
        ''
        ''   Input:  10 Digit Phone Number (string)
        ''   Returns: Associated Name  (or blank, if no data or request fails)
        ''
        ''  
        ''      request is made using authorization codes   ($0.004 per request)
        ''
        Dim Result As String
        '' Validate parm is 10 digits (more error checking needed here)
        If PhoneNumber.Length <> 10 Then
            hs.WriteLog("GetFromOpenCNAM", "Supplied parameter not 10 digits exactly: " & PhoneNumber.ToString)
            Return ""
        End If

        Result = SendOpenCNAM(PhoneNumber)
        Return Result
    End Function
    Function SendOpenCNAM(ByVal sPhoneNumber As String) As String
        ''
        '' SendOpenCNAM
        ''
        ''      Input:  Phone Number
        ''           
        ''
        ''      Returns:  Associated Name (or blank if not fournd)
        ''
        Dim strData As String = ""
        Dim tlRequest As System.Net.HttpWebRequest
        Dim tlResponse As System.Net.HttpWebResponse
        Dim tlStatusCode As Integer
        Dim BaseUrl As String = " https://api.opencnam.com/v3/phone/"
        Dim AuthParms As String = "?account_sid=ACe9dcc6d238284f828bb0c38ec31d4c4a&auth_token=AUd2273d282897411db7b6e84e5e62b422"
        Dim sUrl As String
        sUrl = BaseUrl & sPhoneNumber

        sUrl = sUrl & AuthParms

        Try
            tlRequest = CType(System.Net.WebRequest.Create(sUrl), System.Net.HttpWebRequest)
            tlRequest.ContentType = "text/html"
            tlRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"
            tlRequest.Method = "GET"
            tlRequest.Timeout = 1000
            tlRequest.AllowAutoRedirect = True
            tlResponse = CType(tlRequest.GetResponse(), System.Net.HttpWebResponse)
            tlStatusCode = tlResponse.StatusCode
            If tlStatusCode = System.Net.HttpStatusCode.OK Then
                strData = New System.IO.StreamReader(tlResponse.GetResponseStream()).ReadToEnd()
                hs.WriteLog("SendOpenCNAM", "Web Returned: " & strData)
            Else
                Return ""
                hs.WriteLog("SendOpenCNAM", "HttpWebRequest Returned: " & tlStatusCode.ToString)
            End If
            tlResponse.Close()
        Catch ex As System.Net.WebException
            If ex.Message.Contains("(404)") Then
                '' 404   No Data for Phone Number, return blank
                Return ""
            End If
            If ex.Message.Contains("(401)") Then
                '' 401   Not Authorized, account codes invalid
                hs.WriteLog("SendOpenCNAM", "Authorization Failed, URL: " & sUrl)
                Return ""
            End If
            If ex.Message.Contains("(400)") Then
                '' 400   Phone Number Provided is Invalid
                hs.WriteLog("SendOpenCNAM", "Invalid Phone Number, URL: " & sUrl)
                Return ""
            End If
            If ex.Message.Contains("(403)") Then
                '' 403   Exceeded Daily Request Count
                hs.WriteLog("SendOpenCNAM", "Exceeded Request Limit, URL: " & sUrl)
                Return ""
            End If
            If ex.Message.Contains("(402)") Then
                '' 402  Deliquent Account
                hs.WriteLog("SendOpenCNAM", "Account is Deliquent, URL: " & sUrl)
                Return ""
            End If
            '' ELSE
            hs.WriteLog("SendOpenCNAM", "HTTP Request Failed: " & ex.ToString)
            Return ""
        End Try
        Return strData
    End Function

    Private Function FormatPhoneNumber(ByVal a As String) As String
        Dim n As String, m As String, i As Integer
        '' Check for 0/1 prefix
        If a.Substring(0, 1) = "0" Or a.Substring(0, 1) = "1" Then
            n = a.Substring(1)                                                          'strip leading char
            m = a.Substring(0, 1) & "."                                         'save prefix of 1. or 0.
        Else
            n = a                                                                                       'do not strip leading char
            m = ""                                                                                  'no prefix
        End If
        '' Terminate if * found
        i = n.IndexOf("*")
        If i >= 0 Then
            n = n.Substring(0, i - 1)
        End If
        Select Case n.Length
            Case 10
                '' 314.842.5089
                Return m & n.Substring(0, 3) & "." & n.Substring(3, 3) &
                 "." & n.Substring(6, 4)
            Case 7
                '' 842.5089
                Return m & n.Substring(0, 3) & "." & n.Substring(3, 4)
            Case 8, 9
                '' 842.5089 nn
                Return m & n.Substring(0, 3) & "." & n.Substring(3, 4) & " "
            Case Is > 10
                '' 314.842.5089 nn
                Return m & n.Substring(0, 3) & "." & n.Substring(3, 3) &
                 "." & n.Substring(6, 4) & " "
            Case Is < 5
                Return "  "
            Case Else
                Return n & m
        End Select
    End Function
End Class

Class SendToTablet
    Public Sub Main(ByVal Parms As Object)
        ''
        '' This is the old approach to messaging to tablet using AutoRemote
        ''
        ''   Tasker now supports simple http requests, see HttpToTablet
        ''
        Dim p, rc As String
        Dim BaseUrl As String
        'BaseUrl = "https://autoremotejoaomgcd.appspot.com/sendmessage?key=APA91bH8jVe1B7BANohr8XeDEOP0OoLpXSud-FvHvwbBSvvyC0-0Y4xo1bSWggSXGX8rK2KvUpF0Uzu9DuczuSBXLGa-wPt57AqrsPOgKRd4DsvzUZHAB310GsxFkrbzDiTAxyWBp7qo&message="


        BaseUrl = "https://autoremotejoaomgcd.appspot.com/sendmessage?key=d0b3rr0ykbw:APA91bE6y6-gl5Cxupt9UctaxAljIjYKizWtziYqI9E2EMQmFWvPDfNf4XHV4gyPDE1EfoYMKkjLBIkUuDDujtohaBfzTN8wn92lje09zMRuFTKY8XTk-c8&message="

        'BaseUrl = "http://192.168.1.247:1817/sendmessage?message="



        p = Parms.ToString
        hs.WriteLog("SendToTablet", p)
        rc = SendPush(BaseUrl & p)
        If rc <> "OK" Then
            hs.WriteLog("SendToTablet", "Bad RC from SendPush: " & rc)
            hs.WriteLog("SendToTablet", "URL: " & BaseUrl & p)
        End If
    End Sub
    Function SendPush(ByVal sURL As String) As String
        Dim strData As String = ""
        Dim tlRequest As System.Net.HttpWebRequest
        Dim tlResponse As System.Net.HttpWebResponse
        Dim tlStatusCode As Integer
        Try
            tlRequest = CType(System.Net.WebRequest.Create(sURL), System.Net.HttpWebRequest)
            tlRequest.ContentType = "text/html"
            tlRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"
            tlRequest.Method = "GET"
            tlRequest.Timeout = 10000
            tlRequest.AllowAutoRedirect = True
            tlResponse = CType(tlRequest.GetResponse(), System.Net.HttpWebResponse)
            tlStatusCode = tlResponse.StatusCode
            If tlStatusCode = System.Net.HttpStatusCode.OK Then
                strData = New System.IO.StreamReader(tlResponse.GetResponseStream()).ReadToEnd()
                If strData.Contains("OK") Then strData = "OK"
            Else
                Return "Grab Error"
            End If
            tlResponse.Close()
        Catch ex As Exception
            hs.WriteLog("SendToTablet", "HTTP Request Failed: " & ex.ToString)
        End Try
        Return strData
    End Function
End Class



Class NowNightTime
    Public Sub Main(Optional ByVal Parms As String = "")
        ' Dim cc As HomeSeerAPI.CAPI.CAPIControl
        Dim cr As HomeSeerAPI.CAPI.CAPIControlResponse
        Dim XmasTime As Boolean
        Dim HouseOccupied As Boolean
        Dim WeHaveGuests As Boolean
        Dim Halloween As Boolean
        Dim devBsmtBedroomOutsideLight As Integer


        XmasTime = (hs.DeviceValueEx(hs.GetDeviceRefByName("Virtual Virtual IsXmas")) > 0)
        Halloween = (hs.DeviceValueEx(hs.GetDeviceRefByName("Virtual Virtual IsHalloween")) > 0)
        HouseOccupied = (hs.DeviceValueByName("Virtual Virtual HouseOccupied") > 0)
        WeHaveGuests = hs.IsOnByName("Virtual Virtual Guests")
        devBsmtBedroomOutsideLight = GetFeatureRefByFullname(hs, "Outside Lower Deck East", "")

        '' Activate Link for all UPB stuff
        ControlDevice("NightTime Link", "Activate")

        '' Check if House Is Occupied
        If Not HouseOccupied Then
            '' House is NOT Occupied
            ''    Kitchen Counter Puck Lights ON
            ControlDevice("Kitchen Counter Puck Lights", "ON")
        Else
            '' House IS Occupied
            If XmasTime Then
                '' Christmas and House Occupied
                hs.TriggerEvent("XmasIndoorON")
                'ControlDevice("AllInddorXmasLightsLink", "Activate")
            End If
        End If

        '' We Have Guests
        If WeHaveGuests Then
            ControlDevice("J and J Bath Night Light", "On")
        End If

        '' Lower Deck Outside Lights       DIM to their default
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Outside Lower Deck Link"), True, "Activate", False, False))
        If WeHaveGuests Then hs4.SendControlForFeatureByValue(35, 0)   'light outside bsmt bedroom off if we have guests



        ''''' Center Light DIM
        'cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Outside Lower Deck Cntr"), True, "Dim (value)%", False, False)
        'cc.ControlValue = 50
        'cr = hs.CAPIControlHandler(cc)
        ''''' East LightDIM if No Guests
        'If Not WeHaveGuests Then
        '    cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Outside Lower Deck East"), True, "Dim (value)%", False, False)
        '    cc.ControlValue = 30
        '    cr = hs.CAPIControlHandler(cc)
        'End If

        '' Christmas
        If XmasTime Then
            ControlDevice("AllOutdoorXmasLightsLink", "Activate")
            hs.WaitSecs(2)
            ControlDevice("Outside Front Porch", "OFF")
            'ControlDevice("Outside Coach Lights", "Off")
        End If

        '' Halloween  
        If Halloween Then
            ControlDevice("Outside Front Porch", "OFF")
            ControlDevice("Halloween Front Porch", "ON")
        End If
    End Sub
    Private Sub ControlDevice(DeviceName As String, DeviceAction As String)
        '' Do all CAPI stuff to control a device
        Dim CC As HomeSeerAPI.CAPIControl, CCResp As HomeSeerAPI.CAPIControlResponse
        Dim DevRef As Integer

        DevRef = hs.GetDeviceRefByName(DeviceName)
        If DevRef < 0 Then
            hs.WriteLog("ControlDevice", "Error, Could Not Get Device Reference for:  " & DeviceName)
            Exit Sub
        End If
        CC = hs.CAPIGetSingleControl(DevRef, True, DeviceAction, False, False)
        If IsNothing(CC) Then
            hs.WriteLog("ControlDevice", "Error, Could Not Find Action: " & DeviceAction & "     for Device: " & DeviceName)
            Exit Sub
        End If
        CCResp = hs.CAPIControlHandler(CC)
    End Sub
End Class

Public Class HttpToTablet
    Public Sub Main(ByVal Parms As Object)
        ''
        ''  HttpToTablet uses the Tasker web server on the Kitchen tablet to make requests via HTTP Get
        ''
        '' Extract Parms --> ParmArray
        Dim ParmArray(0) As String
        '' Determine if Parms is a single String, or an Array of Strings
        If Parms.GetType().ToString = "System.String" Then
            '' Single String from HS scripting engine. Extract parms
            ParmArray(0) = ""
            If Parms.ToString.Contains("|") Then
                ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
            Else
                ParmArray(0) = Parms.ToString
            End If
        Else
            '' Array of strings from HsTouch
            ParmArray = CType(Parms, String())
        End If
        '' ParmArray() now contains one or more passed parameters

        Dim p, rc As String
        Dim BaseUrl As String
        BaseUrl = "http://192.168.1.247:8037/"
        p = ParmArray(0).ToString.Trim.ToLower
        hs.WriteLog("HttpToTablet", p)
        rc = SendPush(BaseUrl & p)
        If rc <> "OK" Then
            hs.WriteLog("HttpToTablet", "Bad RC from SendPush: " & rc)
            hs.WriteLog("HttpToTablet", "URL: " & BaseUrl & p)
        End If
    End Sub
    Public Sub Testit(ByVal parms As Object)
        Main("beep")
    End Sub
    Function SendPush(ByVal sURL As String) As String
        Dim strData As String = ""
        Dim tlRequest As System.Net.HttpWebRequest
        Dim tlResponse As System.Net.HttpWebResponse
        Dim tlStatusCode As Integer
        Try
            tlRequest = CType(System.Net.WebRequest.Create(sURL), System.Net.HttpWebRequest)
            tlRequest.ContentType = "text/html"
            tlRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"
            tlRequest.Method = "GET"
            tlRequest.Timeout = 10000
            tlRequest.AllowAutoRedirect = True
            tlResponse = CType(tlRequest.GetResponse(), System.Net.HttpWebResponse)
            tlStatusCode = tlResponse.StatusCode
            If tlStatusCode = System.Net.HttpStatusCode.OK Then
                strData = New System.IO.StreamReader(tlResponse.GetResponseStream()).ReadToEnd()
                If strData.ToUpper.Contains("OK") Then strData = "OK"
            Else
                Return "HTTP Error"
            End If
            tlResponse.Close()
        Catch ex As Exception
            hs.WriteLog("HttpToTablet", "HTTP Request Failed: " & ex.ToString)
        End Try
        Return strData
    End Function
End Class

Public Class Bedtime
    Public Sub Main(ByVal Parms As Object)
        Dim ParmArray(10) As String
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If
        ''
        '' Bedtime button in Master Bedroom pushed, shut down house for the night
        ''
        Dim SonosMasterControl As Integer
        Dim rc As Boolean
        '' Get DevRefs
        SonosMasterControl = GetFeatureRefByFullname(hs, "Sonos4 Master Sonos Master Control", "Sonos4 Master Control")

        '' Kitchen and Laundry
        hs.TriggerEvent("AllKitchenLightsOFF")
        '' TVs
        hs.TriggerEvent("KitchenTvOff")
        hs.TriggerEvent("MoRoomTvOff")
        'hs.TriggerEvent("MstrBdrmTvOff")
        '' Sun Room
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("MissouriRoomLights"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("MO Room Fan Link"), True, "Snap Off", False, False))
        '' Great Room
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Great Room ALL OFF"), True, "Snap Off", False, False))
        '' Dining Room
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Dining Room Ceiling"), True, "Snap Off", False, False))
        '' Upper Deck
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("DeckALLOFF"), True, "Snap Off", False, False))
        '' Foyer and Hallway
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Foyer Ceiling Link"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hallway Ceiling North Link"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hallway Ceiling South Link"), True, "Snap Off", False, False))
        '' Hall Bath
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Guest Bathroom Light Link"), True, "Snap Off", False, False))
        '' Office
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Office Ceiling Link"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Office Desk Link"), True, "Snap Off", False, False))
        '' Front Bedroom
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Front Bedroom Ceiling Light Link"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Front Bedroom Lamps Llink"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Front Bedroom Fan Link"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Front Bedroom Closet Link"), True, "Snap Off", False, False))
        '' Middle Bedroom
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Middle Bedroom Ceiling Light Link"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Middle Bedroom Lamps Link"), True, "Snap Off", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Middle Bedroom Ceiling Fan Link"), True, "Snap Off", False, False))
        '' Jack and Jill Bath
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("JandJ ALL Link"), True, "Snap Off", False, False))
        '' Basement
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("All Basement OFF Link"), True, "Snap Off", False, False))
        '' Sonos Players
        'hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Sonos Master Master"), True, "All Zones Pause", False, False))

        rc = hs4.SendControlForFeatureByValue(SonosMasterControl, 1000)   'Pause all players
    End Sub
End Class

Class WeatherAlert
    Public Sub Main(ByVal Parms As String)
        Dim NightTime As Boolean = (Now().TimeOfDay < CDate(hs.Sunrise).TimeOfDay) Or (Now().TimeOfDay > CDate(hs.Sunset).TimeOfDay)
        Dim s, ss As String

        s = hs.CAPIGetStatus(hs.GetDeviceRefByName("weather Alerts weatherXML Saint Louis Last Alert")).Status
        ss = hs.DeviceStringByName("weather Alerts weatherXML Saint Louis Last Alert")
        hs.WriteLog("WeatherAlert.vb", Parms)
        ' hs.RunScriptFunc("SendToTablet.vb", "Main", "Speak Weather Alert " & s, True, True)
        hs.RunScriptFunc("SendToTablet.vb", "Main", "DisplayWeatherAlerts", True, True)
        'hs.WriteLog("test", "now.timeofday: " & Now.TimeOfDay.ToString)
        'hs.WriteLog("test", "cdate: " & CDate("9:00pm").TimeOfDay.ToString)
        If Now.Hour > 8 Or Now.Hour < 22 Then
            ' hs.SpeakEx(37, "There is a new weather alert", True)
            hs.PluginFunction("Sonos4", "", "PlayerAnnouncement", {"There is a new weather alert", "RINCON_48A6B8A646EC01400", "40"})  'Kitchen

        End If
    End Sub
End Class

Class SevereWeatherAlert
    Public Sub Main(ByVal Parms As String)
        Dim NightTime As Boolean = (Now().TimeOfDay < CDate(hs.Sunrise).TimeOfDay) Or (Now().TimeOfDay > CDate(hs.Sunset).TimeOfDay)
        'Dim s, ss As String

        ' hs.SpeakEx(37, "warning,,severe weather alert", True)

        hs.PluginFunction("Sonos4", "", "PlayerAnnouncement", {"warning,,severe weather alert", "RINCON_48A6B8A646EC01400", "40"})  'Kitchen
    End Sub
End Class

Public Class SendAnEmail
    Public Sub Main(ByVal pParms As String)
        ''
        '' Single Parm String:     "Subject~To~Body"
        ''
        Dim s, Parms() As String
        If pParms.Length < 5 Then
            hs.WriteLog("SendAnEmail", "Invalid Parms: " & pParms)
            Exit Sub
        End If
        Parms = pParms.Split(ToChar("~"))                  'get individual parameters
        If Parms.Length <> 3 Then
            hs.WriteLog("SendAnEmail", "Invalid Parms: " & pParms)
            Exit Sub
        End If
        s = EmailIt(Parms(2), Parms(1), Parms(0))
        If s <> "" Then
            hs.WriteLog("SendAnEmail", s)
        End If
    End Sub
    'Private Function OLDEmailIt(ByVal EmailBody As String, ByVal EmailTo As String, ByVal EmailSubject As String) As String
    '    Dim msg As New System.Net.Mail.MailMessage
    '    Dim Smtp As System.Net.Mail.SmtpClient
    '    msg.From = New System.Net.Mail.MailAddress("ed@tenholder.net", "HS")
    '    msg.To.Add(EmailTo)
    '    msg.Subject = EmailSubject
    '    msg.Body = EmailBody
    '    msg.IsBodyHtml = False
    '    msg.Priority = Net.Mail.MailPriority.Normal
    '    Smtp = New System.Net.Mail.SmtpClient()
    '    Smtp.Credentials = New System.Net.NetworkCredential("edtenholder@gmail.com", "D00dgd00d")
    '    Smtp.Port = 587
    '    Smtp.Host = "smtp.gmail.com"
    '    Smtp.EnableSsl = True
    '    Try
    '        Smtp.Send(msg)
    '    Catch ex As Exception
    '        Return ("Send Failed: " & ex.Message & ".  EmailSubject: " & EmailSubject)
    '    End Try
    '    Return ("")
    'End Function
    Private Function EmailIt(ByVal EmailBody As String, ByVal EmailTo As String, ByVal EmailSubject As String) As String
        Dim msg As New System.Net.Mail.MailMessage
        Dim Smtp As System.Net.Mail.SmtpClient
        msg.From = New System.Net.Mail.MailAddress("tennotices@gmail.com", "NOTICES")
        msg.To.Add(EmailTo)
        msg.Subject = EmailSubject
        msg.Body = EmailBody
        msg.IsBodyHtml = False
        msg.Priority = Net.Mail.MailPriority.Normal
        Smtp = New System.Net.Mail.SmtpClient()
        Smtp.Credentials = New System.Net.NetworkCredential("tennotices@gmail.com", "rwclcjddarvxgnsn")
        Smtp.Port = 587
        Smtp.Host = "smtp.gmail.com"
        Smtp.EnableSsl = True
        Try
            Smtp.Send(msg)
        Catch ex As Exception
            Return ("Send Failed: " & ex.Message & ".  EmailSubject: " & EmailSubject)
        End Try
        Return ("")
    End Function

End Class

'Public Class HomeAlarm

'    Const AlarmSoundingLinkName As String = "HomeAlarmSounding"
'    Const HsVar As String = "HomeAlarmVar"
'    Public Sub AlarmSoundingStarts(ByVal Parms As Object)
'        Dim Path As String, Line As String, cc() As String
'        Dim NumOfLines As Integer = 0, NumOfLinks As Integer = 0, NumOfDevices As Integer = 0, a As String
'        Dim Devices As New Dictionary(Of Integer, String), TargetDevices As New Dictionary(Of String, Integer)
'        Dim SavedStatus As New Dictionary(Of String, String)
'        Dim AlarmSoundingLink As Integer, i As Integer
'        Dim dev As Scheduler.Classes.DeviceClass, devStatus As Integer = 0, devValue As Long

'        '' Initialize
'        TargetDevices.Clear()
'        Devices.Clear()
'        SavedStatus.Clear()
'        '' Find Export File
'        Path = hs.GetINISetting("UPSTART", "export_file", "", "UPBSpud.ini")
'        '' Loop through entire file
'        Using sr As New System.IO.StreamReader(Path)
'            Do
'                Line = sr.ReadLine()
'                If Not (Line Is Nothing) Then
'                    '' Process each line from Export File
'                    NumOfLines = NumOfLines + 1
'                    Line = Line & ",00"
'                    cc = Line.Split(ToChar(","))
'                    Select Case cc(0)
'                        Case "1"
'                            Exit Do 'last line reached
'                        Case "2"
'                            '' Link definition
'                            NumOfLinks = NumOfLinks + 1
'                            ''   Find the Link Number based upon known Link Name
'                            If cc(2).Trim = AlarmSoundingLinkName Then AlarmSoundingLink = cc(1).ToString
'                        Case "3"
'                            '' Device Definition
'                            NumOfDevices = NumOfDevices + 1
'                            Devices.Add(cc(1).ToString, cc(11).Trim & " " & cc(12).Trim)
'                        Case "4"
'                            '' Preset Record
'                            If cc(4).ToString = AlarmSoundingLink Then
'                                '' This Device Receives the Alarm Link
'                                a = Devices(CInt(cc(3)))   ' name of target device
'                                If Not TargetDevices.ContainsKey(a) Then
'                                    '' Target Devices not in table, add it
'                                    TargetDevices.Add(a, CInt(cc(4)))
'                                End If
'                            End If
'                        Case Else
'                    End Select
'                End If
'            Loop Until Line Is Nothing
'            sr.Close()
'        End Using
'        '' Loop through all found Target Devices
'        a = hs.CreateVar(HsVar)
'        For Each c As String In TargetDevices.Keys
'            '' Save the Current State for each Target Device so it can be Restored when Alarm Stops
'            devValue = hs.DeviceValueByName(c)
'            i = hs.GetDeviceRefByName(c)
'            dev = CType(hs.GetDeviceByRef(i), Scheduler.Classes.DeviceClass)
'            ''''        'devStatus = hs.DeviceStatus(dev.hc & dev.dc)
'            a = devStatus.ToString & "," & devValue.ToString
'            SavedStatus.Add(c, a)
'            hs.WriteLog("AlarmSoundingStarts", "Saving Current State for: " & c & ":" & a)
'        Next c
'        hs.SaveVar(HsVar, SavedStatus)
'        '' Send Link to BLINK all target devices
'        hs.SetDeviceValueByName("UPB Device Link " & AlarmSoundingLinkName, 16)
'        ''''''      hs.ExecX10("a1", "All Lights On")
'        '' Warn Occupants via Sonos
'        hs.SpeakEx(37, "alarm is sounding.  get the hell out", True)
'        '' Write HS Log records
'        hs.WriteLog("AlarmSoundingStarts", "Lines=" & NumOfLines.ToString & " Devices=" & NumOfDevices.ToString & " Links=" & NumOfLinks.ToString & " Targets=" & TargetDevices.Count.ToString)
'    End Sub
'    Public Sub AlarmSoundingStops(ByVal Parms As Object)
'        Dim SavedStatus As New Dictionary(Of String, String)
'        Dim cc() As String

'        '' Restore State of Devices blinking due to alarm
'        SavedStatus = CType(hs.GetVar(HsVar), Dictionary(Of String, String))
'        If SavedStatus Is Nothing Then
'            hs.WriteLog("AlarmSoundingStops", "No Saved Devices to Restore in: " & HsVar)
'        Else
'            For Each kvp As KeyValuePair(Of String, String) In SavedStatus
'                cc = kvp.Value.Split(ToChar(","))
'                hs.SetDeviceValueByName(kvp.Key, ToDouble(cc(1)))
'                hs.WriteLog("AlarmSoundingStops", "Set Device: " & kvp.Key & "   Value=" & cc(1))
'            Next
'        End If
'        '' Let Everyone Know All Is Clear
'        hs.SpeakEx(37, "Don't be scared, alarm has been disarmed", True)
'    End Sub
'    'Public Sub LaundryEntry(ByVal Parms As Object)
'    '    '' Laundry/Garage Door has been opened, at night, and the Alarm was Armed (ELK rule fires Task, HS runs this Event/Script
'    '    LinkOnInterval("UPB Device Link Laundry Ceiling Link", 0, 30)
'    'End Sub
'    'Private Sub LinkOnInterval(ByVal LinkName As String, ByVal OnDelaySeconds As Integer, ByVal OffDelaySeconds As Integer)
'    '    ''
'    '    '' Activate 'LinkName' in 'OnDelaySeconds' and then Deactivates 'LinkName in 'OffDelaySeconds'
'    '    ''
'    '    ActivateALink(LinkName, OnDelaySeconds)
'    '    If OffDelaySeconds > 0 Then
'    '        '' Schedule Event to Deactivate it later
'    '        DeactivateALink(LinkName, OffDelaySeconds)
'    '    End If
'    'End Sub

'    'Private Sub ActivateALink(ByVal LinkName As String, ByVal OnDelaySeconds As Integer)
'    '    ''
'    '    '' Activate 'LinkName' in 'OnDelaySeconds'   (Now if OnDelaySeconds=0)
'    '    ''
'    '    If OnDelaySeconds <= 0 Then
'    '        '' Activate it Now
'    '        hs.SetDeviceValueByName(LinkName, 1)              'activate link
'    '    Else
'    '        '' Schedule Event to Activate it later
'    '        ScheduleALink(LinkName, LinkAction.Activate, OnDelaySeconds)
'    '    End If
'    'End Sub

'    'Private Sub DeactivateALink(ByVal LinkName As String, ByVal OffDelaySeconds As Integer)
'    '    ''
'    '    '' Deactivate 'LinkName' in OffDelaySeconds'  (Now is OffDelaySeconds=0)
'    '    ''
'    '    If OffDelaySeconds <= 0 Then
'    '        '' Deactivate it Now
'    '        hs.SetDeviceValueByName(LinkName, 2)              'deactivate link
'    '    Else
'    '        '' Schedule Event to Deactivate it later
'    '        ScheduleALink(LinkName, LinkAction.Deactivate, OffDelaySeconds)
'    '    End If
'    'End Sub

'    Private Enum LinkAction
'        Activate = 1
'        Deactivate = 2
'    End Enum

'    'Private Sub ScheduleALink(ByVal LinkName As String, ByVal Action As LinkAction, ByVal DelaySeconds As Integer)
'    '    ''
'    '    '' Schedule 'LinkName' to be Activated or Deactivated according to 'LinkAction' in 'DelaySeconds'
'    '    ''
'    '    Dim TempEventName As String
'    '    Dim anEventRef As Integer
'    '    Dim anEvent As Scheduler.Classes.EventClass

'    '    TempEventName = "Delayed_" & LinkName & "_"
'    '    Select Case Action
'    '        Case LinkAction.Activate
'    '            TempEventName = TempEventName & "Activate"
'    '        Case LinkAction.Deactivate
'    '            TempEventName = TempEventName & "Deactivate"
'    '    End Select

'    '    '' Remove any delayed events for the virtual device
'    '    hs.RemoveDelayedEvent("", TempEventName)
'    '    hs.DeleteEvent(TempEventName)
'    '    '' Create Manual Event to be triggered after all have left house
'    '    anEventRef = hs.NewEventGetRef(TempEventName)                               'create new event
'    '    anEvent = hs.GetEventByRef(anEventRef)                                      'get reference to it
'    '    anEvent.misc = &H10                                                         'delete after trigger
'    '    anEvent.ev_abs_time = Scheduler.Classes.trigger_type_values.TYPE_MANUAL     'manual event
'    '    '' Add the Activate/Deactivate Action
'    '    Select Case Action
'    '        Case LinkAction.Activate
'    '            hs.AddAction(anEventRef,
'    '                Scheduler.Classes.action_type_value.ACTION_DEVICE,
'    '                hs.GetDeviceRefByName(LinkName),
'    '                "On", 1, "Activate")
'    '        Case LinkAction.Deactivate
'    '            hs.AddAction(anEventRef,
'    '                Scheduler.Classes.action_type_value.ACTION_DEVICE,
'    '                hs.GetDeviceRefByName(LinkName),
'    '                "Off", 2, "Deactivate")
'    '    End Select
'    '    '' Now that the manual event has been created, schedule it for a little while
'    '    ''   It will delete itself after it runs
'    '    hs.DelayTrigger(DelaySeconds, TempEventName)
'    'End Sub
'End Class

Class HouseStatus
    Public Sub Main(ByVal Parms As Object)
        Dim test As String = AlexaRequest("dummy")
    End Sub
    Public Function AlexaRequest(Parms As String) As String
        ''
        '' Return House Status as Text
        ''
        Dim s As New StringBuilder
        hs.WriteLog("HouseStatus", "script started")
        '' Build status text

        '' House Alarm
        s.Append("the house alarm is set to " & hs.CAPIGetStatus(hs.GetDeviceRefByName("Elk M1 Area Status UltraM1G3 Plugin AreaStatus 01 [Area 1 Arming Status]")).Status)

        '' Basement Ceiling Vents
        If hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Main NCNTR (On/Off/Dim)")) > 5 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Main SWEST (On/Off/Dim)")) > 5 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Main NWEST (On/Off/Dim)")) > 5 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Main NEAST (On/Off/Dim)")) > 5 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Main SEAST (On/Off/Dim)")) > 5 Then
            s.Append(",, , a basement main ceiling vent is open")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Bdrm EAST (On/Off/Dim)")) > 5 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Bdrm WEST (On/Off/Dim)")) > 5 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Bdrm BATH (On/Off/Dim)")) > 5 Then
            s.Append(",, , a basement bedroom ceiling vent is open")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Sewing WEST  (On/Off/Dim)")) > 5 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Vent Sewing EAST (On/Off/Dim)")) > 5 Then
            s.Append(",, , a basement sewing room ceiling vent is open")
        End If

        '' Basement Storage Room Doors
        If hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Door Stor East Open/Close")) <> 0 Then
            s.Append(",, , basement storage door east is open")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Door Stor West")) <> 0 Then
            s.Append(",, , basement storage door west is open")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("JowiHue JowiHue Door Cedar Closet Open/Close")) <> 0 Then
            s.Append(",, , basement cedar closet door is open")
        End If

        '' Basement Bedroom Lights
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Bsmt Bedroom West Lamp")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Bsmt Bedroom N Bed Lamp")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Bsmt Bedroom S Bed Lamp")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Bsmt Bedroom Bath Shower")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Bsmt Bedroom Closet")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Bsmt Bedroom Bdrm Ceiling Wes")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Bsmt Bedroom Bdrm Bed South")) <> 0 Then
            s.Append(", , a basement bedroom light is on")
        End If

        '' Basement Storage Area Lights
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Stor W Ceiling")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Stor Ctr Ceiling")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Stor SE Ceiling")) <> 0 Then
            s.Append(", a basement storage area light is on")
        End If

        '' Office Lights
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Office Desk")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Office Ceiling")) <> 0 Then
            s.Append(", an office light is on")
        End If

        '' Kitchen Lights
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Kitchen Table")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Passthrough")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen East Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Island Pendants")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Passthrough Cab")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Counter Pucks")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Top Pucks")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Cooking Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Sink Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Kitchen Stove Ceiling")) <> 0 Then
            s.Append(", a kitchen light is on")
        End If

        '' Basement Main Area Lights
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Main Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Basement South Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Stairs")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Game Table")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Bar Lights")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Hallway Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Basement Closet Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Basement East Ceiling")) <> 0 Then
            s.Append(", a basement main room light is on")
        End If

        '' Lower Deck Lights
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Outside Lower Deck Cntr")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Outside Lower Deck West")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Outside Lower Deck East")) <> 0 Then
            s.Append(", a lower deck light is On")
        End If

        '''' Screened Deck Heaters
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Deck Left Switch - Ch 1")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Deck Left Switch - Ch 2")) <> 0 Then
            s.Append(", a deck heater is on")
        End If

        '' Screened Deck Fans
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Deck Right Switch - Ch 1")) <> 0 Then
            s.Append(", the deck fans are on")
        End If

        '' Screened Deck Lights
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Deck Middle Switch - Ch 1")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Deck Middle Switch - Ch 2")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Deck Right Switch - Ch 2")) <> 0 Then
            s.Append(", a deck light is on")
        End If

        '' Jack and Jill Bathroom
        If hs.DeviceValueEx(hs.GetDeviceRefByName("J and J Bath Night Light")) <> 0 Then
            s.Append(", jack and jill bathroom night light is on")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("J and J Bath Vanity Light")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("J and J Bath Ceiling Light")) <> 0 Then
            s.Append(", a jack and jill bathroom light is on")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("J and J Bath Ceiling Heat")) <> 0 Then
            s.Append(", jack and jill bathroom ceiling heat is on")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("J and J Bath Ceiling Fan")) <> 0 Then
            s.Append(", jack and jill bathroom ceiling fan is on")
        End If

        '' Middle Bedroom
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Middle Bedroom Lamps")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Middle Bedroom Ceiling Light")) <> 0 Then
            s.Append(", a middle bedroom light is on")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Middle Bedroom Ceiling Fan")) <> 0 Then
            s.Append(", the middle bedroom fan is on")
        End If

        '' Master Bath
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath MRT Counter")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Ceiling")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Tub")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Outside Shower")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Shower")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Toilet Light")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath MRT Vanity")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Accent Light")) <> 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath EJT Vanity")) <> 0 Then
            s.Append(", a master bath light is on")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Fan")) <> 0 Then
            s.Append(", master bath ceiling fan is on")
        End If

        '' Great Room
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Great Room East Ceiling Fan")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Great Room West Ceiling Fan")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Great Room Lamps")) <> 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Great Room Fire Place")) <> 0 Then
            s.Append(", a great room light is on")
        End If

        '' Garage
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Garage Garage Ceiling")) <> 0 Then
            s.Append(", ' the garage light is on")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Zone UltraM1G3 Plugin Zone 066 [Garage Door SO]")) <> 1 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Zone UltraM1G3 Plugin Zone 067 [Garage Door NO]")) <> 1 Then
            s.Append(", , a garage door is open")
        End If

        '' Do we have House Guests?
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Virtual Virtual Guests")) = 1 Then
            s.Append(", we have house guests")
        End If

        '' Is it Xmas Time?
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Virtual Virtual IsXmas")) > 0 Then
            s.Append(", house is set for christmas")
        End If

        '' Master Bath Floor
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Master Bath Floor")) <> 0 Then
            s.Append(", master bath floor heat is on")
        End If

        '' SONOS Speakers
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Office State")) = 1 Then
            s.Append(", office sonos is playing")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Kitchen State")) = 1 Then
            s.Append(", kitchen sonos is playing")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Deck East State")) = 1 Or hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Deck West State")) = 1 Then
            s.Append(", deck sonos is playing")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Sewing Room State")) = 1 Then
            s.Append(", sewing room sonos is playing")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Master Bedroom State")) = 1 Then
            s.Append(", master bedroom sonos is playing")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Basement State")) = 1 Then
            s.Append(", basement sonos is playing")
        End If
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Sonos Great Room State")) = 1 Then
            s.Append(", great room sonos is playing")
        End If
        '' Lawn Sprinklers
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Valve West Front")) > 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Valve East Side")) > 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Valve East Back")) > 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Valve West Back")) > 0 Or
                hs.DeviceValueEx(hs.GetDeviceRefByName("Valve West Driveway")) > 0 Or
            hs.DeviceValueEx(hs.GetDeviceRefByName("Valve East Front")) > 0 Then
            s.Append(", lawn sprinklers are running")
        End If
        '' Return Status as Text
        If s.Length < 1 Then s.Append("nothing to report")
        Return s.ToString
    End Function
End Class

Class TestHouse
    Public Sub Main(ByVal Parms As Object)
        Dim s As String
        s = AlexaRequest("")
    End Sub
    Public Function AlexaRequest(Parms As String) As String
        ''
        '' Run tests of home interfaces
        ''
        Dim s As New StringBuilder
        Dim cc As HomeSeerAPI.CAPI.CAPIControl
        Dim cr As HomeSeerAPI.CAPI.CAPIControlResponse
        '' Respond to Alexa
        's.Append("ten is finished testing interfaces to Amazon Echo")

        '' Test interface to kitchen tablet
        hs.RunScriptFunc("SendToTablet.vb", "Main", "Speak tablet, tablet, tablet, tablet, tablet", False, True)
        'hs.WaitSecs(20)
        'hs.RunScriptFunc("SendToTablet.vb", "Main", "treat", True, True)   ' Long Beep
        'hs.WaitSecs(20)
        'hs.RunScriptFunc("SendToTablet.vb", "Main", "AlarmEntryDelay", True, True)   ' Alarm Screen
        'hs.WaitSecs(20)
        'hs.RunScriptFunc("SendToTablet.vb", "Main", "Slides", True, True)   ' Slide Show
        'hs.WaitSecs(20)
        'hs.RunScriptFunc("SendToTablet.vb", "Main", "treat", True, True)   ' Long Beep

        '' Send Email to EJT
        hs.RunScriptFunc("SendAnEmail.vb", "Main", "TEN Test~3143228520@txt.att.net~Test Email From TEN", True, True)

        ''   Test Harmony Interface  --  Watch Roku on Kitchen TV
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Plugin HarmonyHub Current Activity"), True, "Watch Roku", False, False))

        '' Test UPB Interface 
        '''' Kitchen ON
        cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Kitchen ON Link"), True, "Activate", False, False)
        cr = hs.CAPIControlHandler(cc)
        hs.WaitSecs(2)
        '''' Kitchen DIM
        cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Kitchen DIM Link"), True, "Activate", False, False)
        cr = hs.CAPIControlHandler(cc)
        hs.WaitSecs(2)
        '''' Kitchen OFF  (will also turn off TV
        cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Kitchen OFF Link"), True, "Activate", False, False)
        cr = hs.CAPIControlHandler(cc)
        hs.WaitSecs(2)
        '''' Kitchen ON
        cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Kitchen ON Link"), True, "Activate", False, False)
        cr = hs.CAPIControlHandler(cc)

        '' Test interface to house alarm
        'hs.WaitSecs(2)
        hs.TriggerEvent("AlexaTest")

        '' Send Notification to Alexas
        hs.RunScriptFunc("SendToAlexa.vb", "Main", "test notification from ten", False, True)

        '' Test interface to Sonos
        hs.WaitSecs(3)
        'hs.SpeakEx(37, "sonos, sonos, sonos, sonos, sonos", False)

        hs.PluginFunction("Sonos4", "", "PlayerAnnouncement", {"test test test test", "RINCON_48A6B8A646EC01400", "40"})  'Kitchen


        ''   Test Harmony Interface  --  Turn OFF Kitchen TV
        cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Plugin HarmonyHub Current Activity"), True, "PowerOff", False, False)
        cr = hs.CAPIControlHandler(cc)

        '' Send Closing Statement to Alexa Speaker
        hs.WaitSecs(5)
        hs.RunScriptFunc("AlexaSpeak.vb", "Main", "Kitchen|finished testing all of ten's interfaces", False, True)
        Return s.ToString
    End Function
End Class

Class SetHouseAlarmAway
    Public Sub Main(ByVal Parms As Object)
    End Sub
    Public Function AlexaRequest(Parms As String) As String
        Dim s As New StringBuilder
        ''
        '' Set house alarm to AWAY, checking if successful and returning result to Alexa
        ''
        '' Check if already in STAY
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Area UltraM1G3 Plugin Area 01 [Tenholders]")) = 2 Then
            s.Append("alarm is already set to, stay")
        Else
            '' Not in STAY, so try and set it to STAY
            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Elk M1 Area UltraM1G3 Plugin Area 01 [Tenholders]"), True, "Armed Stay", False, False))
            hs.WaitSecs(1)
            If hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Area UltraM1G3 Plugin Area 01 [Tenholders]")) = 2 Then
                '' Attempt to STAY worked
                s.Append("alarm successfully set to, stay")
            Else
                '' Attempt to STAY did not take, try again
                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Elk M1 Area UltraM1G3 Plugin Area 01 [Tenholders]"), True, "Armed Stay", False, False))
                hs.WaitSecs(1)
                If hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Area UltraM1G3 Plugin Area 01 [Tenholders]")) = 2 Then
                    '' Attempt worked
                    s.Append("alarm successfully set to, stay")
                Else
                    '' Still failed, give up
                    s.Append("could not set alarm to stay, check all zones")
                End If
            End If
        End If
        '' Place text into HS variable
        hs.WriteLog("SetHouseAlarmAway", "Response to Alexa: " & s.ToString)
        If s.Length < 1 Then s.Append("failed")
        Return s.ToString
    End Function
End Class
'Class AlexaToSonos
'    Public Sub Main(ByVal Parms As Object)
'    End Sub
'    Public Function AlexaRequest(Parms As String) As String
'        Dim s As New StringBuilder, rc As String, s2, s3 As String
'        s2 = Parms
'        hs.WriteLog("AlexaToSonos", "parm:" & s2)
'        If s2.ToUpper.Trim.Contains("CONTROL") Then
'            '' Request for which player to control, save it
'            s3 = GetPlayerName(s2)
'            If s3.Length < 1 Then
'                '' Could not find a valid player name
'                s.Append("I heard, " & s2 & " , could not determine a valid sonos player name.  please try again")
'            Else
'                '' Have a good player, save it for future sonos commands
'                rc = hs.SaveVar("SonosPlayer", s3)
'                If rc.Length > 0 Then
'                    '' variable probably no exist, create it
'                    hs.CreateVar("SonosPlayer")
'                    rc = hs.SaveVar("SonosPlayer", s3)
'                    If rc.Length > 0 Then
'                        hs.WriteLog("AlexaToSonos", "SaveVar RC: " & rc)
'                    End If
'                End If
'                s.Append("TEN now controlling " & s3 & " player")
'            End If
'        End If
'        '' Return Response to Alexa
'        hs.WriteLog("AlexaToSonos", "Returns:" & s.ToString)
'        If s.Length < 1 Then s.Append("OK")
'        Return s.ToString
'    End Function
'    Private Function GetPlayerName(AlexaString As String) As String
'        Dim InString() As String = {"deck", "sewing", "bedroom", "basement", "computer", "office", "great", "kitchen"}
'        Dim PlayerNames() As String = {"Sonos Deck East", "Sonos Sewing Room", "Sonos Master Bedroom", "Sonos Basement", "Sonos Computer Room", "Sonos Office", "Sonos Great Room", "Sonos Kitchen"}

'        For i As Integer = 0 To InString.Length - 1
'            If AlexaString.ToLower.Trim.Contains(InString(i)) Then
'                Return PlayerNames(i)
'            End If
'        Next
'        Return ""
'    End Function
'End Class

'Class AlexaKitchenTV
'    Public Sub Main(ByVal Parms As Object)    ' This is here to facilitate tenScripting
'    End Sub
'    Public Function FirstRequest(parm As String) As String
'        ''
'        '' Called when Alexa heard:  'Kitchen TV', and on subsequent calls during conversation (without the 'Kitchen TV')
'        ''
'        Dim parms() As String, Spoken As String, theNumber As String, s As String
'        Dim Words() As String

'        hs.WriteLog("aTV1", parm)
'        parms = parm.Split(ToChar("|"))
'        Spoken = parms(0).ToUpper.Trim.ToUpper
'        theNumber = parms(1).ToUpper.Trim
'        Words = Spoken.Split(ToChar(" "))

'        If Spoken = "NO" Then
'            '' NO, do not want another request
'            Return "ok, all done"
'        End If
'        If Spoken.Contains("OFF") Then
'            '' Turn OFF TV
'            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Plugin HarmonyHub Kitchen TV"), True, "Power Off", False, False))
'            ' hs.PluginFunction("IRTrans", "", "SendIR", {"kitchen_tv", "power_off"})
'            Return ("OK, turning kitchen tv off")
'        End If
'        If Spoken.Contains("LOUDER") Or Spoken.Contains("UP") Then
'            '' Volume UP
'            For i As Integer = 1 To 5
'                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony Kitchen TV"), True, "VolumeUp", False, False))
'            Next i
'            Return "another request?"
'        End If
'        If Spoken.Contains("SOFTER") Or Spoken.Contains("DOWN") Then
'            '' Volume DOWN
'            For i As Integer = 1 To 5
'                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony Kitchen TV"), True, "VolumeDown", False, False))
'            Next i
'            Return "another request?"
'        End If
'        If Words.Contains("ON") Then
'            '' ON
'            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony Kitchen Hub"), True, "Watch Kitchen TV", False, False))
'            Return "ok, on, another request?"
'        End If
'        If Spoken.Contains("MUTE") Then
'            '' Mute
'            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony Kitchen TV"), True, "Mute", False, False))
'            Return "another request?"
'        End If
'        '' Play or Pause
'        If Spoken.Contains("PLAY") Then
'            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "Play", False, False))
'            Return "another request?"
'        End If
'        If Spoken.Contains("PAUSE") Then
'            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "Pause", False, False))
'            Return "another request?"
'        End If
'        ''
'        '' Handle Marilyn's Hearing Aids
'        ''
'        If Spoken.Contains("HEARING") Or Spoken.Contains("AIDS") Or Spoken.Contains("AID") Then
'            DelayThenSend(0, "Menu")
'            DelayThenSend(5, "DirectionDown")
'            DelayThenSend(5, "DirectionRight")
'            DelayThenSend(4, "DirectionDown")
'            For i As Integer = 1 To 6
'                DelayThenSend(2, "DirectionDown")
'            Next
'            DelayThenSend(1, "DirectionRight")
'            DelayThenSend(2, "Exit")
'            Return "another request?"
'        End If
'        ''
'        '' Handle Channel Change
'        ''
'        ''    By Name
'        Dim ChNames() As String = {"FOX SPORTS", "FOX NEWS", "FOX", "C. B. S.", "N. B. C.", "A. B. C.", "ABC", "E. S. P. N.", "ESPN", "WEATHER", "C. N. N.", "BRAVO", "HGTV", "GARDEN"}
'        Dim ChNumbers() As String = {"671", "360", "2", "4", "5", "30", "30", "206", "206", "362", "202", "237", "229", "229"}
'        For i As Integer = 0 To ChNames.Length - 1
'            If Spoken.Contains(ChNames(i)) Then
'                '' Found a Channel by Name
'                s = ChangeChannel(ChNumbers(i))
'                Return "Channel Number " & ChNumbers(i) & " , another request?"
'            End If
'        Next
'        ''    By Number
'        If theNumber.Length > 1 Then
'            s = ChangeChannel(theNumber)
'        End If
'        '' Do not match on any request, ask again
'        Return "did not understand. say no to quit?"
'    End Function
'    Friend Sub DelayThenSend(DelaySeconds As Integer, Command As String)
'        hs.WaitSecs(DelaySeconds)
'        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony Kitchen TV"), True, Command, False, False))
'    End Sub
'    Friend Function ChangeChannel(ChNumber As String) As String
'        ''
'        '' Send Channel Number to DTV2
'        ''
'        Dim s1 As String
'        ' Loop through requested number
'        For i As Integer = 0 To ChNumber.Length - 1
'            s1 = ChNumber.Substring(i, 1)           'get a single digit of number
'            Select Case s1
'                Case "0"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "0", False, False))
'                Case "1"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "1", False, False))
'                Case "2"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "2", False, False))
'                Case "3"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "3", False, False))
'                Case "4"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "4", False, False))
'                Case "5"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "5", False, False))
'                Case "6"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "6", False, False))
'                Case "7"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "7", False, False))
'                Case "8"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "8", False, False))
'                Case "9"
'                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hubs Harmony DTV2"), True, "9", False, False))
'                Case Else
'                    ' Shouldn't be here
'                    hs.WriteLog("KitTV", "ChangeChannel parm contains non-digit. Parm:" & ChNumber)
'                    Return "Error, see HS log"
'            End Select
'            hs.WaitSecs(0.5)
'        Next
'        Return ""
'    End Function
'End Class
Class LeavingHouse
    Public Sub Main(ByVal Parms As Object)
        '
        ' Alarm Has Been Set Away
        '
        Dim NightTime As Boolean = (Now().TimeOfDay < CDate(hs.Sunrise).TimeOfDay) Or (Now().TimeOfDay > CDate(hs.Sunset).TimeOfDay)
        Dim SonosMasterControl As Integer
        Dim rc As Boolean
        '' Get DevRefs
        SonosMasterControl = GetFeatureRefByFullname(hs, "Sonos4 Master Sonos Master Control", "Sonos4 Master Control")



        If NightTime Then
            ' If Night, turn on garage light (for awhile) and kitchen counter pucks
            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Garage Ceiling Link"), True, "Activate", False, False))
            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Main Kitchen Counter Puck Lights"), True, "On", False, False))
        End If


        '' Wait for User to change their mind
        hs.WaitSecs(15)
        ' Cancel processing if no longer set to AWAY
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Area UltraM1G3 Plugin Area 01 [Tenholders]")) <> 1 Then Exit Sub



        ' Set House as Occupied, Kitchen Lights OFF, TVs OFF, Announce Goodbye, Pause All Sonos, Send Email
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Virtual Virtual HouseOccupied"), True, "Away", False, False))
        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Main Kitchen All Kitchen OFF"), True, "On", False, False))
        hs.TriggerEvent("TurnTVsOFF")
        'hs.SpeakEx(37, "Goodbye", True)
        'hs.WaitSecs(15)
        ' hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Sonos Master Master"), True, "All Zones Pause", False, False))


        rc = hs4.SendControlForFeatureByValue(SonosMasterControl, 1000)   'Pause all players


        'hs.RunScriptFunc("SendAnEmail.vb", "Main", "Empty~3143228520@txt.att.net~Empty House", True, True)
        hs.RunScriptFunc("PushIt.vb", "Main", "Joe|House Status|The House is Now Empty", True, True)

        hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Leaving House Link"), True, "Off", False, False))

        If True Then
            ' If Night, wait 3 minutes and then turn off garage light
            hs.WaitSecs(3 * 60)
            hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Garage Ceiling Link"), True, "Deactivate", False, False))
        End If
    End Sub
End Class

Public Class ShowSlides
    Public Sub Main(ByVal Parms As Object)
        Dim ParmArray(10) As String
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If
        '' Send command to Tablet to display Slide Show
        hs.RunScriptFunc("HttpToTablet.vb", "Main", "Slides", True, True)

    End Sub
End Class

Public Class AlexaTriggers
    Public Sub Main(ByVal Parms As Object)
        ''
        '' Create Report of Alexa Helper Triggers
        ''
        Dim ParmArray(10) As String
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If
        '
        Dim TriggerName, aText, aName, aMode, aAction, tAction, aDevice As String
        Dim sw As StreamWriter
        Dim mText As String()
        Dim s As String

        '' Create output file
        sw = System.IO.File.CreateText("d:\files\temp\AlexaTriggers.csv")

        sw.WriteLine("""Trigger"",""Mode"",""Text"",""Device"",""Action""")
        ''Loop through all possible triggers
        For i = 1 To 200
            TriggerName = "Trigger" & i.ToString.Trim
            aText = hs.GetINISetting(TriggerName, "AlexaText", "", "Jon00Alexa.ini")
            mText = aText.Replace("||", "|").Split(ToChar("|"))
            aName = Format(i, "00") & " " & hs.GetINISetting(TriggerName, "Name", "", "Jon00Alexa.ini")
            aMode = hs.GetINISetting(TriggerName, "Mode", "", "Jon00Alexa.ini")
            aDevice = hs.GetINISetting(TriggerName, "Device", "", "Jon00Alexa.ini")
            tAction = ""
            aAction = ""
            s = hs.GetINISetting(TriggerName, "RunEvent", "", "Jon00Alexa.ini")
            If s.Length > 0 Then
                tAction = "Event= "
                aAction = s
            Else
                s = hs.GetINISetting(TriggerName, "RunScript", "", "Jon00Alexa.ini")
                If s.Length > 0 Then
                    tAction = "Script= "
                    aAction = s
                Else
                    s = hs.GetINISetting(TriggerName, "CAPI", "", "Jon00Alexa.ini")
                    If s.Length > 0 Then
                        tAction = "CAPI= "
                        aAction = s.Replace(",", "'")
                    End If
                End If
            End If



            If aText.Length > 1 Then
                For Each s In mText
                    sw.WriteLine(aName & "," & aMode & "," & s & "," & aDevice & "," & tAction & aAction)
                    aName = ""
                    aMode = ""
                    aAction = ""
                    tAction = ""
                    aDevice = ""
                Next s
            End If
        Next
        sw.Close()
    End Sub
End Class


Public Class SendToAlexa
    Public Sub Main(ByVal Parms As Object)
        ''
        '' Create Notifications for Alexa Speakers
        ''
        Dim ParmArray(10) As String
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If
        Dim Key As String = "amzn1.ask.account.AFGTGQWJWV7U7QA6FYWRNMCOSON3HQ2UMARJATSSQH6ZES22AQFF54XTMQQXPAJJOJ753TFERFHWNUSOGW37VRMDJYGHHBXEZY3MATXIM6NGV4BLVMS5ABPVNICO3S5LWLSOCF5URVQFSAGMOQULQOH34Y6QSIWM4XPKFL6HAK4AZR4RL2247U55O6DQPRHCHKNGD23T2ET7MKY"
        Dim Notification = ParmArray(0)
        Dim host As String = "https://api.notifymyecho.com"
        Dim path As String = "/v1/NotifyMe?notification=" & Notification & "&accessCode=" & Key
        Dim r As String
        r = hs.GetURL(host, path, True, 443)
        hs.WriteLog("SendToAlexa", r)
    End Sub
End Class

Public Class HsTouchParms
    Public Sub Main(ByVal ParmArray() As String)
        '' Retrieve parameters
        '' Parms(0) Device ID
        '' Parms(1) String representing action type (i.e. inc or dec)
        '' Parms(2) Integer indicating incremental step (i.e. 1, 2, 5 etc) 
        Dim strDeviceRef As String
        Dim strAction As String
        Dim strIncrement As String
        strDeviceRef = ParmArray(0)
        strAction = ParmArray(1)
        strIncrement = ParmArray(2)
    End Sub
End Class

Public Class ShutDownHS
    Public Sub Main(ByVal Parms As Object)
        Dim ParmArray(10) As String
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If

        hs.PluginFunction("BLShutdown", "", "Shutdown", New Object() {2})

    End Sub
End Class


Public Class Startup
    ' This is the startup script
    ' It is run once when HomeSeer starts up
    ' 
    '
    Public Sub Main(Parm As Object)

        hs.WriteLog("Startup", "(Startup.vb script) Scripting is OK and is now running Startup.vb")

        'Dim SpeakClients As String = ""
        'SpeakClients = hs.GetInstanceList
        'If SpeakClients Is Nothing Then SpeakClients = ""
        'If String.IsNullOrEmpty(SpeakClients.Trim) Then
        '    hs.WriteLog("Startup", "(Startup.vb script) No speaker clients detected, waiting up to 30 seconds for any to connect...")
        '    ' There are no connected speaker clients, so let's wait 30 seconds
        '    '	(which is the default re-connect interval for a speaker client)
        '    '	to see if we can get one connected, otherwise the two speak
        '    '	commands below will not be heard.
        '    hs.WaitSecs(1)
        '    Dim Start As Date = Now()
        '    Do
        '        SpeakClients = hs.GetInstanceList
        '        If SpeakClients Is Nothing Then SpeakClients = ""
        '        If String.IsNullOrEmpty(SpeakClients.Trim) Then
        '            hs.WaitSecs(1)
        '        Else
        '            Exit Do
        '        End If
        '    Loop Until Now.Subtract(Start).TotalSeconds > 30
        'End If


        '' Speak - comment the next line if you do not want HomeSeer to speak at startup.
        'hs.Speak("Welcome to Home-Seer", True)

        '' speak the port the web server is running on
        'Dim port As String = hs.GetINISetting("Settings", "gWebSvrPort", "")
        'If port <> "80" Then
        '    hs.Speak("Web server port number is " & port)
        'End If


        ' You may add your own commands to this script.
        ' See the scripting section of the HomeSeer help system for more information.
        ' You may access help by going to your HomeSeer website and clicking the HELP button,
        ' or by pointing your browser to the /help page of your HomeSeer system.

        '' Initialize HS Variables Used by Several Scripts
        Dim er As String
        er = hs.CreateVar("RoombaStatusList")
        hs.WriteLog("Startup", "(Startup.vb script) HS variable RoombaStatusList created")
        '' Initialize for Jon00 Links
        hs.RunScriptFunc("Jon00LinksHS4.vben", "Main", "", False, False)

        '' Initialize for Jon00 Tiles
        hs.RunScriptFunc("Jon00HSTile.vben", "Startup", "", True, True)

    End Sub
End Class


Public Class TryPoll
    Public Sub Main(ByVal Parms As Object)
        Dim ParmArray(10) As String
        ' Dim pl As HomeSeerAPI.IPlugInAPI.PollResultInfo
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If

        Dim PollResult As New HomeSeerAPI.IPlugInAPI.PollResultInfo
    End Sub
End Class
Public Class BsmtVents
    Public Sub Main(ByVal Parms As Object)
        BedroomOpen("")
    End Sub
    Public Sub BedroomOpen(ByVal Parms As Object)
        OpenVent("JowiHue JowiHue Vent Bdrm W (On/Off/Dim)")
        OpenVent("JowiHue JowiHue Vent Bdrm E (On/Off/Dim)")
        OpenVent("JowiHue JowiHue Vent Bath (On/Off/Dim)")
    End Sub
    Public Sub BedroomClose(ByVal Parms As Object)
        CloseVent("JowiHue JowiHue Vent Bdrm W (On/Off/Dim)")
        CloseVent("JowiHue JowiHue Vent Bdrm E (On/Off/Dim)")
        CloseVent("JowiHue JowiHue Vent Bath (On/Off/Dim)")
    End Sub
    Public Sub MainOpen(ByVal Parms As Object)
        OpenVent("JowiHue JowiHue Vent Main NW (On/Off/Dim)")
        OpenVent("JowiHue JowiHue Vent Main NC (On/Off/Dim)")
        OpenVent("JowiHue JowiHue Vent Main NE (On/Off/Dim)")
        OpenVent("JowiHue JowiHue Vent Main SW (On/Off/Dim)")
        OpenVent("JowiHue JowiHue Vent Main SE (On/Off/Dim)")
    End Sub
    Public Sub MainClose(ByVal Parms As Object)
        CloseVent("JowiHue JowiHue Vent Main NW (On/Off/Dim)")
        CloseVent("JowiHue JowiHue Vent Main NC (On/Off/Dim)")
        CloseVent("JowiHue JowiHue Vent Main NE (On/Off/Dim)")
        CloseVent("JowiHue JowiHue Vent Main SW (On/Off/Dim)")
        CloseVent("JowiHue JowiHue Vent Main SE (On/Off/Dim)")
    End Sub
    Public Sub SewingOpen(ByVal Parms As Object)
        ' FlashDevice("Sewing Room Fan")
        OpenVent("JowiHue JowiHue Vent Sewing E (On/Off/Dim)")
        OpenVent("JowiHue JowiHue Vent Sewing W (On/Off/Dim)")
    End Sub
    Public Sub SewingClose(ByVal Parms As Object)
        'FlashDevice("Sewing Room Fan")
        CloseVent("JowiHue JowiHue Vent Sewing E (On/Off/Dim)")
        CloseVent("JowiHue JowiHue Vent Sewing W (On/Off/Dim)")
    End Sub
    Public Sub AllClose(ByVal Parms As Object)
        BedroomClose("")
        MainClose("")
        SewingClose("")
    End Sub
    Private Sub OpenVent(VentName As String)
        Dim cc As HomeSeerAPI.CAPI.CAPIControl
        Dim cr As HomeSeerAPI.CAPI.CAPIControlResponse
        cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName(VentName), True, "(value)%", False, False)
        cc.ControlValue = 99
        cr = hs.CAPIControlHandler(cc)
        hs.WaitSecs(1)
    End Sub
    Private Sub CloseVent(VentName As String)
        Dim cc As HomeSeerAPI.CAPI.CAPIControl
        Dim cr As HomeSeerAPI.CAPI.CAPIControlResponse
        cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName(VentName), True, "(value)%", False, False)
        cc.ControlValue = 1
        cr = hs.CAPIControlHandler(cc)
        hs.WaitSecs(1)
    End Sub
End Class

Public Class TestRoombaPlugin
    Public Sub Main(ByVal Parms As Object)
        Dim ParmArray(10) As String
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If

        Dim s As String = hs.CAPIGetStatus(hs.GetDeviceRefByName("BLRoombaWifi Roomba")).Status

    End Sub
End Class


Public Class SaveHs4Devices
    Public Sub Main(ByVal Parms As Object)
        ''
        '' Extract Parms --> ParmArray
        ''
        Dim ParmArray(15) As String
        '' Determine if Parms is a single String, or an Array of Strings
        If Parms.GetType().ToString = "System.String" Then
            '' Single String from HS scripting engine. Extract parms
            ParmArray(0) = ""
            If Parms.ToString.Contains("|") Then
                ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
            Else
                ParmArray(0) = Parms.ToString
            End If
        Else
            '' Array of strings from HsTouch
            ParmArray = CType(Parms, String())
        End If
        '' ParmArray() now contains one or more passed parameters

        Dim Folder As String
        Dim FileName As String
        Dim aFile As System.IO.StreamWriter
        Dim DevRefs As List(Of Integer)
        Dim aDeviceEntry As DeviceEntry
        Dim AllDevices As New Dictionary(Of String, Integer)   'used to check for duplicates
        Dim CombinedName As String
        Dim IsThisDuplicate As Integer
        Dim NumberOfRecords As Integer
        Dim OutputRecords As New SortedDictionary(Of Integer, String)   'records to be written to CSV file
        Dim OutputRecord As String
        Dim aRecord(6) As String
        Dim s1 As String
        Dim ref2 As Integer
        NumberOfRecords = 0
        hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Script Started", "SaveHs4Devices")
        '' Get output folder from script parameters
        If ParmArray(0).Length < 3 Then
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Invalid script parameter, Folder: " & ParmArray(0), "SaveHs4Devices")
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "__terminating ....", "SaveHs4Devices")
            Exit Sub
        Else
            Folder = ParmArray(0)
        End If
        '' Create Output File
        If Folder.Substring(Folder.Length - 1, 1) = "/" Or Folder.Substring(Folder.Length - 1, 1) = "\" Then
            '' separator already there
        Else
            '' add separator
            Folder = Folder + "\"
        End If
        FileName = Folder & "SavedHs4Devices Created  " & Format(Now, "yyyyMMdd") & "  " & Format(Now, "HHmmss") & ".csv"
        If Not IO.Directory.Exists(Folder) Then
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Folder " & Folder & " does not exist, terminating", "SaveHs4Devices")
            Exit Sub
        End If
        aFile = My.Computer.FileSystem.OpenTextFileWriter(FileName, False)
        aFile.WriteLine("DevRef,IsFeature,DeviceName,FeatureName,IsDuplicate,Address,Interface")   'CSV header
        '' Get all of the HS Devices and Features
        DevRefs = hs4.GetAllRefs
        For Each ref As Integer In DevRefs
            aDeviceEntry = BuildDeviceEntry(ref)
            '' Check to see if this is a duplicate 
            CombinedName = aDeviceEntry.DeviceName.Trim & "~" & aDeviceEntry.FeatureName.Trim
            If AllDevices.Keys.Contains(CombinedName) Then
                '' This is a duplicate
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Warning, "Duplicate: " & CombinedName & "(" & ref.ToString & ")", "SaveHs4Devices")
                IsThisDuplicate = 1
                '' Find previous duplicate and mark it as such
                ref2 = AllDevices(CombinedName)
                aRecord = OutputRecords(ref2).Split(Convert.ToChar(","))   'get the prior duplicate
                OutputRecords.Remove(ref2)  'remove it
                aRecord(4) = "1"   'flag the replacement as a duplicate
                s1 = ""
                For i As Integer = 0 To 5
                    s1 = s1 & aRecord(i) & ","
                Next
                s1 = s1 & aRecord(6)
                OutputRecords.Add(ref2, s1)   'add the changed record back
            Else
                '' Not a duplicate
                AllDevices.Add(CombinedName, ref)
                IsThisDuplicate = 0
            End If
            '' Save output record
            NumberOfRecords = NumberOfRecords + 1
            OutputRecord = Format(aDeviceEntry.DevRef, "00000") & "," & aDeviceEntry.IsFeature.ToString & "," & aDeviceEntry.DeviceName.ToString & "," & aDeviceEntry.FeatureName.ToString & "," & IsThisDuplicate.ToString & "," & aDeviceEntry.Address & "," & aDeviceEntry.PluginInterface
            OutputRecords.Add(ref, OutputRecord)
        Next
        '' Output the saved records to the CSV file
        For Each kvp As KeyValuePair(Of Integer, String) In OutputRecords
            aFile.WriteLine(kvp.Value)
        Next
        aFile.Close()
        hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Processed " & NumberOfRecords.ToString & " Devices/Features and saved to file " & FileName, "SaveHs4Devices")
        hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Script Ended", "SaveHs4Devices")
    End Sub
    Private Function BuildDeviceEntry(DevRef As Int32) As DeviceEntry
        Dim de As DeviceEntry
        Dim FullName As String
        Dim hAssociatedDevices() As Integer
        Dim DevRef2 As Int32
        Dim aName, aLocation, aLocation2, aAddress As String, aRelationship As HomeSeer.PluginSdk.Devices.Identification.ERelationship
        Dim aPluginInterface As String
        Dim IsDevice As Boolean
        Dim aDev As Scheduler.Classes.DeviceClass
        '' New DeviceEntry
        de = New DeviceEntry
        de.DeviceName = ""
        de.DevRef = 0
        de.FeatureName = ""
        de.IsFeature = 0
        de.Address = ""
        de.PluginInterface = ""
        '' Get Device/Feature info for this one
        IsDevice = hs4.IsRefDevice(DevRef)
        de.DevRef = DevRef
        aName = hs4.GetPropertyByRef(DevRef, HomeSeer.PluginSdk.Devices.EProperty.Name).ToString
        aLocation = hs4.GetPropertyByRef(DevRef, HomeSeer.PluginSdk.Devices.EProperty.Location).ToString
        aLocation2 = hs4.GetPropertyByRef(DevRef, HomeSeer.PluginSdk.Devices.EProperty.Location2).ToString
        aAddress = hs4.GetPropertyByRef(DevRef, HomeSeer.PluginSdk.Devices.EProperty.Address).ToString
        FullName = (aLocation2 & " " & aLocation & " " & aName).Trim
        aRelationship = CType(hs4.GetPropertyByRef(DevRef, HomeSeer.PluginSdk.Devices.EProperty.Relationship), HomeSeer.PluginSdk.Devices.Identification.ERelationship)
        aPluginInterface = hs4.GetPropertyByRef(DevRef, HomeSeer.PluginSdk.Devices.EProperty.Interface).ToString
        If IsDevice Then
            '' Device
            de.IsFeature = 0
            de.DeviceName = FullName
            de.FeatureName = ""
        Else
            '' Feature
            de.IsFeature = 1
            de.FeatureName = FullName
            '' Get the parent device
            Dim deviceClass As Classes.DeviceClass = CType(hs.GetDeviceByRef(DevRef), Scheduler.Classes.DeviceClass)
            aDev = deviceClass
            hAssociatedDevices = aDev.AssociatedDevices(hs)
            If hAssociatedDevices.Count <> 1 Then
                '' Associations screwed up, feature should have exactly one association
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "****** Feature has " & hAssociatedDevices.Count.ToString & " Associations", "SaveHs4Devices")
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "__should always be 1 for a Feature", "SaveHs4Devices")
                Return de
            Else
                '' Good association, get the parent
                DevRef2 = hAssociatedDevices(0)
                aName = hs4.GetPropertyByRef(DevRef2, HomeSeer.PluginSdk.Devices.EProperty.Name).ToString
                aLocation = hs4.GetPropertyByRef(DevRef2, HomeSeer.PluginSdk.Devices.EProperty.Location).ToString
                aLocation2 = hs4.GetPropertyByRef(DevRef2, HomeSeer.PluginSdk.Devices.EProperty.Location2).ToString
                FullName = (aLocation2 & " " & aLocation & " " & aName).Trim
                de.DeviceName = FullName
            End If
        End If
        de.Address = aAddress
        de.PluginInterface = aPluginInterface
        Return de
    End Function
    Structure DeviceEntry
        Public DevRef As Integer
        Public IsFeature As Integer
        Public DeviceName As String
        Public FeatureName As String
        Public Address As String
        Public PluginInterface As String
    End Structure
End Class

Public Class CompareHsMethods
    Public Sub Main(ByVal Parms As Object)
        Dim ParmArray(10) As String
        '' Extract script parameters into ParmArray()
        ParmArray(0) = ""
        If Parms.ToString.Contains("|") Then
            ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
        Else
            ParmArray(0) = Parms.ToString
        End If

        Dim anAssembly As System.Reflection.Assembly
        Dim modules() As System.Reflection.Module
        Dim aModule As [Module]
        Dim types() As Type
        Dim methodinfos() As MethodInfo
        Dim DuplicateCheck As New Dictionary(Of String, String)
        Dim Duplicates As New Dictionary(Of String, String)
        Dim pis() As ParameterInfo
        Dim s1, s2 As String

        '' Get all hs Methods
        anAssembly = System.Reflection.Assembly.GetAssembly(GetType(IHSApplication))
        modules = anAssembly.GetLoadedModules()
        aModule = modules(0)
        types = aModule.GetTypes()
        For Each t As Type In types
            methodinfos = t.GetMethods
            For Each m As MethodInfo In methodinfos
                If m.IsPublic And m.ReflectedType.ToString.Trim.ToUpper = "HOMESEERAPI.IHSAPPLICATION" Then
                    'Console.WriteLine(m.Name & " " & m.ReflectedType.ToString)
                    pis = m.GetParameters()
                    s1 = "hs= "
                    For Each pi As ParameterInfo In pis
                        s1 = s1 & pi.Name & "(" & pi.ParameterType.Name & ")~"
                    Next
                    s1 = s1 & " R=" & m.ReturnType.ToString
                    DuplicateCheck.Add(m.Name, s1)

                End If
            Next
        Next


        '' Get all hs4 Methods
        anAssembly = System.Reflection.Assembly.GetAssembly(GetType(HomeSeer.PluginSdk.IHsController))
        modules = anAssembly.GetLoadedModules()
        aModule = modules(0)
        types = aModule.GetTypes()
        For Each t As Type In types
            methodinfos = t.GetMethods
            For Each m As MethodInfo In methodinfos
                If m.IsPublic And m.ReflectedType.ToString.Trim.ToUpper = "HomeSeer.PluginSdk.IHsController".ToUpper Then
                    'If m.IsPublic Then
                    'Console.WriteLine(m.Name & " " & m.ReflectedType.ToString)

                    pis = m.GetParameters()
                    s1 = "hs4= "
                    For Each pi As ParameterInfo In pis
                        s1 = s1 & pi.Name & "(" & pi.ParameterType.Name & ")~"
                    Next
                    s1 = s1 & " R=" & m.ReturnType.ToString


                    If DuplicateCheck.Keys.Contains(m.Name) Then
                        s2 = DuplicateCheck(m.Name) & "   " & s1
                        Duplicates.Add(m.Name, s2)
                        Console.WriteLine(m.Name & "= " & s2)
                    Else
                        DuplicateCheck.Add(m.Name, s1)
                    End If

                End If
            Next
        Next


    End Sub
End Class


Public Class AlexaSpeak
    Public Sub Main(ByVal Parms As Object)
        '' Extract Parms --> ParmArray
        Dim ParmArray(0) As String
        '' Determine if Parms is a single String, or an Array of Strings
        If Parms.GetType().ToString = "System.String" Then
            '' Single String from HS scripting engine. Extract parms
            ParmArray(0) = ""
            If Parms.ToString.Contains("|") Then
                ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
            Else
                ParmArray(0) = Parms.ToString
            End If
        Else
            '' Array of strings from HsTouch
            ParmArray = CType(Parms, String())
        End If
        '' ParmArray() now contains one or more passed parameters

        ''  AlexaSpeak uses Alexa Skill VoiceMonkey to send text to Echo speakers
        '
        ' Parameters:
        '  (0) Alexa speaker used to speak, valid:   Kitchen, Office   (any case)
        '  (1) Text to be spoken.  
        '          ie:   "Kitchen|this is a test"
        '
        Dim s1, sUrl, sToken, sMonkey, sPrefix, sText As String
        sToken = "ce6c55428881e89b0f2aca2b8022889e_2509b10986f21e47f1f5978faf48c9c5"
        sMonkey = "monkey" & ParmArray(0).ToLower
        sPrefix = "https://api-v2.voicemonkey.io/announcement?token="
        sText = ParmArray(1).Replace(" ", "%20")
        sUrl = sPrefix & sToken & "&device=" & sMonkey & "&text=" & sText
        s1 = WebRequest(sUrl)
        If Not s1.Contains("success") Then
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Bad return from WebRequest:", "AlexaSpeak")
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "  " & s1, "AlexaSpeak")
        End If
    End Sub
    Friend Function WebRequest(ByVal sURL As String) As String
        Dim strData As String = ""
        Dim tlRequest As System.Net.HttpWebRequest
        Dim tlResponse As System.Net.HttpWebResponse
        Dim tlStatusCode As Integer
        Try
            tlRequest = CType(System.Net.WebRequest.Create(sURL), System.Net.HttpWebRequest)
            tlRequest.ContentType = "text/html"
            tlRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"
            tlRequest.Method = "GET"
            tlRequest.Timeout = 10000
            tlRequest.AllowAutoRedirect = True
            tlResponse = CType(tlRequest.GetResponse(), System.Net.HttpWebResponse)
            tlStatusCode = tlResponse.StatusCode
            If tlStatusCode = System.Net.HttpStatusCode.OK Then
                strData = New System.IO.StreamReader(tlResponse.GetResponseStream()).ReadToEnd()
            Else
                Return "WebRequest Failed"
            End If
            tlResponse.Close()
        Catch ex As Exception
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "HTTP Request Exception: " & ex.ToString, "AlexaSpeak")
        End Try
        Return strData
    End Function
    Public Sub TestIt(P As Object)
        '' Speak test message from Office Echo
        Main("Office|test of Alexa Speak")   'use VoiceMonkey to speak test phrase in Kitchen
    End Sub

End Class

Public Class RestartKitchenRoku
    Public Sub Main(ByVal Parms As Object)
        ''
        '' Extract Parms --> ParmArray
        ''
        Dim ParmArray(0) As String
        '' Determine if Parms is a single String, or an Array of Strings
        If Parms.GetType().ToString = "System.String" Then
            '' Single String from HS scripting engine. Extract parms
            ParmArray(0) = ""
            If Parms.ToString.Contains("|") Then
                ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
            Else
                ParmArray(0) = Parms.ToString
            End If
        Else
            '' Array of strings from HsTouch
            ParmArray = CType(Parms, String())
        End If
        '' ParmArray() now contains one or more passed parameters

        '
        ' This script will respond to Kitchen button to restart the Kitchen TV Roku device
        '
        Dim devRoku As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Kitchen TV", "JowiHue JowiHue Kitchen TV")
        Dim rc As Boolean
        rc = hs4.SendControlForFeatureByValue(devRoku, 0)   'power down Roku
        hs.RunScriptFunc("AlexaSpeak.vb", "Main", "Kitchen|Kitchen Roku is being restarted, please wait", False, True)
        hs.WaitSecs(10)
        rc = hs4.SendControlForFeatureByValue(devRoku, 255)   'power up Roku

    End Sub
End Class

Public Class PushIt
    Public Sub Main(ByVal Parms As Object)
        ''
        '' Extract Parms --> ParmArray
        ''
        Dim ParmArray(0) As String
        '' Determine if Parms is a single String, or an Array of Strings
        If Parms.GetType().ToString = "System.String" Then
            '' Single String from HS scripting engine. Extract parms
            ParmArray(0) = ""
            If Parms.ToString.Contains("|") Then
                ParmArray = Parms.ToString.Split(Convert.ToChar("|"))
            Else
                ParmArray(0) = Parms.ToString
            End If
        Else
            '' Array of strings from HsTouch
            ParmArray = CType(Parms, String())
        End If
        '' ParmArray() now contains one or more passed parameters
        '
        '' Parameters:  Destination (Joe or Marilyn) | Subject | Text
        Dim Destination As String
        Select Case ParmArray(0).ToUpper
            Case "JOE"
                Destination = "EjtPhone"
            Case "MARILYN"
                Destination = "EjtPhone"
            Case Else
                Destination = "EjtPhone"
        End Select

        hs.PluginFunction("Pushover", "", "SendMessage", New Object() {Destination, Destination, "", ParmArray(2), ParmArray(1)})

    End Sub
End Class

Public Class Xmas

    ' Requires tenGlobalMethods.dll
    '     https://tenholder.net/tenWare2/ScriptingExtensions/Overview.aspx
    Public Sub IndoorON(ByVal Parms As Object)
        ''
        ''  Indoor Xmas Lights ON
        ''
        '' Get Device Refs
        Dim IndoorLink As Integer = GetFeatureRefByFullname(hs, "AllInddorXmasLightsLink", "")
        Dim FoyerPlug As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Rout Foyer", "JowiHue JowiHue Rout Foyer On/Off")
        Dim Plug03 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug03", "JowiHue JowiHue Plug03 Plug")
        Dim Plug04 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug04", "JowiHue JowiHue Plug04 Plug")
        Dim OutdoorLink As Integer = GetFeatureRefByFullname(hs, "AllOutdoorXmasLightsLink", "")
        Dim Plug05 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug05", "JowiHue JowiHue Plug05 Plug")
        '
        Dim rc As Boolean
        rc = hs4.SendControlForFeatureByValue(IndoorLink, -4)
        rc = hs4.SendControlForFeatureByValue(FoyerPlug, 255)
        rc = hs4.SendControlForFeatureByValue(Plug03, 255)
        rc = hs4.SendControlForFeatureByValue(Plug04, 255)
        rc = hs4.SendControlForFeatureByValue(Plug05, 255)
    End Sub
    Public Sub IndoorOFF(ByVal Parms As Object)
        ''
        ''  Indoor Xmas Lights OFF
        ''
        '' Get Device Refs
        Dim IndoorLink As Integer = GetFeatureRefByFullname(hs, "AllInddorXmasLightsLink", "")
        Dim FoyerPlug As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Rout Foyer", "JowiHue JowiHue Rout Foyer On/Off")
        Dim Plug03 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug03", "JowiHue JowiHue Plug03 Plug")
        Dim Plug04 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug04", "JowiHue JowiHue Plug04 Plug")
        Dim OutdoorLink As Integer = GetFeatureRefByFullname(hs, "AllOutdoorXmasLightsLink", "")
        Dim Plug05 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug05", "JowiHue JowiHue Plug05 Plug")
        '
        Dim rc As Boolean
        rc = hs4.SendControlForFeatureByValue(IndoorLink, -3)
        rc = hs4.SendControlForFeatureByValue(FoyerPlug, 0)
        rc = hs4.SendControlForFeatureByValue(Plug03, 0)
        rc = hs4.SendControlForFeatureByValue(Plug04, 0)
        rc = hs4.SendControlForFeatureByValue(Plug05, 0)
    End Sub
    Public Sub OutdoorON(ByVal parms As Object)
        ''
        ''  Outdoor Xmas Lights ON
        ''
        '' Get Device Refs
        Dim IndoorLink As Integer = GetFeatureRefByFullname(hs, "AllInddorXmasLightsLink", "")
        Dim FoyerPlug As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Rout Foyer", "JowiHue JowiHue Rout Foyer On/Off")
        Dim Plug03 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug03", "JowiHue JowiHue Plug03 Plug")
        Dim Plug04 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug04", "JowiHue JowiHue Plug04 Plug")
        Dim OutdoorLink As Integer = GetFeatureRefByFullname(hs, "AllOutdoorXmasLightsLink", "")
        Dim Plug05 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug05", "JowiHue JowiHue Plug05 Plug")
        '
        Dim rc As Boolean
        rc = hs4.SendControlForFeatureByValue(OutdoorLink, -4)
    End Sub

    Public Sub OutdoorOFF(ByVal parm As Object)
        ''
        '' Outdoor Xmas Lights OFF
        ''
        '' Get Device Refs
        Dim IndoorLink As Integer = GetFeatureRefByFullname(hs, "AllInddorXmasLightsLink", "")
        Dim FoyerPlug As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Rout Foyer", "JowiHue JowiHue Rout Foyer On/Off")
        Dim Plug03 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug03", "JowiHue JowiHue Plug03 Plug")
        Dim Plug04 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug04", "JowiHue JowiHue Plug04 Plug")
        Dim OutdoorLink As Integer = GetFeatureRefByFullname(hs, "AllOutdoorXmasLightsLink", "")
        Dim Plug05 As Integer = GetFeatureRefByFullname(hs, "JowiHue JowiHue Plug05", "JowiHue JowiHue Plug05 Plug")
        '
        Dim rc As Boolean
        rc = hs4.SendControlForFeatureByValue(OutdoorLink, -3)

    End Sub
    Public Sub AllON(ByVal parms As Object)
        ''
        ''  All Xmas Lights ON
        ''
        IndoorON(Nothing)
        OutdoorON(Nothing)

    End Sub
    Public Sub AllOFF(ByVal parms As Object)
        ''
        ''  All Xmas Lights OFF
        ''
        IndoorOFF(Nothing)
        OutdoorOFF(Nothing)
    End Sub
End Class

Public Class UPBSaveAndRestore
    Const RoombaHsVar As String = "RoombaStatusList"
    Public Sub Main(ByVal Parms As Object)    ' Only used for tenScripting testing
        Dim s As String
        'Dim d As Dictionary(Of String, String)
        's = AlexaRoombaStart("start roomba|#")
        'd = GetStatus(RoombaHsVar)
        's = AlexaRoombaNextRoom("great room|#")
        'd = GetStatus(RoombaHsVar)
        s = AlexaRoombaNextRoom("kitchen|#")
        'd = GetStatus(RoombaHsVar)
        's = AlexaRoombaNextRoom("hallway|#")
        'd = GetStatus(RoombaHsVar)
        's = s
        'd = GetStatus(RoombaHsVar)
        's = AlexaRoombaStop("")
        's = s
    End Sub
    ''
    ''  AlexaRoombaStart      Is called at the beginning of a Roomba run to initialize things.  Then, Alexa calls AlexaRoombaNextRoom for each room
    ''
    ''                      This includes all of the lights that are on a path from Roomba's Home to the room to be vacuumed
    ''                     This routine is called once for each room to be vacuumed based upon multiple voice commands
    ''
    Public Function AlexaRoombaStart(AlexaRequest As String) As String
        Dim StatusList As New Dictionary(Of String, String)     ' Name:CapiStatus
        '' Initialize everything for multiple Room requests
        hs.WriteLog("RoombaStart", "Input:" & AlexaRequest)    'log request from Alexa Helper
        StatusList = New Dictionary(Of String, String)
        StatusList.Clear()
        SaveStatus(RoombaHsVar, StatusList)
        '' Tell Alexa what to say
        Return "Say a room to clean or just say FINISHED?"
    End Function

    Public Function AlexaRoombaNextRoom(AlexaRequest As String) As String
        ''
        '' Called when Alexa Helper processes each room to be cleaned
        ''
        ''     AlexaRequest:   Sent from AlexaHelper in form    spokentext|extractednumber
        ''
        Dim parms() As String, Spoken As String
        Dim Words() As String
        Dim StatusList As New Dictionary(Of String, String)
        hs.WriteLog("Roomba Start", "Input:" & AlexaRequest)    'log request from Alexa Helper
        parms = AlexaRequest.Split(ToChar("|"))   'extract parameters from Alexa Helper
        Spoken = parms(0).ToUpper.Trim.ToUpper   'what Alexa heard
        'theNumber = parms(1).ToUpper.Trim   'extracted number from spoken string
        Words = Spoken.Split(ToChar(" "))   'list of words in spoken text

        ''''' FINISHED or NO  or  NONE or DONE
        If Spoken = "NO" Or Spoken = "NONE" Or Spoken = "DONE" Or Spoken = "FINISHED" Then
            '' NO, do not want another request
            Return "ok, you are now ready to run roomba to clean your floors"   'without trailing "?", Alexa Helper will terminate processing
        End If

        ''''' KITCHEN
        If Words.Contains("KITCHEN") Then
            '' Prepare to clean Kitchen
            GreatRoom()
            Kitchen()
            Return "ready to clean kitchen. tell me another room name or say FINISHED?"
        End If

        ''''' GREAT ROOM
        If Words.Contains("GREAT") Then
            '' Prepare to clean Great Room
            GreatRoom()
            Return "ready to clean Great Room, tell me another room name or say FINISHED?"
        End If

        ''''' Hallway
        If Words.Contains("HALLWAY") Or Words.Contains("HALL") Then
            GreatRoom()
            HallWay()
            DiningRoom()
            Foyer()
            Return "ready to clean hallway. tell me another room name or say FINISHED?"
        End If

        ''''' Foyer
        If Words.Contains("ENTRY") Or Words.Contains("FOYER") Or Words.Contains("ENTRYWAY") Then
            GreatRoom()
            DiningRoom()
            Foyer()
            Return "ready to clean the foyer. tell me another room name or say FINISHED?"
        End If

        ''''' Dining Room
        If Words.Contains("DINING") Or Words.Contains("DININGROOM") Then
            GreatRoom()
            DiningRoom()
            Return "ready to clean the dining room. tell me another room name or say FINISHED?"
        End If

        ''''' Office
        If Words.Contains("OFFICE") Then
            GreatRoom()
            HallWay()
            DiningRoom()
            Foyer()
            Office()
            Return "ready to clean the office. tell me another room name or say FINISHED?"
        End If

        ''''' Front Bedroom
        If Words.Contains("FRONT") Then
            GreatRoom()
            DiningRoom()
            Foyer()
            HallWay()
            FrontBedroom()
            Return "ready to clean front bedroom. tell me another room name or say FINISHED?"
        End If

        ''''' Middle Bedroom
        If Words.Contains("MIDDLE") Then
            GreatRoom()
            DiningRoom()
            Foyer()
            HallWay()
            MiddleBedroom()
            Return "ready to clean middle bedroom. tell me another room name or say FINISHED?"
        End If

        ''''' Jack and Jill Bath
        If Words.Contains("JACK") Or Words.Contains("JILL") Then
            GreatRoom()
            DiningRoom()
            Foyer()
            HallWay()
            MiddleBedroom()
            FrontBedroom()
            JackJill()
            Return "ready to clean jack and jill bath. tell me another room name or say FINISHED?"
        End If

        ''''' Guest/Hall Bath
        If (Words.Contains("GUEST") Or Words.Contains("HALL") Or Words.Contains("HALLWAY")) And (Words.Contains("BATH") Or Words.Contains("BATHROOM")) Then
            GreatRoom()
            DiningRoom()
            Foyer()
            HallWay()
            GuestBath()
            Return "ready to clean the hall bath room. tell me another room name or say FINISHED?"
        End If

        ''''' Master Bedroom
        If (Words.Contains("MASTER")) And (Words.Contains("BED") Or Words.Contains("BEDROOM")) Then
            GreatRoom()
            DiningRoom()
            Foyer()
            HallWay()
            MasterBedroom()
            Return "ready to clean the master bed room. tell me another room name or say FINISHED?"
        End If

        ''''' Master Bath
        If (Words.Contains("MASTER")) And (Words.Contains("BATH") Or Words.Contains("BATHROOM")) Then
            GreatRoom()
            DiningRoom()
            Foyer()
            HallWay()
            MasterBedroom()   'this turns both bedroom and bath on
            Return "ready to clean the master bath room. tell me another room name or say FINISHED?"
        End If

        ''''' Sunroom
        If Words.Contains("SUN") Or Words.Contains("SON") Or Words.Contains("MISSOURI") Or Words.Contains("SUNROOM") Then
            GreatRoom()
            Kitchen()
            SunRoom()
            Return "ready to clean the missouri room. tell me another room name or say FINISHED?"
        End If

        ''''' Laundry
        If Words.Contains("LAUNDRY") Or Words.Contains("LAUNDRYROOM") Then
            GreatRoom()
            Kitchen()
            Laundry()
            Return "ready to clean the laundry room. tell me another room name or say FINISHED?"
        End If

        ''''' Found No Matches
        Return "i did not understand that. tell me another room name or say FINISHED?"
    End Function
    Friend Sub Laundry()
        '' Turn ON Laundry Room Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Laundry Ceiling Link")   'retrieve current status of Laundry Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Laundry Ceiling Link"), True, "Snap On", False, False))   'turn Laundry Lights ON
    End Sub
    Friend Sub SunRoom()
        '' Turn ON Sunroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("MissouriRoomLights")   'retrieve current status of Sunroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("MissouriRoomLights"), True, "Snap On", False, False))   'turn Sunroom Lights ON
    End Sub
    Friend Sub MasterBedroom()
        '' Turn ON Master Bedroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Master Bedroom All OFF")   'retrieve current status of Master Bedroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bedroom All OFF"), True, "Snap On", False, False))   'turn Master Bedroom Lights ON
    End Sub
    Friend Sub GuestBath()
        '' Turn ON Guest Bath Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Guest Bathroom Light Link")   'retrieve current status of Guest Bath Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Guest Bathroom Light Link"), True, "Snap On", False, False))   'turn Guest Bath Lights ON
    End Sub
    Friend Sub JackJill()
        '' Turn ON Jack and Jill Bath Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("JandJ Roomba Link")   'retrieve current status of Jack and Jill Bath Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("JandJ ALL Link"), True, "Snap On", False, False))   'turn Jack and Jill Bath Lights ON
    End Sub
    Friend Sub FrontBedroom()
        '' Turn ON Front Bedroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Front Bedroom ALL Link")   'retrieve current status of Front Bedroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Front Bedroom ALL Link"), True, "Snap On", False, False))   'turn Front Bedroom Lights ON
    End Sub
    Friend Sub MiddleBedroom()
        '' Turn ON Middle Bedroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Middle Bedroom ALL Link")   'retrieve current status of Middle Bedroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Middle Bedroom ALL Link"), True, "Snap On", False, False))   'turn Middle Bedroom Lights ON
    End Sub
    Friend Sub Kitchen()
        '' Turn ON Kitchen Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Kitchen OFF Link")   'retrieve current status of Kitchen Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Kitchen OFF Link"), True, "Snap On", False, False))   'turn Kitchen Lights ON
        ' hs.TriggerEvent("AllKitchenLightsON")    'X10 Kitchen Lights
    End Sub
    Friend Sub GreatRoom()
        ''  Turn ON Great Room Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Great Room ALL OFF")   'retrieve current status of Great Room Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Great Room ALL OFF"), True, "Snap On", False, False))   'turn Great Room Lights ON
    End Sub
    Friend Sub HallWay()
        ''  Turn ON Hallway Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Hallway Ceiling Link")   'retrieve current status of Hallway Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hallway Ceiling Link"), True, "Snap On", False, False))   'turn Great Room Lights ON
    End Sub
    Friend Sub DiningRoom()
        '' Turn ON Dining Room Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Dining Room Ceiling")   'retrieve current status of Dining Room Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Dining Room Ceiling"), True, "Snap On", False, False))   'turn Dining Room Lights ON
    End Sub
    Friend Sub Foyer()
        '' Turn ON Foyer Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Foyer Ceiling Link")   'retrieve current status of Foyer Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Foyer Ceiling Link"), True, "Snap On", False, False))   'turn Foyer Lights ON
    End Sub
    Friend Sub Office()
        '' Turn ON Office Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Office Ceiling Link")   'retrieve current status of Foyer Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Office Ceiling Link"), True, "Snap On", False, False))   'turn Foyer Lights ON
    End Sub
    ''
    ''  AlexaRoombaStop      Is called by the Alexa Helper Triggers to restore all of the lights that were turned ON to their original states
    ''
    Public Function AlexaRoombaStop(AlexaRequest As String) As String
        Dim Status As New Dictionary(Of String, String), s As String
        '' Restore Lights Status
        Status = GetStatus(RoombaHsVar)
        UpbRestoreStates(Status)
        s = Status.Count.ToString & " lights will be returned to their original state"
        '' Clear Saved Statuses
        Status = New Dictionary(Of String, String)
        SaveStatus(RoombaHsVar, Status)
        Return s
    End Function
    ''
    '' AddLightsToStatusList:  Adds a list of Light Statuses to the Saved List in HS Variable, No Duplicates
    ''
    ''   StatusList: Status List to add to HS Variable
    ''
    Private Sub AddLightsToStatusList(StatusList As Dictionary(Of String, String))
        Dim sl As New Dictionary(Of String, String)
        sl = GetStatus(RoombaHsVar)   'get the saved status list
        For Each kp As KeyValuePair(Of String, String) In StatusList   'loop through lights to add to saved list (Name,CapiStatus)
            If sl.Keys.Contains(kp.Key.ToString) Then
                '' already there, skip it
            Else
                '' not there, add it
                sl.Add(kp.Key, kp.Value)   'add it to save list
            End If
        Next
        SaveStatus(RoombaHsVar, sl)
    End Sub
    Private Sub UpbRestoreStates(DeviceStates As Dictionary(Of String, String))
        Dim DevName, DevCapiStatus As String, s As String
        Dim cc As HomeSeerAPI.CAPI.CAPIControl
        Dim cr As HomeSeerAPI.CAPI.CAPIControlResponse
        For Each kvp As KeyValuePair(Of String, String) In DeviceStates
            DevName = kvp.Key.ToString
            DevCapiStatus = kvp.Value.ToString
            Select Case DevCapiStatus.ToUpper.Substring(0, 2)
                Case "ON"        'ON
                    cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName(DevName), True, "On", False, False))
                    If cr <> CAPIControlResponse.All_Success Then hs.WriteLog("UpbRestoreStates", "ON failure=" & cr.ToString & "    Dev=" & DevName)
                Case "OF"        'OFF
                    cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName(DevName), True, "Off", False, False))
                    If cr <> CAPIControlResponse.All_Success Then hs.WriteLog("UpbRestoreStates", "OFF failure=" & cr.ToString & "    Dev=" & DevName)
                Case "DI"        'DIM
                    ' Extract Dim Level from Status
                    s = DevCapiStatus.Substring(4)   'strip off "Dim "
                    s = s.Substring(0, s.Length - 1)  'strip off trailing "%"
                    cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName(DevName), True, "Dim (value)%", False, False)
                    cc.ControlValue = CInt(s)
                    cr = hs.CAPIControlHandler(cc)
                    If cr <> CAPIControlResponse.All_Success Then hs.WriteLog("UpbRestoreStates", "DIM  failure=" & cr.ToString & "    Dev=" & DevName)
                Case Else
            End Select
        Next
    End Sub
    ''
    ''  SaveStatus:   Save the Status of the Devices into an HS Variable
    ''    HsVar:   Name of HS Variable to hold status
    ''    UpbStatus:   Dictionary of Upb Device Statuses
    ''
    Private Sub SaveStatus(HsVar As String, UpbStatus As Dictionary(Of String, String))
        Dim er As String
        Try
            er = hs.CreateVar(HsVar)
        Catch ex As Exception
        End Try
        er = hs.SaveVar(HsVar, UpbStatus)
        If er = "" Then
        Else
            hs.WriteLog("UpbSaveStatus", "Error Saving to HS Variable: " & HsVar & ". Error: " & er)
        End If
    End Sub
    ''
    '' GetStatus:   Retrieves the Status of the Devices from an HS Variable
    ''
    ''       HsVar:   Name of HS Variable holding the Device Statuses
    ''       Returns:   Dictionary containing the retrieved Statuses
    ''
    Private Function GetStatus(HsVar As String) As Dictionary(Of String, String)
        Dim s As Dictionary(Of String, String)
        s = CType(hs.GetVar(HsVar), Dictionary(Of String, String))
        If s Is Nothing Then
            Return New Dictionary(Of String, String)
        Else
            Return s
        End If
    End Function
    ''
    '' UpbRetrieveStatus:  Find all of the UPB Devices that reference a UPB Link and Return their Status
    ''
    ''    TargetLinkName   UPB Link used to find Devices  (they reference this link)
    ''
    ''    Returns Dictionary(of String,String) containing list of   Device Names:CapiStatus
    ''
    Public Function UpbRetrieveStatus(TargetLinkName As String) As Dictionary(Of String, String)
        Dim NumOfLines As Integer = 0, NumOfLinks As Integer = 0, NumOfDevices As Integer = 0, a As String
        Dim Line As String, cc() As String
        Dim TargetLinkNumber As Integer
        Dim SavedStatus As New Dictionary(Of String, String)
        Dim Devices As New Dictionary(Of Integer, String), TargetDevices As New Dictionary(Of String, Integer)
        Dim UpbExportPath As String     'Path to the UPStart Exported File
        Dim DevRef As Integer
        '' Initialize
        TargetDevices.Clear()
        Devices.Clear()
        SavedStatus.Clear()
        UpbExportPath = hs.GetINISetting("UPSTART", "export_file", "", "UPBSpud.ini")
        '' Loop through entire file
        Using sr As New System.IO.StreamReader(UpbExportPath)
            Do
                Line = sr.ReadLine()
                Console.WriteLine(Line)
                If Not (Line Is Nothing) Then
                    '' Process each line from Export File
                    NumOfLines = NumOfLines + 1
                    Line = Line & ",00"
                    cc = Line.Split(ToChar(","))
                    Select Case cc(0)
                        Case "1"
                            Exit Do 'last line reached
                        Case "2"
                            '' Link definition
                            NumOfLinks = NumOfLinks + 1
                            ''   Find the Link Number based upon known Link Name
                            Console.WriteLine(cc(2))
                            If cc(2).Trim = TargetLinkName Then
                                TargetLinkNumber = CInt(cc(1))
                            End If
                        Case "3"
                            '' Device Definition
                            If CInt(cc(8)) = 2 Then
                                ' 2 channels
                                NumOfDevices = NumOfDevices + 2
                                Devices.Add(CInt(cc(1)), cc(11).Trim & " " & cc(12).Trim)     'ID:Room+Name
                                Devices.Add(CInt(cc(1)) + 10000, cc(11).Trim & " " & cc(12).Trim)     'ID+10000:Room+Name
                            Else
                                ' 1 channel
                                NumOfDevices = NumOfDevices + 1
                                Devices.Add(CInt(cc(1)), cc(11).Trim & " " & cc(12).Trim)     'ID:Room+Name
                            End If

                        Case "4"
                            '' Preset Record
                            If CInt(cc(4)) = TargetLinkNumber Then
                                '' This Device Receives the Targeted Link
                                If Devices.ContainsKey(CInt(cc(3)) + 10000) Then
                                    '' Two Channels
                                    a = Devices(CInt(cc(3))) & " - Ch 1"  ' name of target device
                                    If Not TargetDevices.ContainsKey(a) Then
                                        '' Target Devices not in table, add it
                                        TargetDevices.Add(a, CInt(cc(4)))    'Add Device Name 
                                    End If
                                    a = Devices(CInt(cc(3))) & " - Ch 1"  ' name of target device
                                    If Not TargetDevices.ContainsKey(a) Then
                                        '' Target Devices not in table, add it
                                        TargetDevices.Add(a, CInt(cc(4)))    'Add Device Name 
                                    End If
                                Else
                                    '' One Channel
                                    a = Devices(CInt(cc(3)))   ' name of target device
                                    If Not TargetDevices.ContainsKey(a) Then
                                        '' Target Devices not in table, add it
                                        TargetDevices.Add(a, CInt(cc(4)))    'Add Device Name 
                                    End If
                                End If
                            End If
                        Case Else
                    End Select
                End If
            Loop Until Line Is Nothing
            sr.Close()
        End Using
        '' Loop through all found Target Devices, Saving their CapiStatus
        For Each c As String In TargetDevices.Keys
            '' Save the Current State for each Target Device so it can be Restored later
            DevRef = hs.GetDeviceRefByName(c)
            a = hs.CAPIGetStatus(DevRef).Status
            SavedStatus.Add(c, a)    ' Name:CapiStatus
            hs.WriteLog("UpbSave", "Saving Current State for: " & c & ":" & a)
        Next c
        Return SavedStatus
    End Function
End Class

Public Class AlexaRoomba
    Const RoombaHsVar As String = "RoombaStatusList"
    Const SaveName As String = "AlexaRoomba"
    Dim AllRefs As New List(Of Integer)
    Public Sub Main(ByVal Parms As Object)    ' Only used for tenScripting testing

        CleanKitchen("clean this here room")
    End Sub



    Public Function RoombaFinished(Parm As Object) As String
        ''
        '' RoombaFinished is called by HS Event when the iRobot plugin detects Roomba has finished a job
        ''
        ''     It can also be called by the Alexa Helper to manually restore lights
        ''
        Dim rc As String
        rc = RestoreDeviceStates(hs4, SaveName, True)   'return devices/features to their saved states and clear saved Refs
        If rc <> "" Then
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "RestoreDeviceStates RC: " & rc, "RoombaFinished")
            Return "Their was an error attempting to restore lights to their prior state, check home seer log"
        End If
        Return "Roomba is finished"
    End Function
    Public Function CleanKitchen(AlexaRequest As String) As String
        ''
        '' Run Roomba in the Kitchen
        ''
        Dim Rooms() As String = {"Great Room", "Kitchen"}   'HS Categories to process
        Dim rc As String
        If CheckRoomba() <> "" Then Return "roomba unavailable, please try later"
        Try
            'hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.info, "AlexaRequest: " & AlexaRequest, "CleanKitchen")
            rc = SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
            ' hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "SaveDeviceStates RC: " & rc, "CleanKitchen")
            rc = DeviceOn(hs4, Rooms)   'turn ON all of the lights
            'hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "DeviceOn RC: " & rc, "CleanKitchen")
            hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaKitchen", True, False).ToString()  'tell Alexa to clean the room
            ' hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "After runScriptFunc", "CleanKitchen")
        Catch ex As Exception
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Exception: " & ex.Message, "CleanKitchen")
        End Try
        Return "OK, I told Roomba to vacuum the kitchen floor"
    End Function
    Public Function CleanHallway(AlexaRequest As String) As String
        ''
        '' Run Roomba in the Foyer and Hallway
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        Try
            rc = SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
            rc = DeviceOn(hs4, Rooms)   'turn ON all of the lights
            hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaHallway", True, False).ToString()  'tell Alexa to clean the room
        Catch ex As Exception
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Exception: " & ex.Message, "CleanHallway")
        End Try
        Return "OK, I told Roomba to vacuum the foyer and hallways"
    End Function
    Public Function CleanTable(AlexaRequest As String) As String
        ''
        '' Run Roomba in the Kitchen Table Area
        ''
        Dim Rooms() As String = {"Great Room", "Kitchen"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaTable", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the kitchen table area"
    End Function
    Public Function CleanSunroom(AlexaRequest As String) As String
        ''
        '' Run Roomba in the Sunroom
        ''
        Dim Rooms() As String = {"Great Room", "Kitchen", "Sunroom"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaSunroom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the sun room"
    End Function
    Public Function CleanKitchenTable(AlexaRequest As String) As String
        ''
        '' Run Roomba to clean the Kitchen floor and the area around the Kitchen Table
        ''
        Dim Rooms() As String = {"Great Room", "Kitchen"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaKitchenTable", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the entire kitchen"
    End Function
    Public Function CleanKitchenTableSunroom(AlexaRequest As String) As String
        ''
        '' Run Roomba to clean the entire Kitchen and Sunroom
        ''
        Dim Rooms() As String = {"Great Room", "Kitchen", "Sunroom"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaKitchenTableSunroom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the entire kitchen and sun room"
    End Function
    Public Function CleanEastGreatRoom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the East side of the Great Room
        ''
        Dim Rooms() As String = {"Great Room", "Sunroom", "Dining Room"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaEastGreatRoom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the east side of the great room"
    End Function
    Public Function CleanWestGreatRoom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the West side of the Great Room
        ''
        Dim Rooms() As String = {"Great Room", "Sunroom", "Dining Room"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaWestGreatRoom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the west side of the great room"
    End Function
    Public Function CleanEastWestGreatRoom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Great Room
        ''
        Dim Rooms() As String = {"Great Room", "Sunroom", "Dining Room"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaEastWestGreatRoom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the great room"
    End Function
    Public Function CleanLaundryRoom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Laundry Room
        ''
        Dim Rooms() As String = {"Great Room", "Kitchen", "Laundry"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaLaundry", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the laundry room"
    End Function
    Public Function CleanGuestBath(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Guest Bathroom
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway", "Guest Bath"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaGuestBath", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the hallway bath room"
    End Function
    Public Function CleanOffice(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Office
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway", "Office"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaOffice", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the office"
    End Function
    Public Function CleanMasterBedroom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Master Bedroom
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway", "Master Bedroom"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaMasterBedroom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the master bedroom"
    End Function

    Public Function CleanMasterBath(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Master Bathroom
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway", "Master Bedroom", "Master Bath"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaMasterBath", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the master bath room"
    End Function

    Public Function CleanFrontBedroom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Front Bedroom
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway", "Front Bedroom"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaFrontBedroom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the front bed room"
    End Function
    Public Function CleanMiddleBedroom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Middle Bedroom
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway", "Middle Bedroom"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaMiddleBedroom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the middle bed room"
    End Function

    Public Function CleanJackAndJillBath(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Jack and Jill Bath
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway", "Middle Bedroom", "Front Bedroom", "J and J Bath"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "roombaJackAndJillBath", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the jack and jill bath"
    End Function

    Public Function CleanDiningRoom(AlexaRequest As String) As String
        ''
        '' Run Roomba for the Dining Room
        ''
        Dim Rooms() As String = {"Great Room", "Dining Room", "Hallway"}   'HS Categories to process
        Dim rc As String
        rc = CheckRoomba()
        If rc <> "" Then Return "roomba not ready to clean. " & rc & ". please try later"
        hs4.RunScriptFunc("AlexaSpeak.vb", "Main", "Kitchen|remember to remove roomba brushes before cacuuming the dining room", True, True)
        SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
        DeviceOn(hs4, Rooms)   'turn ON all of the lights
        hs.WaitSecs(3)   'time for Alexa to complete prior speak
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "roombaDiningRoom", True, False)   'Voice Monkey button for Alexa Routine
        Return "OK, I told Roomba to vacuum the dining room"
    End Function

    Public Sub GoHome(AlexaRequest As String)
        ''
        '' Tell Roomba to Go Home
        ''
        ''
        hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Will tell Alexa to send Roomba Home", "GoHome")
        hs4.RunScriptFunc("RunAlexaRoutine.vb", "Main", "RoombaGoHome", True, False)   'Voice Monkey button for Alexa Routine
    End Sub



    Friend Function CheckRoomba() As String
        ''
        ''  Check if Roomba is connected to iRobot plugin and is ready to clean
        ''
        Dim refStatus, refError As Integer
        Dim ErrorValue, StatusValue As Double
        Dim RoombaError As String
        Dim RoombaStatus As String
        '' Get iRobot plugin Feature Refs
        refError = GetFeatureRefByFullname(hs, "Plugin iRobot Roomba", "Plugin iRobot Error")
        refStatus = GetFeatureRefByFullname(hs, "Plugin iRobot Roomba", "Plugin iRobot Status")
        '' Current Feature Values
        ErrorValue = CType(hs4.GetPropertyByRef(refError, HomeSeer.PluginSdk.Devices.EProperty.Value), Double)
        StatusValue = CType(hs4.GetPropertyByRef(refStatus, HomeSeer.PluginSdk.Devices.EProperty.Value), Double)
        '' Tell Panel that Roomba is starting
        hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Checking Roomba status before cleaning job", "CheckRoomba")
        hs.TriggerEvent("ShowRoombaStarting")
        '' Check if iRobot plugin is connected to Roomba
        If ErrorValue > 10000 Then
            '' Not connected
            hs4.RunScriptFunc("AlexaSpeak.vb", "Main", "Kitchen|Roomba not talking to home seer, try again later", True, True)
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Roomba is not connected to HS/iRobot, will still send request to clean to Alexa", "CheckRoomba")
            Return "Roomba not connected to home seer"
        End If
        '' Check if Roomba is having a problem
        If ErrorValue <> 0 Then
            '' Roomba Error Condition
            RoombaError = hs4.GetPropertyByRef(refError, HomeSeer.PluginSdk.Devices.EProperty.DisplayedStatus).ToString
            hs4.RunScriptFunc("AlexaSpeak.vb", "Main", "Roomba cannot clean now because her status is " & RoombaError, True, True)
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Roomba cannot clean now because her status is " & RoombaError, "CheckRoomba")
            Return "Roomba error is " & RoombaError
        End If
        '' Check if Roomba is ready to clean
        If StatusValue <> 0 Then
            '' Roomba not ready to clean
            RoombaStatus = hs4.GetPropertyByRef(refStatus, HomeSeer.PluginSdk.Devices.EProperty.DisplayedStatus).ToString
            hs4.RunScriptFunc("AlexaSpeak.vb", "Main", "Roomba is not ready to clean as she is " & RoombaStatus, True, True)
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Roomba is not ready to clean as she is " & RoombaStatus, "CheckRoomba")
            Return "Roomba status is " & RoombaStatus
        End If
        '' If here, all is good, let's clean
        Return ""

    End Function







    Public Function AlexaRoombaNextRoom(AlexaRequest As String) As String
        ''
        '' Called when Alexa Helper processes each room to be cleaned
        ''
        ''     AlexaRequest:   Sent from AlexaHelper in form    spokentext|extractednumber
        ''
        Dim parms() As String, Spoken As String
        Dim Words() As String
        Dim StatusList As New Dictionary(Of String, String)
        Log("AlexaRoombaNextRoom Input: " & AlexaRequest)    'log request from Alexa Helper
        parms = AlexaRequest.Split(ToChar("|"))   'extract parameters from Alexa Helper
        Spoken = parms(0).ToUpper.Trim.ToUpper   'what Alexa heard
        'theNumber = parms(1).ToUpper.Trim   'extracted number from spoken string
        Words = Spoken.Split(ToChar(" "))   'list of words in spoken text

        ''''' FINISHED or NO  or  NONE or DONE
        If Spoken = "NO" Or Spoken = "NONE" Or Spoken = "DONE" Or Spoken = "FINISHED" Then
            '' NO, do not want another request
            Log("AlexaRoombaNextRoom Dialog Finished because Input: " & Spoken)
            SaveDeviceStates(hs4, SaveName, AllRefs)   'save the current state of all the lights to be turned ON
            DeviceOn(hs4, AllRefs)   'turn on all of the lights on Roomba's path
            Return "ok, ready to run roomba.  turning on " & AllRefs.Count.ToString & " lights."  'without trailing "?", Alexa Helper will terminate processing
        End If






        ''''' Found No Matches
        Return "i did not understand that. tell me another room name or say FINISHED?"
    End Function
    Friend Sub Laundry()
        '' Turn ON Laundry Room Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Laundry Ceiling Link")   'retrieve current status of Laundry Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Laundry Ceiling Link"), True, "Snap On", False, False))   'turn Laundry Lights ON
    End Sub
    Friend Sub SunRoom()
        '' Turn ON Sunroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("MissouriRoomLights")   'retrieve current status of Sunroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("MissouriRoomLights"), True, "Snap On", False, False))   'turn Sunroom Lights ON
    End Sub
    Friend Sub MasterBedroom()
        '' Turn ON Master Bedroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Master Bedroom All OFF")   'retrieve current status of Master Bedroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bedroom All OFF"), True, "Snap On", False, False))   'turn Master Bedroom Lights ON
    End Sub
    Friend Sub GuestBath()
        '' Turn ON Guest Bath Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Guest Bathroom Light Link")   'retrieve current status of Guest Bath Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Guest Bathroom Light Link"), True, "Snap On", False, False))   'turn Guest Bath Lights ON
    End Sub
    Friend Sub JackJill()
        '' Turn ON Jack and Jill Bath Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("JandJ Roomba Link")   'retrieve current status of Jack and Jill Bath Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("JandJ ALL Link"), True, "Snap On", False, False))   'turn Jack and Jill Bath Lights ON
    End Sub
    Friend Sub FrontBedroom()
        '' Turn ON Front Bedroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Front Bedroom ALL Link")   'retrieve current status of Front Bedroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Front Bedroom ALL Link"), True, "Snap On", False, False))   'turn Front Bedroom Lights ON
    End Sub
    Friend Sub MiddleBedroom()
        '' Turn ON Middle Bedroom Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Middle Bedroom ALL Link")   'retrieve current status of Middle Bedroom Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Middle Bedroom ALL Link"), True, "Snap On", False, False))   'turn Middle Bedroom Lights ON
    End Sub
    Friend Sub Kitchen()
        '' Turn ON Kitchen Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Kitchen OFF Link")   'retrieve current status of Kitchen Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Kitchen OFF Link"), True, "Snap On", False, False))   'turn Kitchen Lights ON
        ' hs.TriggerEvent("AllKitchenLightsON")    'X10 Kitchen Lights
    End Sub
    Friend Sub GreatRoom()

        ProcessRoom("GreatRoom")

        'Dim DevRefs As List(Of Integer)
        'DevRefs = GetRefsByCategoryName(hs4, "GreatRoom")
        'For Each ref As Integer In DevRefs
        '    If Not AllRefs.Contains(ref) Then AllRefs.Add(ref)
        'Next ref

        ''  Turn ON Great Room Lights
        'Dim cr As CAPIControlResponse
        'Dim StatusList As New Dictionary(Of String, String)
        'StatusList = UpbRetrieveStatus("Great Room ALL OFF")   'retrieve current status of Great Room Lights
        'AddLightsToStatusList(StatusList)   'add lights to save status list
        'cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Great Room ALL OFF"), True, "Snap On", False, False))   'turn Great Room Lights ON
    End Sub
    Friend Sub HallWay()
        ''  Turn ON Hallway Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Hallway Ceiling Link")   'retrieve current status of Hallway Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Hallway Ceiling Link"), True, "Snap On", False, False))   'turn Great Room Lights ON
    End Sub
    Friend Sub DiningRoom()
        '' Turn ON Dining Room Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Dining Room Ceiling")   'retrieve current status of Dining Room Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Dining Room Ceiling"), True, "Snap On", False, False))   'turn Dining Room Lights ON
    End Sub
    Friend Sub Foyer()
        '' Turn ON Foyer Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Foyer Ceiling Link")   'retrieve current status of Foyer Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Foyer Ceiling Link"), True, "Snap On", False, False))   'turn Foyer Lights ON
    End Sub
    Friend Sub Office()
        '' Turn ON Office Lights
        Dim cr As CAPIControlResponse
        Dim StatusList As New Dictionary(Of String, String)
        StatusList = UpbRetrieveStatus("Office Ceiling Link")   'retrieve current status of Foyer Lights
        AddLightsToStatusList(StatusList)   'add lights to save status list
        cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Office Ceiling Link"), True, "Snap On", False, False))   'turn Foyer Lights ON
    End Sub
    ''
    ''  AlexaRoombaStop      Is called by the Alexa Helper Triggers to restore all of the lights that were turned ON to their original states
    ''
    Public Function AlexaRoombaStop(AlexaRequest As String) As String

        RestoreDeviceStates(hs4, SaveName)

        'Dim Status As New Dictionary(Of String, String), s As String
        '' Restore Lights Status
        'Status = GetStatus(RoombaHsVar)
        'UpbRestoreStates(Status)
        's = Status.Count.ToString & " lights will be returned to their original state"
        '' Clear Saved Statuses
        'Status = New Dictionary(Of String, String)
        'SaveStatus(RoombaHsVar, Status)
        Return "Now restoring " & AllRefs.Count.ToString & " lights to their original state"
    End Function
    ''
    '' AddLightsToStatusList:  Adds a list of Light Statuses to the Saved List in HS Variable, No Duplicates
    ''
    ''   StatusList: Status List to add to HS Variable
    ''
    Private Sub AddLightsToStatusList(StatusList As Dictionary(Of String, String))
        Dim sl As New Dictionary(Of String, String)
        sl = GetStatus(RoombaHsVar)   'get the saved status list
        For Each kp As KeyValuePair(Of String, String) In StatusList   'loop through lights to add to saved list (Name,CapiStatus)
            If sl.Keys.Contains(kp.Key.ToString) Then
                '' already there, skip it
            Else
                '' not there, add it
                sl.Add(kp.Key, kp.Value)   'add it to save list
            End If
        Next
        SaveStatus(RoombaHsVar, sl)
    End Sub
    Private Sub UpbRestoreStates(DeviceStates As Dictionary(Of String, String))
        Dim DevName, DevCapiStatus As String, s As String
        Dim cc As HomeSeerAPI.CAPI.CAPIControl
        Dim cr As HomeSeerAPI.CAPI.CAPIControlResponse
        For Each kvp As KeyValuePair(Of String, String) In DeviceStates
            DevName = kvp.Key.ToString
            DevCapiStatus = kvp.Value.ToString
            Select Case DevCapiStatus.ToUpper.Substring(0, 2)
                Case "ON"        'ON
                    cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName(DevName), True, "On", False, False))
                    If cr <> CAPIControlResponse.All_Success Then hs.WriteLog("UpbRestoreStates", "ON failure=" & cr.ToString & "    Dev=" & DevName)
                Case "OF"        'OFF
                    cr = hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName(DevName), True, "Off", False, False))
                    If cr <> CAPIControlResponse.All_Success Then hs.WriteLog("UpbRestoreStates", "OFF failure=" & cr.ToString & "    Dev=" & DevName)
                Case "DI"        'DIM
                    ' Extract Dim Level from Status
                    s = DevCapiStatus.Substring(4)   'strip off "Dim "
                    s = s.Substring(0, s.Length - 1)  'strip off trailing "%"
                    cc = hs.CAPIGetSingleControl(hs.GetDeviceRefByName(DevName), True, "Dim (value)%", False, False)
                    cc.ControlValue = CInt(s)
                    cr = hs.CAPIControlHandler(cc)
                    If cr <> CAPIControlResponse.All_Success Then hs.WriteLog("UpbRestoreStates", "DIM  failure=" & cr.ToString & "    Dev=" & DevName)
                Case Else
            End Select
        Next
    End Sub
    ''
    ''  SaveStatus:   Save the Status of the Devices into an HS Variable
    ''    HsVar:   Name of HS Variable to hold status
    ''    UpbStatus:   Dictionary of Upb Device Statuses
    ''
    Private Sub SaveStatus(HsVar As String, UpbStatus As Dictionary(Of String, String))
        Dim er As String
        Try
            er = hs.CreateVar(HsVar)
        Catch ex As Exception
        End Try
        er = hs.SaveVar(HsVar, UpbStatus)
        If er = "" Then
        Else
            hs.WriteLog("UpbSaveStatus", "Error Saving to HS Variable: " & HsVar & ". Error: " & er)
        End If
    End Sub
    ''
    '' GetStatus:   Retrieves the Status of the Devices from an HS Variable
    ''
    ''       HsVar:   Name of HS Variable holding the Device Statuses
    ''       Returns:   Dictionary containing the retrieved Statuses
    ''
    Private Function GetStatus(HsVar As String) As Dictionary(Of String, String)
        Dim s As Dictionary(Of String, String)
        s = CType(hs.GetVar(HsVar), Dictionary(Of String, String))
        If s Is Nothing Then
            Return New Dictionary(Of String, String)
        Else
            Return s
        End If
    End Function
    ''
    '' UpbRetrieveStatus:  Find all of the UPB Devices that reference a UPB Link and Return their Status
    ''
    ''    TargetLinkName   UPB Link used to find Devices  (they reference this link)
    ''
    ''    Returns Dictionary(of String,String) containing list of   Device Names:CapiStatus
    ''
    Public Function UpbRetrieveStatus(TargetLinkName As String) As Dictionary(Of String, String)
        Dim NumOfLines As Integer = 0, NumOfLinks As Integer = 0, NumOfDevices As Integer = 0, a As String
        Dim Line As String, cc() As String
        Dim TargetLinkNumber As Integer
        Dim SavedStatus As New Dictionary(Of String, String)
        Dim Devices As New Dictionary(Of Integer, String), TargetDevices As New Dictionary(Of String, Integer)
        Dim UpbExportPath As String     'Path to the UPStart Exported File
        Dim DevRef As Integer
        '' Initialize
        TargetDevices.Clear()
        Devices.Clear()
        SavedStatus.Clear()
        UpbExportPath = hs.GetINISetting("UPSTART", "export_file", "", "UPBSpud.ini")
        '' Loop through entire file
        Using sr As New System.IO.StreamReader(UpbExportPath)
            Do
                Line = sr.ReadLine()
                Console.WriteLine(Line)
                If Not (Line Is Nothing) Then
                    '' Process each line from Export File
                    NumOfLines = NumOfLines + 1
                    Line = Line & ",00"
                    cc = Line.Split(ToChar(","))
                    Select Case cc(0)
                        Case "1"
                            Exit Do 'last line reached
                        Case "2"
                            '' Link definition
                            NumOfLinks = NumOfLinks + 1
                            ''   Find the Link Number based upon known Link Name
                            Console.WriteLine(cc(2))
                            If cc(2).Trim = TargetLinkName Then
                                TargetLinkNumber = CInt(cc(1))
                            End If
                        Case "3"
                            '' Device Definition
                            If CInt(cc(8)) = 2 Then
                                ' 2 channels
                                NumOfDevices = NumOfDevices + 2
                                Devices.Add(CInt(cc(1)), cc(11).Trim & " " & cc(12).Trim)     'ID:Room+Name
                                Devices.Add(CInt(cc(1)) + 10000, cc(11).Trim & " " & cc(12).Trim)     'ID+10000:Room+Name
                            Else
                                ' 1 channel
                                NumOfDevices = NumOfDevices + 1
                                Devices.Add(CInt(cc(1)), cc(11).Trim & " " & cc(12).Trim)     'ID:Room+Name
                            End If

                        Case "4"
                            '' Preset Record
                            If CInt(cc(4)) = TargetLinkNumber Then
                                '' This Device Receives the Targeted Link
                                If Devices.ContainsKey(CInt(cc(3)) + 10000) Then
                                    '' Two Channels
                                    a = Devices(CInt(cc(3))) & " - Ch 1"  ' name of target device
                                    If Not TargetDevices.ContainsKey(a) Then
                                        '' Target Devices not in table, add it
                                        TargetDevices.Add(a, CInt(cc(4)))    'Add Device Name 
                                    End If
                                    a = Devices(CInt(cc(3))) & " - Ch 1"  ' name of target device
                                    If Not TargetDevices.ContainsKey(a) Then
                                        '' Target Devices not in table, add it
                                        TargetDevices.Add(a, CInt(cc(4)))    'Add Device Name 
                                    End If
                                Else
                                    '' One Channel
                                    a = Devices(CInt(cc(3)))   ' name of target device
                                    If Not TargetDevices.ContainsKey(a) Then
                                        '' Target Devices not in table, add it
                                        TargetDevices.Add(a, CInt(cc(4)))    'Add Device Name 
                                    End If
                                End If
                            End If
                        Case Else
                    End Select
                End If
            Loop Until Line Is Nothing
            sr.Close()
        End Using
        '' Loop through all found Target Devices, Saving their CapiStatus
        For Each c As String In TargetDevices.Keys
            '' Save the Current State for each Target Device so it can be Restored later
            DevRef = hs.GetDeviceRefByName(c)
            a = hs.CAPIGetStatus(DevRef).Status
            SavedStatus.Add(c, a)    ' Name:CapiStatus
            hs.WriteLog("UpbSave", "Saving Current State for: " & c & ":" & a)
        Next c
        Return SavedStatus
    End Function


    Friend Sub Log(s As String)
        '' Write Message to HS log
        hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, s, "AlexaRoomba")
    End Sub
    Friend Sub ProcessRoom(Room As String)
        Dim DevRefs As List(Of Integer)
        DevRefs = GetRefsByCategoryName(hs4, Room)
        For Each ref As Integer In DevRefs
            If Not AllRefs.Contains(ref) Then AllRefs.Add(ref)
        Next ref
    End Sub
End Class
Public Class RunAlexaRoutine
    Public Function Main(ByVal Parms As Object) As String
        ''
        '' This script will run the requested Alexa Routine (Parms)
        ''
        ''  RunAlexaRoutine uses Alexa Skill VoiceMonkey to send the request to Amazon
        ''
        Dim s1 As String
        Dim MonkeyToken, Prefix, Device, Url As String
        Try
            ' hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Parms: " & Parms.ToString, "RunAlexaRoutine")
            '' Build Voice Monkey API URL
            MonkeyToken = "ce6c55428881e89b0f2aca2b8022889e_2509b10986f21e47f1f5978faf48c9c5"   'unique token for Voice Monkey account
            Prefix = "https://api-v2.voicemonkey.io/trigger?token="   'Web location of Monkey API
            Device = Parms.ToString.ToLower.Trim   'Monkey device to trigger Alexa Routine
            Url = Prefix & MonkeyToken & "&device=" & Device
            'https://api-v2.voicemonkey.io/trigger?token=ce6c55428881e89b0f2aca2b8022889e_2509b10986f21e47f1f5978faf48c9c5&device=rombakitchentablesunroom
            '' Send request to Voice Monkey
            '  hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "URL: " & Url, "RunAlexaRoutine")
            s1 = WebRequest(Url)
            ' hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "WebRequest RC: " & s1, "RunAlexaRoutine")
            If Not s1.Contains("success") Then
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Bad return from WebRequest:", "RunAlexaRoutine")
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "   " & s1, "RunAlexaRoutine")
            End If
            '  hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Returning to caller", "RunAlexaRoutine")
        Catch ex As Exception
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Exception: " & ex.Message & " " & ex.InnerException.Message, "RunAlexaRoutine")
        End Try
        Return ""
    End Function
    Function WebRequest(ByVal sURL As String) As String
        Dim strData As String = ""
        Dim tlRequest As System.Net.HttpWebRequest
        Dim tlResponse As System.Net.HttpWebResponse
        Dim tlStatusCode As Integer
        Try
            ' hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "sURL: " & sURL, "RunAlexaRoutine/WebRequest")
            tlRequest = CType(System.Net.WebRequest.Create(sURL), System.Net.HttpWebRequest)
            tlRequest.ContentType = "text/html"
            tlRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"
            tlRequest.Method = "GET"
            tlRequest.Timeout = 10000
            tlRequest.AllowAutoRedirect = True
            tlResponse = CType(tlRequest.GetResponse(), System.Net.HttpWebResponse)
            '   hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "StatusCode: " & tlResponse.StatusCode, "RunAlexaRoutine/WebRequest")
            '   hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "StatusDescription: " & tlResponse.StatusDescription, "RunAlexaRoutine/WebRequest")
            tlStatusCode = tlResponse.StatusCode
            If tlStatusCode = System.Net.HttpStatusCode.OK Then
                strData = New System.IO.StreamReader(tlResponse.GetResponseStream()).ReadToEnd()
                ' hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "strData: " & strData, "RunAlexaRoutine/WebRequest")
            Else
                Return "WebRequest Failed with Response Code: " & tlStatusCode.ToString
            End If
            tlResponse.Close()
        Catch ex As Exception
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "HTTP Request Failed: " & ex.ToString, "RunAlexaRoutine/WebRequest")
        End Try
        Return strData
    End Function
    Public Sub TestIt(Routine As Object)
        '' Speak test message from Office Echo via Alexa Routine
        Main("testvoicemonkeyroutines")   'Monkey and Routine created for testing
    End Sub
End Class

Public Class HouseCleaners
    Dim SaveName As String = "HouseCleaners"
    Public Sub Main(ByVal Parms As Object)

    End Sub

    Public Function StartCleaning(AlexaRequest As String) As String
        ''
        '' Turn ON a bunch of lights after saving current state
        ''
        Dim Rooms() As String = {"Kitchen", "Great Room", "Dining Room", "Front Bedroom", "Guest Bath", "Hallway", "J and J Bath", "Laundry", "Master Bath", "Master Bedroom", "Middle Bedroom", "Office", "Sunroom"}   'HS Categories to process

        Dim rc As String
        hs.TriggerEvent("ShowRoombaStarting")   'display WAIT panel on kitchen tablet
        Try
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "AlexaRequest: " & AlexaRequest, "StartCleaning")
            rc = SaveDeviceStates(hs4, SaveName, Rooms)   'save device states
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "SaveDeviceStates RC: " & rc, "StartCleaning")
            rc = DeviceOn(hs4, Rooms)   'turn ON all of the lights
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "DeviceOn RC: " & rc, "StartCleaning")
        Catch ex As Exception
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Exception: " & ex.Message, "StartCleaning")
        End Try
        Return "OK, turning on lighs for house cleaners"
    End Function

    Public Function CleanersFinished(AlexaRequest As String) As String
        ''
        ''  Restore lights now that cleaners are finished
        ''
        Dim rc As String
        hs.TriggerEvent("ShowRoombaStarting")   'display WAIT panel on kitchen tablet
        rc = RestoreDeviceStates(hs4, SaveName, True)   'return devices/features to their saved states and clear saved Refs
        If rc <> "" Then
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "RestoreDeviceStates RC: " & rc, "CleanersFinished")
            Return "Their was an error attempting to restore lights to their prior state, check home seer log"
        Else
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Lights Restored to prior state", "CleanersFinished")
        End If
        Return "Lights have been restored to saved state"
    End Function

End Class

Public Class PhotoFrame
    Public Sub Main(ByVal Parms As Object)

        ''
        '' This script is called regularly to check POP email account for new photos
        ''
        '' Script Constants
        Const ScriptName As String = "PhotoFrame"
        Const PopServer As String = "pop.gmail.com"
        Const PopPort As Integer = 995
        Const UseSSL As Boolean = True
        Const UserId As String = "tenholderphotos@gmail.com"
        Const Password As String = "fkppfvmfvmmehpcv"
        Dim p As String = "PW" & (Left(MonthName(Now.Month, False), 1) & Right(MonthName(Now.Month, False), 1)).ToUpper

        Dim TargetPath As String = "c:\inetpub\HA\HS\Photos\PhotoFrame\"   'production location
        TargetPath = "d:\Files\PhotoFrame\"    'For testing only   @DONOTEXPORT
        Dim SavePath As String = "c:\inetpub\HA\HS\Photos\PhotoFrameSave\"   'production location
        SavePath = "d:\Files\PhotoFrameSave\"    'For testing only   @DONOTEXPORT

        Dim MessageFrom As String
        Dim MessageSubject As String
        Dim aPopClient As New Pop3Client
        Dim popMessageCount As Integer
        Dim aMessage As OpenPop.Mime.Message
        Dim MessageParts As List(Of OpenPop.Mime.MessagePart)
        Dim FilePath As String = TargetPath
        Dim AttachmentPath As String
        Dim MessageStream As FileStream
        Dim BinaryStream As BinaryWriter
        Dim s1 As String

        '' Access POP inbox
        aPopClient.Connect(PopServer, PopPort, UseSSL)   'connect to Gmail
        aPopClient.Authenticate(UserId, Password)   'login
        popMessageCount = aPopClient.GetMessageCount()   'get number of emails in the inbox  
        If popMessageCount < 1 Then
            '' No emails waiting, get out
            If aPopClient.Connected Then aPopClient.Dispose()
            Exit Sub
        End If

        '' Process all emails
        hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Found " & popMessageCount.ToString & " emails", ScriptName)
        For i As Integer = 1 To popMessageCount
            aMessage = aPopClient.GetMessage(popMessageCount)   'get an email
            MessageSubject = aMessage.Headers.Subject   'get the subject of this email
            MessageFrom = aMessage.Headers.From.Address  'get the email sender
            '' Send message to phone that email received
            hs.RunScriptFunc("Pushit.vb", "Main", "JOE|PhotoFrame|Email Received from " & MessageFrom, False, True)
            '' Check if proper credentials
            If Not MessageSubject.ToUpper.Contains(p) Then
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Email Not Authorized", ScriptName)   'log bad credentials
                hs.RunScriptFunc("SendAnEmail.vb", "Main", "PhotoFrame Email Failed~" & MessageFrom & "~*** Could Not Authenticate Sender ***", False, True)   'reply back, bad credentials
                If aPopClient.Connected Then aPopClient.Dispose()
                Exit Sub
            End If
            '' Clear the folder of old images if regquested
            If MessageSubject.ToUpper.Contains("CLEAR") Or MessageSubject.ToUpper.Contains("DELETE") Then
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Clearing Existing Photos", ScriptName)
                Try
                    For Each s As String In My.Computer.FileSystem.GetFiles(TargetPath, FileIO.SearchOption.SearchTopLevelOnly, "*.jpg")
                        s1 = Path.GetFileName(s)   'get just the filename from the full path
                        My.Computer.FileSystem.CopyFile(TargetPath & s1, SavePath & s1, True)   'back up each file
                        FileSystem.Kill(TargetPath & s1)   'delete it
                    Next s
                Catch ex As Exception
                    '' Delete or Copy failed, logit and quit
                    hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "Exception Copying/Deleting Files: " & ex.Message, ScriptName)
                    If aPopClient.Connected Then aPopClient.Dispose()
                    Exit Sub
                End Try
            End If
            '' Process attachments
            MessageParts = aMessage.FindAllAttachments()   'get all attachments for this message
            If MessageParts.Count < 1 Then
                '' No attachments
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Warning, "Email skipped, no attachments", ScriptName)
            Else
                '' Email has one or more attachments
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, MessageParts.Count.ToString & " attachments found", ScriptName)
                For Each part As OpenPop.Mime.MessagePart In MessageParts
                    AttachmentPath = FilePath & part.FileName
                    hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Saving File: " & AttachmentPath, ScriptName)
                    MessageStream = New FileStream(AttachmentPath, FileMode.Create)
                    BinaryStream = New BinaryWriter(MessageStream)
                    BinaryStream.Write(part.Body)
                    BinaryStream.Close()
                    MessageStream.Dispose()
                Next
            End If
        Next i

        If aPopClient.Connected Then aPopClient.Dispose()

        '' Send Command to Kitchen Tablet
        If popMessageCount > 0 Then
            hs.RunScriptFunc("HttpToTablet.vb", "Main", "PhotoFrameMessage", False, True)   'send message to kitchen tablet to show PhotoFrame
        End If

    End Sub
End Class

Public Class FindDuplicateNames
    Public Sub Main(ByVal Parms As Object)
        Dim AllRefs As List(Of Integer)
        Dim aAbsDevice As AbstractHsDevice
        Dim Duplicates As New Dictionary(Of String, Integer())
        Dim s1, s2 As String
        Dim StoredRefs(2) As Integer
        Dim OutputFile As String

        ''
        '' Find all HS4 Duplicate Names for all AbstractDevices  (Devices or Features)
        ''
        OutputFile = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\HsDuplicateNames.txt"  'Folder Path to the Desktop

        If System.IO.File.Exists(OutputFile) Then
            System.IO.File.Delete(OutputFile)
        End If
        WriteErrorLog("            These are the duplicate Device Names found", OutputFile)
        WriteErrorLog("  ", OutputFile)
        '' Get all HS4 DevRefs
        AllRefs = hs4.GetAllRefs
        '' Loop thru all Refs, looking for duplicate Names  (Location2 & Location & Name)
        For Each iRef As Integer In AllRefs
            If hs4.IsRefDevice(iRef) Then
                aAbsDevice = hs4.GetDeviceByRef(iRef)
            Else
                aAbsDevice = hs4.GetFeatureByRef(iRef)
                If IsNothing(aAbsDevice) Then aAbsDevice = hs4.GetDeviceByRef(iRef)
            End If
            If IsNothing(aAbsDevice) Then
                '' failed to get device/feature
                s1 = "********* Failed to get DevRef " & iRef.ToString
                Console.WriteLine(s1)
                WriteErrorLog(s1, OutputFile)
            Else
                '' We have a Device or Feature
                s1 = aAbsDevice.Location2 & " " & aAbsDevice.Location & " " & aAbsDevice.Name
                '  Console.WriteLine(iRef.ToString & "    " & s1)
                If Duplicates.ContainsKey(s1) Then
                    '' Name already in dictionary
                    StoredRefs = Duplicates(s1)  'Get current list of Refs for this name
                    ReDim Preserve StoredRefs(StoredRefs.Count)   'increase size of array
                    StoredRefs(StoredRefs.Count - 1) = iRef   'add current DevRef to end of array
                    Duplicates.Remove(s1)   'Remove the old entry
                    Duplicates.Add(s1, StoredRefs)   'add back the new entry
                Else
                    '' Name needs to be added to dictionary
                    ReDim StoredRefs(0)
                    StoredRefs(0) = iRef
                    Duplicates.Add(s1, StoredRefs)
                End If
            End If
        Next iRef
        '' Now, loop through the table we just built looking for entries with more than one DevRef for each Name
        For Each pair As KeyValuePair(Of String, Integer()) In Duplicates
            If pair.Value.Count > 1 Then
                '' Found duplicates, iterate all the dup refs
                s1 = ""
                For Each i As Integer In pair.Value
                    s1 = s1 & i.ToString & ","
                Next i
                s2 = "***** Duplicate Refs for Device Name""" & pair.Key & """" & ": " & s1
                Console.WriteLine(s2)
                WriteErrorLog(s2, OutputFile)
            End If
        Next pair
    End Sub
    Friend Sub WriteErrorLog(s As String, p As String)
        Dim DesktopPath As String
        DesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)   'Folder Path to the Desktop
        System.IO.File.AppendAllText(p, s & Environment.NewLine)
    End Sub
End Class
Public Class FindDuplicateAddresses
    Public Sub Main(ByVal Parms As Object)
        Dim AllRefs As List(Of Integer)
        Dim aAbsDevice As AbstractHsDevice
        Dim Duplicates As New Dictionary(Of String, Integer())
        Dim s1, s2 As String
        Dim StoredRefs(2) As Integer
        Dim OutputFile As String

        ''
        '' Find all HS4 Duplicate Addresses for all AbstractDevices  (Devices or Features)
        ''
        OutputFile = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\HsDuplicateAddresses.txt"  'Folder Path to the Desktop

        If System.IO.File.Exists(OutputFile) Then
            System.IO.File.Delete(OutputFile)
        End If
        WriteErrorLog("            These are the duplicate Adresses found", OutputFile)
        WriteErrorLog("  ", OutputFile)

        '' Get all HS4 DevRefs
        AllRefs = hs4.GetAllRefs
        '' Loop thru all Refs, looking for duplicate Names  (Location2 & Location & Name)
        For Each iRef As Integer In AllRefs
            If hs4.IsRefDevice(iRef) Then
                aAbsDevice = hs4.GetDeviceByRef(iRef)
            Else
                aAbsDevice = hs4.GetFeatureByRef(iRef)
                If IsNothing(aAbsDevice) Then aAbsDevice = hs4.GetDeviceByRef(iRef)
            End If
            If IsNothing(aAbsDevice) Then
                '' failed to get device/feature
                s1 = "********* Failed to get DevRef " & iRef.ToString
                Console.WriteLine(s1)
                WriteErrorLog(s1, OutputFile)
            Else
                '' We have a Device or Feature
                s1 = aAbsDevice.Address
                If Duplicates.ContainsKey(s1) Then
                    '' Address already in dictionary
                    StoredRefs = Duplicates(s1)  'Get current list of Refs for this name
                    ReDim Preserve StoredRefs(StoredRefs.Count)   'increase size of array
                    StoredRefs(StoredRefs.Count - 1) = iRef   'add current DevRef to end of array
                    Duplicates.Remove(s1)   'Remove the old entry
                    Duplicates.Add(s1, StoredRefs)   'add back the new entry
                Else
                    '' Name needs to be added to dictionary
                    ReDim StoredRefs(0)
                    StoredRefs(0) = iRef
                    Duplicates.Add(s1, StoredRefs)
                End If
            End If
        Next iRef
        '' Now, loop through the table we just built looking for entries with more than one DevRef for each Address
        For Each pair As KeyValuePair(Of String, Integer()) In Duplicates
            If pair.Value.Count > 1 Then
                '' Found duplicates, iterate all the dup refs
                s1 = ""
                For Each i As Integer In pair.Value
                    s1 = s1 & i.ToString & ","
                Next i
                s2 = "***** Duplicate Refs for Address """ & pair.Key & """" & "                " & s1
                Console.WriteLine(s2)
                WriteErrorLog(s2, OutputFile)
            End If
        Next pair
    End Sub
    Friend Sub WriteErrorLog(s As String, p As String)
        Dim DesktopPath As String
        DesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)   'Folder Path to the Desktop
        System.IO.File.AppendAllText(p, s & Environment.NewLine)
    End Sub
End Class
Public Class FindDuplicateFullNames
    Public Sub Main(ByVal Parms As Object)
        Dim AllRefs As List(Of Integer)
        Dim aAbsDevice As AbstractHsDevice
        Dim Duplicates As New Dictionary(Of String, Integer())
        Dim s1, s2 As String
        Dim ss() As String
        Dim StoredRefs(2) As Integer
        Dim OutputFile As String

        ''
        '' Find all HS4 Duplicate Fullnames for all AbstractDevices  (Devices or Features)
        ''
        OutputFile = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\HsDuplicateFullnamess.txt"  'Folder Path to the Desktop

        If System.IO.File.Exists(OutputFile) Then
            System.IO.File.Delete(OutputFile)
        End If
        WriteErrorLog("            These are the duplicate Fullnames found", OutputFile)
        WriteErrorLog("  ", OutputFile)
        '' Get all HS4 DevRefs
        AllRefs = hs4.GetAllRefs
        '' Loop thru all Refs, looking for duplicate Names  (Location2 & Location & Name)
        For Each iRef As Integer In AllRefs
            If hs4.IsRefDevice(iRef) Then
                aAbsDevice = hs4.GetDeviceByRef(iRef)
            Else
                aAbsDevice = hs4.GetFeatureByRef(iRef)
                If IsNothing(aAbsDevice) Then aAbsDevice = hs4.GetDeviceByRef(iRef)
            End If
            If IsNothing(aAbsDevice) Then
                '' failed to get device/feature
                s1 = "********* Failed to get DevRef " & iRef.ToString
                Console.WriteLine(s1)
                WriteErrorLog(s1, OutputFile)
            Else
                '' We have a Device or Feature
                ss = GetFullnameByRefLocal(hs4, iRef)
                s1 = ss(0) & "||" & ss(1)
                If Duplicates.ContainsKey(s1) Then
                    '' Address already in dictionary
                    StoredRefs = Duplicates(s1)  'Get current list of Refs for this name
                    ReDim Preserve StoredRefs(StoredRefs.Count)   'increase size of array
                    StoredRefs(StoredRefs.Count - 1) = iRef   'add current DevRef to end of array
                    Duplicates.Remove(s1)   'Remove the old entry
                    Duplicates.Add(s1, StoredRefs)   'add back the new entry
                Else
                    '' Fullname needs to be added to dictionary
                    ReDim StoredRefs(0)
                    StoredRefs(0) = iRef
                    Duplicates.Add(s1, StoredRefs)
                End If
            End If
        Next iRef
        '' Now, loop through the table we just built looking for entries with more than one DevRef for each Address
        For Each pair As KeyValuePair(Of String, Integer()) In Duplicates
            If pair.Value.Count > 1 Then
                '' Found duplicates, iterate all the dup refs
                s1 = ""
                For Each i As Integer In pair.Value
                    s1 = s1 & i.ToString & ","
                Next i
                s2 = "***** Duplicate Refs for Fullname """ & pair.Key & """" & "         " & s1
                Console.WriteLine(s2)
                WriteErrorLog(s2, OutputFile)
            End If
        Next pair
    End Sub
    Friend Sub WriteErrorLog(s As String, p As String)
        Dim DesktopPath As String
        DesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)   'Folder Path to the Desktop
        System.IO.File.AppendAllText(p, s & Environment.NewLine)
    End Sub
    Public Function GetFullnameByRefLocal(hs4 As HomeSeer.PluginSdk.IHsController, DevRef As Integer) As String()
        '' GetFullnameByRef      Returns a 2 element string array containing 
        ''                           String(0) = Device Name (Root)
        ''                           String(1) = Feature Name
        ''
        ''      If DevRef is a root device, then String(1) = ""
        ''
        Dim ss(1) As String
        Dim i1 As Integer
        Dim aName, aLocation, aLocation2 As String
        Dim Relationship As Integer
        Dim AssociatedDevices As HashSet(Of Integer)
        Dim Dev As HomeSeer.PluginSdk.Devices.AbstractHsDevice
        '' Get Fullname of DevRef
        Dev = hs4.GetDeviceByRef(DevRef)
        If Dev Is Nothing Then
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "GetFullnameByRef: DevRef " & DevRef.ToString & " not found", "tenGlobalMethods")
            Return {"", ""}
        End If
        aName = Dev.Name
        aLocation = Dev.Location
        aLocation2 = Dev.Location2
        If hs4.IsRefDevice(Dev.Ref) Then
            '' Device
            ss(0) = (aLocation2 & " " & aLocation & " " & aName).Trim
            ss(1) = ""
            Return ss
        Else
            '' Feature
            ss(1) = (aLocation2 & " " & aLocation & " " & aName).Trim   'Fullname of Feature device
            Relationship = Dev.Relationship
            If Relationship <> HomeSeer.PluginSdk.Devices.Identification.ERelationship.Feature Then
                '' Not a Feature
                hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "GetFullnameByRef: DevRef " & Dev.Ref.ToString & " is marked as Feature but is not a Child", "tenGlobalMethods")
                ss(0) = ""
                Return ss
            Else
                '' Got a good Feature, get its Parent device
                AssociatedDevices = Dev.AssociatedDevices
                If AssociatedDevices.Count <> 1 Then
                    hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Error, "DevRef " & Dev.Ref.ToString & " is marked as Feature but it has " & AssociatedDevices.Count.ToString & " Associated Devices", "GetFullNameByRef")
                    ss(0) = ""
                    Return ss
                Else
                    '' Single Associated Device, get Fullname of root device
                    i1 = AssociatedDevices(0)   'parent DevRef
                    aName = hs4.GetPropertyByRef(i1, HomeSeer.PluginSdk.Devices.EProperty.Name).ToString
                    aLocation = hs4.GetPropertyByRef(i1, HomeSeer.PluginSdk.Devices.EProperty.Location).ToString
                    aLocation2 = hs4.GetPropertyByRef(i1, HomeSeer.PluginSdk.Devices.EProperty.Location2).ToString
                    ss(0) = (aLocation2 & " " & aLocation & " " & aName).Trim   'Fullname of root device
                    Return ss
                End If
            End If
        End If
    End Function
End Class
Class EjtLongLoop

    Public Sub Main(ByVal Parms As Object)
        Dim cc As HomeSeerAPI.CAPI.CAPIControl
        Dim cr As HomeSeerAPI.CAPI.CAPIControlResponse

        Dim NightTime As Boolean = (Now().TimeOfDay < CDate(hs.Sunrise).TimeOfDay) Or (Now().TimeOfDay > CDate(hs.Sunset).TimeOfDay)
        Dim refPlants = ten.GetFeatureRefByFullname(hs, "Basement Plants", "")

        ''
        '' Basements Plants
        ''
        If NightTime Then
            cc = hs.CAPIGetSingleControl(refPlants, True, "Off", False, False)
            cr = hs.CAPIControlHandler(cc)
        End If
        ''
        '' Basement Cedar Closet Door
        ''
        'If hs.IsOnByName("Basement Cedar Closet Door") Then
        '    '' Cedar Closet Open
        '    If hs.DeviceTimeByName("Basement Cedar Closet Door") > 15 Then
        '        hs.speakEx(37, "The cedar closet door is open")
        '    End If
        'End If

        ' '' Basement Storage Room Doors
        'If hs.IsOnByName("Basement Stor Door East") Then
        '    '' East Door Open
        '    If hs.DeviceTimeByName("Basement Stor Door East") > 90 Then
        '        hs.speakEx(37, "The east basement storage door is open")
        '    End If
        'End If
        'If hs.IsOnByName("Basement Stor Door West") Then
        '    '' West Door Open
        '    If hs.DeviceTimeByName("Basement Stor Door West") > 90 Then
        '        hs.speakEx(37, "The west basement storage door is open")
        '    End If
        'End If

        ''
        '' Master Bathroom Fan
        ''
        If hs.DeviceValueByName("Master Bath Fan") > 0 Then
            If hs.DeviceTimeByName("Master Bath Fan") > 15 Then
                ControlDevice("Master Bath Fan", "Off")
            End If
        End If

        ''
        '' Jack and Jill Fan and Heat
        ''
        If hs.DeviceValueByName("J and J Bath Ceiling Fan") > 0 Then
            If hs.DeviceTimeByName("J and J Bath Ceiling Fan") > 45 Then
                ControlDevice("J and J Bath Ceiling Fan", "Off")
            End If
        End If
        If hs.DeviceValueByName("J and J Bath Ceiling Heat") > 0 Then
            If hs.DeviceTimeByName("J and J Bath Ceiling Heat") > 15 Then
                ControlDevice("J and J Bath Ceiling Heat", "Off")
            End If
        End If

        ''
        '' Basement Bathroom Fan and Heat
        ''
        If hs.DeviceValueByName("Bsmt Bath Ceiling Fan") > 0 Then
            If hs.DeviceTimeByName("Bsmt Bath Ceiling Fan") > 15 Then
                ControlDevice("Bsmt Bath Ceiling Fan", "Off")
            End If
        End If
        If hs.DeviceValueByName("Bsmt Bath Ceiling Heat") > 0 Then
            If hs.DeviceTimeByName("Bsmt Bath Ceiling Heat") > 15 Then
                ControlDevice("Bsmt Bath Ceiling Heat", "Off")
            End If
        End If

        ''
        '' Guest Bathroom Fan and Ceiling Light
        ''
        If hs.DeviceValueByName("Guest Bathroom Fan") > 0 Then
            If hs.DeviceTimeByName("Guest Bathroom Fan") > 15 Then
                ControlDevice("Guest Bathroom Fan", "Off")
            End If
        End If
        If hs.DeviceValueByName("Guest Bathroom Ceiling Light") > 0 Then
            If hs.DeviceTimeByName("Guest Bathroom Ceiling Light") > 10 Then
                ControlDevice("Guest Bathroom Ceiling Light", "Off")
            End If
        End If

        ''
        '' Bsmt Bedroom Bath Fan
        ''
        If hs.DeviceValueByName("Bsmt Bedroom Bath Center SW") > 0 Then
            If hs.DeviceTimeByName("Bsmt Bedroom Bath Center SW") > 20 Then
                ControlDevice("Bsmt Bedroom Bath Center SW", "Off")
            End If
        End If


        ''
        '' J and J Bath Fan
        ''
        Dim refJandjFan As Integer = GetFeatureRefByFullname(hs, "J and J Bath Ceiling Fan", "")

        If hs.DeviceValue(refJandjFan) = 100 Then
            '' Fan ON
            If hs.DeviceTime(refJandjFan) > 20 Then
                '' For 20 minutes, turn OFF
                hs4.SendControlForFeatureByValue(refJandjFan, 0)   'turn Fan OFF
            End If
        End If




        ''
        '' UPS
        ''
        'If hs.DeviceValueByName("tenHsPower AC Line Status") <> 1 Then
        '    hs.SpeakEx(37, "UPS Offline, Battery Level is " & hs.DeviceValueByName("tenHsPower Battery Life Percent") & " Percent", False)
        'End If

        ''
        '' Garage Doors
        ''
        'If hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Zone UltraM1G3 Plugin Zone 066 [Garage Door SO]")) <> 1 Then
        '    If hs.DeviceTimeByName("Elk M1 Zone UltraM1G3 Plugin Zone 066 [Garage Door SO]") > 60 And
        '        hs.DeviceTimeByName("Elk M1 Zone UltraM1G3 Plugin Zone 066 [Garage Door SO]") < 75 Then
        '        'hs.SpeakEx(37, "the south garage door is open", True)
        '        hs.RunScriptFunc("SendToAlexa.vb", "Main", "the south garage door is open", False, True)
        '    End If
        'End If
        'If hs.DeviceValueEx(hs.GetDeviceRefByName("Elk M1 Zone UltraM1G3 Plugin Zone 067 [Garage Door NO]")) <> 1 Then
        '    If hs.DeviceTimeByName("Elk M1 Zone UltraM1G3 Plugin Zone 067 [Garage Door NO]") > 60 And
        '        hs.DeviceTimeByName("Elk M1 Zone UltraM1G3 Plugin Zone 067 [Garage Door NO]") < 75 Then
        '        ' hs.SpeakEx(37, "the north garage door is open", True)
        '        hs.RunScriptFunc("SendToAlexa.vb", "Main", "the north garage door is open", False, True)
        '    End If
        'End If

        ''
        '' Master Bath Floor Heat
        ''
        Const WL As Boolean = False
        Const TempMax As Double = 75.0, TempMin As Double = 70.0, OverrideMaxTime As Integer = 4 * 60, TempErrorThreshold As Double = 40.0
        Dim devBathroomTemp, devOutsideTemp, devFloor As Integer
        Dim BathroomTemp, OutsideTemp As Double
        Dim HouseOccupied As Boolean
        Dim BathroomTempTime, OverrideTime As Integer
        Dim OverrideValue As String
        Dim FloorStatus As String
        Dim devOverride As Integer
        '' Get Device Refs
        devBathroomTemp = GetFeatureRefByFullname(hs, "JowiHue JowiHue Motion Mstr Bath", "JowiHue JowiHue Motion Mstr Bath Temperature")   'HS4 Scripting Extensions Must be Installed
        devOutsideTemp = GetFeatureRefByFullname(hs, "tenPlugin tenWeatherflowUDP RootDevice", "tenPlugin tenWeatherflowUDP Temperature")      'HS4 Scripting Extensions Must be Installed
        devFloor = GetFeatureRefByFullname(hs, "Master Bath Floor", "")      'HS4 Scripting Extensions Must be Installed
        devOverride = GetFeatureRefByFullname(hs, "Virtual Virtual VirtualDevicesRoot", "Virtual Virtual BathFloorOverride")
        '' Get Thresholds
        If WL Then hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "THRESHOLDS:  Temp Min=" & TempMin.ToString & "   Temp Max=" & TempMax.ToString & "   Override Max Time(minutes)=" & OverrideMaxTime.ToString, "EjtLongLoop")
        '' Get temperatures
        OutsideTemp = CType(hs4.GetPropertyByRef(devOutsideTemp, HomeSeer.PluginSdk.Devices.EProperty.Value), Double)   'outside temperature
        BathroomTemp = CType(hs4.GetPropertyByRef(devBathroomTemp, HomeSeer.PluginSdk.Devices.EProperty.Value), Double)   'Bathroom air temperature
        If WL Then hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "TEMPS:  Outside=" & OutsideTemp.ToString & "   Bathroom=" & BathroomTemp.ToString, "EjtLongLoop")
        '' Get Device Times
        BathroomTempTime = hs.DeviceTime(devBathroomTemp)   'Time since air temperature updated
        OverrideTime = hs.DeviceTime(devOverride)
        If WL Then hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "DEVICE TIMES(minutes):  Temperature=" & BathroomTempTime.ToString & "   Override=" & OverrideTime.ToString, "EjtLongLoop")
        '' Get Current Status
        FloorStatus = hs.DeviceVSP_GetStatus(devFloor, hs.DeviceValue(devFloor), HomeSeerAPI.ePairStatusControl.Status)
        OverrideValue = CType(hs.CAPIGetStatus(devOverride), HomeSeerAPI.CAPI.CAPIStatus).Status.ToString
        If WL Then hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "CURRENT STATUS:  Floor=" & FloorStatus & "   Override=" & OverrideValue, "EjtLongLoop")
        If hs.DeviceValueEx(hs.GetDeviceRefByName("Virtual Virtual HouseOccupied")) = 1 Then
            HouseOccupied = True
        Else
            HouseOccupied = False
        End If
        '' Determine if Floor should be turned ON or OFF
        If OverrideTime < OverrideMaxTime Then
            '' Override from wall switch active
            hs4.WriteLog(HomeSeer.PluginSdk.Logging.ELogType.Info, "Wall Switch Override Active", "EjtLongLoop")
            If OverrideValue = "ForceON" Then
                '' Floor should be ON due to active wall button
                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bath Floor"), True, "On", False, False))
                Exit Sub
            Else
                '' Floor should be OFF due to active wall button
                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bath Floor"), True, "Off", False, False))
                Exit Sub
            End If
        End If
        If BathroomTempTime > 60 Then
            '' Bathroom temp device communication failure (not updated in last hour)
            hs.WriteLogEx("LongLoop", "Master Bath Temperature last updated " & BathroomTempTime.ToString & " minutes ago", "#FF0000")
            If OutsideTemp < TempErrorThreshold Then
                '' No bathroom temp and outside temp is COLD
                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bath Floor"), True, "On", False, False))
            Else
                '' Not so COLD
                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bath Floor"), True, "Off", False, False))
            End If
        Else
            '' Bathroom temp device good
            ' d1 = hs.DeviceValueEx(devBathroomTemp)
            If BathroomTemp < TempMin Then
                '' Turn on Floor Heat
                If HouseOccupied Then
                    hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bath Floor"), True, "On", False, False))
                End If
            End If
            If BathroomTemp > TempMax Then
                '' Turn OFF Floor Heat
                hs.CAPIControlHandler(hs.CAPIGetSingleControl(hs.GetDeviceRefByName("Master Bath Floor"), True, "Off", False, False))
            End If
        End If

        ''
        '' ELK Alarm System Still Connected?
        ''
        'If hs.DeviceTimeByName("Elk M1 Counter UltraM1G3 Plugin Counter 01 [hs_connect]") > 5 Then
        '    hs.WriteLog("LongLoop", "ELK Counter Exceeded 5, now is " & hs.DeviceTimeByName("Elk M1 Counter UltraM1G3 Plugin Counter 01 [hs_connect]"))
        '    hs.RunScriptFunc("SendToAlexa.vb", "Main", "ten has lost connection with the alarm system", False, True)
        'End If
    End Sub
    Private Sub ControlDevice(DeviceName As String, DeviceAction As String)
        '' Do all CAPI stuff to control a device
        Dim CC As HomeSeerAPI.CAPIControl, CCResp As HomeSeerAPI.CAPIControlResponse
        Dim DevRef As Integer

        DevRef = hs.GetDeviceRefByName(DeviceName)
        If DevRef < 0 Then
            hs.WriteLog("ControlDevice", "Error, Could Not Get Device Reference for: " & DeviceName)
            Exit Sub
        End If
        CC = hs.CAPIGetSingleControl(DevRef, True, DeviceAction, False, False)
        If IsNothing(CC) Then
            hs.WriteLog("ControlDevice", "Error, Could Not Find Action: " & DeviceAction & "     for Device: " & DeviceName)
            Exit Sub
        End If
        CCResp = hs.CAPIControlHandler(CC)
    End Sub

End Class
