{"id":2851,"date":"2014-07-28T12:11:02","date_gmt":"2014-07-28T12:11:02","guid":{"rendered":"http:\/\/dollarunderscore.azurewebsites.net\/?p=2851"},"modified":"2014-07-28T12:22:13","modified_gmt":"2014-07-28T12:22:13","slug":"checking-who-is-home-with-powershell","status":"publish","type":"post","link":"https:\/\/p0wershell.com\/?p=2851","title":{"rendered":"Checking who is home with PowerShell"},"content":{"rendered":"<p>The first thing that comes to mind when doing home automation is probably controlling lights and sockets depending on if someone is home or not. Most apps I&#8217;ve seen for this can&#8217;t handle multiple family members though, which is quite useless. You don&#8217;t want to turn on all the lights when you come home if someone else is sleeping or watching a movie with dimmed lights for example.<\/p>\n<p>So how to figure out who is currently at home?<\/p>\n<p>The method I chose was to check if our phones are connected to WiFi. There are a few obstacles to overcome before this works though, for example:<\/p>\n<li>Many smartphones turn off WiFi to save battery (for example the iPhone)<\/li>\n<li>They use dynamic IP-addresses and name resolution is not always available, so how to find them?<\/li>\n<p>The script therefor checks for how long a phone has been offline before it does any changes, and it starts off by doing a pingsweep to populate the arp-table to find the different phones MAC-addresses.<\/p>\n<p>This scripts updates the status of each phone in a file on disk and also changes a device in Telldus Live! to On\/Off. That makes it possible to filter events in Telldus Live! based on who&#8217;s home, or when the last person leaves.<\/p>\n<p>The script-module used here can be found in <a href=\"https:\/\/p0wershell.com\/?p=1661\" title=\"Home Automation Module Updated\">this post<\/a>.<\/p>\n<p>The script looks like this:<\/p>\n<p>[PowerShell]<br \/>\n# Import the telldus module<br \/>\nImport-Module &#8216;C:\\TelldusScripts\\Telldusv2.psm1&#8217;<\/p>\n<p># Set telldus credentials<br \/>\n$Username = &#8220;MyEmailForTelldusLive@contoso.com&#8221;<\/p>\n<p># Get the password from the file (a saved PowerShell credential)<br \/>\n$Password = Get-Content &#8216;C:\\TelldusScripts\\TelldusPassword.txt&#8217; | ConvertTo-SecureString<\/p>\n<p># Build the credential<br \/>\n$TelldusCredential = New-Object System.Management.Automation.PsCredential($Username,$Password)<\/p>\n<p># Specify path to the &#8220;FamilyMember&#8221; file<br \/>\n$FamilyMemberFile = &#8220;C:\\TelldusScripts\\FamilyMembers.csv&#8221;<\/p>\n<p># How many times should we wait for the network scan to finish?<br \/>\n$MaxWaitRounds = 10<\/p>\n<p># How long should we sleep between each &#8220;network scan check&#8221;?<br \/>\n$WaitBetweenNetworkScanSeconds = 10<\/p>\n<p># Load that file into an array<br \/>\n$FamilyMembers = Import-Csv $FamilyMemberFile -Encoding utf8<\/p>\n<p># Get the subnets<br \/>\n$DeviceSubnets = $FamilyMembers | select -ExpandProperty WiFiSubnet | Sort-Object -Unique<\/p>\n<p># Set the time limit for when a device should be considered offline<br \/>\n$NotHomeTimeLimit = 120<\/p>\n<p># Do a ping sweep in those subnets to find any device MACs<br \/>\nforeach ($DeviceSubnet in $DeviceSubnets) {<\/p>\n<p>    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 1..25 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 26..50 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 51..75 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 76..100 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 101..125 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 126..150 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 151..175 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 176..200 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 201..225 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n    Start-Job -ArgumentList $DeviceSubnet -ScriptBlock { $Subnet=$args[0] ; 225..255 | % { ping &#8220;$Subnet$_&#8221; -n 1 -w 1 } }<br \/>\n}<\/p>\n<p>$NumberOfRounds = 1<\/p>\n<p>while ((Get-Job -State Running).count -gt 0 -AND $NumberOfRounds -le $MaxWaitRounds) {<br \/>\n    Write-Output &#8220;Scanning network for devices&#8230; (Round: $NumberOfRounds of max $MaxWaitRounds. Number of jobs running: $((Get-Job -State Running).count).)&#8221;<br \/>\n    sleep -Seconds $WaitBetweenNetworkScanSeconds<br \/>\n    $NumberOfRounds++<br \/>\n}<\/p>\n<p># Loop through the family members<br \/>\nforeach ($FamilyMember in $FamilyMembers) {<\/p>\n<p>    # Reset the variables<br \/>\n    $FamilyMemberLastStatus = $null<br \/>\n    $FamilyMemberCurrentStatus = $null<\/p>\n<p>    # Get last status<br \/>\n    $FamilyMemberLastStatus = Get-Content $($FamilyMember.StatusFile)<\/p>\n<p>    # Get current status by name<br \/>\n    $FamilyMemberCurrentStatus = Test-Connection $($FamilyMember.DeviceName) -Count 1 -Quiet<\/p>\n<p>    # Get it by MAC if that failed<br \/>\n    if ($FamilyMemberCurrentStatus -eq $false) {<br \/>\n        $CurrentFamilyMemberIP = $null<br \/>\n        $CurrentFamilyMemberIP = arp -a | select-string $FamilyMember.DeviceMAC | % { $_.ToString().Trim().Split(&#8221; &#8220;)[0] } | select -First 1<\/p>\n<p>        $FamilyMemberCurrentStatus = Test-Connection $CurrentFamilyMemberIP -Count 1 -Quiet<br \/>\n    }<br \/>\n    # Check if the current status is online<br \/>\n    if ($FamilyMemberCurrentStatus -eq $true) {<\/p>\n<p>        Write-Output &#8220;$($FamilyMember.Name)&#8217;s device is online!&#8221;<\/p>\n<p>        # Update status file<br \/>\n        Write-Output $true | Out-File $FamilyMember.StatusFile<\/p>\n<p>        # If it was offline last time, set the telldus device to on<br \/>\n        if ($FamilyMemberLastStatus -ne $true) {<br \/>\n            Write-Host &#8220;This is a change since last run, setting device to on.&#8221;<br \/>\n            Connect-TelldusLive -Credential $TelldusCredential<br \/>\n            Set-TDDevice -DeviceID $FamilyMember.DeviceID -Action turnOn<br \/>\n        }<br \/>\n    }<br \/>\n    # So the device was offline&#8230;<br \/>\n    else {<br \/>\n        Write-Output &#8220;$($FamilyMember.Name)&#8217;s device is offline!&#8221;<\/p>\n<p>        # Verify if this is temporary (iPhone sleep mode) or if it has been offline for a while<br \/>\n        $LastChangeTime=(gci $($FamilyMember.StatusFile)).LastWriteTime<br \/>\n        $Now=Get-Date<br \/>\n        $LastChangeTimeSpan = (New-TimeSpan -Start $LastChangeTime -End $Now).TotalMinutes<\/p>\n<p>        # Only change status if the device has been offline for $NotHomeTimeLimit minutes.<br \/>\n        if ($LastChangeTimeSpan -gt $NotHomeTimeLimit -AND $FamilyMemberLastStatus -eq $true) {<br \/>\n            # It was, update statusfile to offline<br \/>\n            Write-Output $false | Out-File $($FamilyMember.StatusFile)<\/p>\n<p>            Write-Output &#8220;This device has been offline for more than $NotHomeTimeLimit minutes. Turning Telldus device off&#8230;&#8221;<br \/>\n            Connect-TelldusLive -Credential $TelldusCredential<br \/>\n            Set-TDDevice -DeviceID $FamilyMember.DeviceID -Action turnOff<br \/>\n        }<br \/>\n    }<br \/>\n}<br \/>\n[\/PowerShell]<\/p>\n<p>I&#8217;ve scheduled it to run quite often to always check if someone has arrived. Hopefully the comments in code will give you the help you need, if not, post a comment below and I&#8217;ll be glad to help you!<\/p>\n<p>The csv file ($FamilyMemberFile, C:\\TelldusScripts\\FamilyMembers.csv) used in the script above should have the columns Name, DeviceName, DeviceMAC, WiFiSubnet, StatusFile, DeviceID.<\/p>\n<p>Name = The name of the person who owns the device<br \/>\nDeviceName = The DNS name of the device. iPhones use &#8220;DeviceName.local&#8221;<br \/>\nDeviceMAC = MAC-address of the devices, use &#8220;-&#8221; as a separator.<br \/>\nWiFiSubnet = The subnet where the phone gets its IP-address.<br \/>\nStatusFile = The path to a file unique for this phone where the status can be written (if it&#8217;s home or not)<br \/>\nDeviceID = The device id in Telldus Live! that is used to filter if this person is home.<\/p>\n<p>Example:<br \/>\nName,DeviceName,DeviceMAC,WiFiSubnet,StatusFile,DeviceID<br \/>\nJohn,&#8221;JohnsiPhone.local&#8221;,&#8221;00-01-03-04-05-06&#8243;,&#8221;192.168.1.&#8221;,&#8221;C:\\TelldusScripts\\IsJohnHome.txt&#8221;,&#8221;123456&#8243;<\/p>\n<p>The script will loop through all devices added and set the status in the status file (True\/False) aswell as the device in Telldus Live! (On\/Off) depending on if the phone was online or not.<\/p>\n<p>Hope that made sense for you!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The first thing that comes to mind when doing home automation is probably controlling lights and sockets depending on if someone is home or not. Most apps I&#8217;ve seen for this can&#8217;t handle multiple family members though, which is quite useless. You don&#8217;t want to turn on all the lights when you come home if [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[231,21,241],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p3Zj0A-JZ","_links":{"self":[{"href":"https:\/\/p0wershell.com\/index.php?rest_route=\/wp\/v2\/posts\/2851"}],"collection":[{"href":"https:\/\/p0wershell.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/p0wershell.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/p0wershell.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/p0wershell.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2851"}],"version-history":[{"count":0,"href":"https:\/\/p0wershell.com\/index.php?rest_route=\/wp\/v2\/posts\/2851\/revisions"}],"wp:attachment":[{"href":"https:\/\/p0wershell.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2851"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/p0wershell.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2851"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/p0wershell.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2851"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}